05754476fd154ad563a890244dedc2f96f3e2d13
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSString.h
1 /*
2  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
4  *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2014 Apple Inc. All rights reserved.
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Library General Public
8  *  License as published by the Free Software Foundation; either
9  *  version 2 of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Library General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Library General Public License
17  *  along with this library; see the file COPYING.LIB.  If not, write to
18  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  *  Boston, MA 02110-1301, USA.
20  *
21  */
22
23 #ifndef JSString_h
24 #define JSString_h
25
26 #include "CallFrame.h"
27 #include "CommonIdentifiers.h"
28 #include "Identifier.h"
29 #include "PropertyDescriptor.h"
30 #include "PropertySlot.h"
31 #include "Structure.h"
32 #include <array>
33
34 namespace JSC {
35
36 class JSString;
37 class JSRopeString;
38 class LLIntOffsetsExtractor;
39
40 JSString* jsEmptyString(VM*);
41 JSString* jsEmptyString(ExecState*);
42 JSString* jsString(VM*, const String&); // returns empty string if passed null string
43 JSString* jsString(ExecState*, const String&); // returns empty string if passed null string
44
45 JSString* jsSingleCharacterString(VM*, UChar);
46 JSString* jsSingleCharacterString(ExecState*, UChar);
47 JSString* jsSingleCharacterSubstring(ExecState*, const String&, unsigned offset);
48 JSString* jsSubstring(VM*, const String&, unsigned offset, unsigned length);
49 JSString* jsSubstring(ExecState*, const String&, unsigned offset, unsigned length);
50
51 // Non-trivial strings are two or more characters long.
52 // These functions are faster than just calling jsString.
53 JSString* jsNontrivialString(VM*, const String&);
54 JSString* jsNontrivialString(ExecState*, const String&);
55 JSString* jsNontrivialString(ExecState*, String&&);
56
57 // Should be used for strings that are owned by an object that will
58 // likely outlive the JSValue this makes, such as the parse tree or a
59 // DOM object that contains a String
60 JSString* jsOwnedString(VM*, const String&);
61 JSString* jsOwnedString(ExecState*, const String&);
62
63 JSRopeString* jsStringBuilder(VM*);
64
65 class JSString : public JSCell {
66 public:
67     friend class JIT;
68     friend class VM;
69     friend class SpecializedThunkJIT;
70     friend class JSRopeString;
71     friend class MarkStack;
72     friend class SlotVisitor;
73     friend struct ThunkHelpers;
74
75     typedef JSCell Base;
76
77     static const bool needsDestruction = true;
78     static const bool hasImmortalStructure = true;
79     static void destroy(JSCell*);
80
81 private:
82     JSString(VM& vm, PassRefPtr<StringImpl> value)
83         : JSCell(vm, vm.stringStructure.get())
84         , m_flags(0)
85         , m_value(value)
86     {
87     }
88
89     JSString(VM& vm)
90         : JSCell(vm, vm.stringStructure.get())
91         , m_flags(0)
92     {
93     }
94
95     void finishCreation(VM& vm, size_t length)
96     {
97         ASSERT(!m_value.isNull());
98         Base::finishCreation(vm);
99         m_length = length;
100         setIs8Bit(m_value.impl()->is8Bit());
101         vm.m_newStringsSinceLastHashCons++;
102     }
103
104     void finishCreation(VM& vm, size_t length, size_t cost)
105     {
106         ASSERT(!m_value.isNull());
107         Base::finishCreation(vm);
108         m_length = length;
109         setIs8Bit(m_value.impl()->is8Bit());
110         Heap::heap(this)->reportExtraMemoryCost(cost);
111         vm.m_newStringsSinceLastHashCons++;
112     }
113
114 protected:
115     void finishCreation(VM& vm)
116     {
117         Base::finishCreation(vm);
118         m_length = 0;
119         setIs8Bit(true);
120         vm.m_newStringsSinceLastHashCons++;
121     }
122
123 public:
124     static JSString* create(VM& vm, PassRefPtr<StringImpl> value)
125     {
126         ASSERT(value);
127         int32_t length = value->length();
128         RELEASE_ASSERT(length >= 0);
129         size_t cost = value->cost();
130         JSString* newString = new (NotNull, allocateCell<JSString>(vm.heap)) JSString(vm, value);
131         newString->finishCreation(vm, length, cost);
132         return newString;
133     }
134     static JSString* createHasOtherOwner(VM& vm, PassRefPtr<StringImpl> value)
135     {
136         ASSERT(value);
137         size_t length = value->length();
138         JSString* newString = new (NotNull, allocateCell<JSString>(vm.heap)) JSString(vm, value);
139         newString->finishCreation(vm, length);
140         return newString;
141     }
142
143     Identifier toIdentifier(ExecState*) const;
144     AtomicString toAtomicString(ExecState*) const;
145     AtomicStringImpl* toExistingAtomicString(ExecState*) const;
146     const String& value(ExecState*) const;
147     const String& tryGetValue() const;
148     const StringImpl* tryGetValueImpl() const;
149     unsigned length() const { return m_length; }
150
151     JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const;
152     JS_EXPORT_PRIVATE bool toBoolean() const;
153     bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const;
154     JSObject* toObject(ExecState*, JSGlobalObject*) const;
155     double toNumber(ExecState*) const;
156
157     bool getStringPropertySlot(ExecState*, PropertyName, PropertySlot&);
158     bool getStringPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
159     bool getStringPropertyDescriptor(ExecState*, PropertyName, PropertyDescriptor&);
160
161     bool canGetIndex(unsigned i) { return i < m_length; }
162     JSString* getIndex(ExecState*, unsigned);
163
164     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto)
165     {
166         return Structure::create(vm, globalObject, proto, TypeInfo(StringType, StructureFlags), info());
167     }
168
169     static size_t offsetOfLength() { return OBJECT_OFFSETOF(JSString, m_length); }
170     static size_t offsetOfFlags() { return OBJECT_OFFSETOF(JSString, m_flags); }
171     static size_t offsetOfValue() { return OBJECT_OFFSETOF(JSString, m_value); }
172
173     DECLARE_EXPORT_INFO;
174
175     static void dumpToStream(const JSCell*, PrintStream&);
176     static void visitChildren(JSCell*, SlotVisitor&);
177
178     enum {
179         HashConsLock = 1u << 2,
180         IsHashConsSingleton = 1u << 1,
181         Is8Bit = 1u
182     };
183
184 protected:
185     static const unsigned StructureFlags = OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | StructureIsImmortal;
186
187     friend class JSValue;
188
189     bool isRope() const { return m_value.isNull(); }
190     bool is8Bit() const { return m_flags & Is8Bit; }
191     void setIs8Bit(bool flag) const
192     {
193         if (flag)
194             m_flags |= Is8Bit;
195         else
196             m_flags &= ~Is8Bit;
197     }
198     bool shouldTryHashCons();
199     bool isHashConsSingleton() const { return m_flags & IsHashConsSingleton; }
200     void clearHashConsSingleton() { m_flags &= ~IsHashConsSingleton; }
201     void setHashConsSingleton() { m_flags |= IsHashConsSingleton; }
202     bool tryHashConsLock();
203     void releaseHashConsLock();
204
205     mutable unsigned m_flags;
206
207     // A string is represented either by a String or a rope of fibers.
208     unsigned m_length;
209     mutable String m_value;
210
211 private:
212     friend class LLIntOffsetsExtractor;
213
214     static JSValue toThis(JSCell*, ExecState*, ECMAMode);
215
216     String& string() { ASSERT(!isRope()); return m_value; }
217
218     friend JSValue jsString(ExecState*, JSString*, JSString*);
219     friend JSString* jsSubstring(ExecState*, JSString*, unsigned offset, unsigned length);
220 };
221
222 class JSRopeString : public JSString {
223     friend class JSString;
224
225     friend JSRopeString* jsStringBuilder(VM*);
226
227 public:
228     class RopeBuilder {
229     public:
230         RopeBuilder(VM& vm)
231             : m_vm(vm)
232             , m_jsString(jsStringBuilder(&vm))
233             , m_index(0)
234         {
235         }
236
237         bool append(JSString* jsString)
238         {
239             if (m_index == JSRopeString::s_maxInternalRopeLength)
240                 expand();
241             if (static_cast<int32_t>(m_jsString->length() + jsString->length()) < 0) {
242                 m_jsString = nullptr;
243                 return false;
244             }
245             m_jsString->append(m_vm, m_index++, jsString);
246             return true;
247         }
248
249         JSRopeString* release()
250         {
251             RELEASE_ASSERT(m_jsString);
252             JSRopeString* tmp = m_jsString;
253             m_jsString = 0;
254             return tmp;
255         }
256
257         unsigned length() const { return m_jsString->m_length; }
258
259     private:
260         void expand();
261
262         VM& m_vm;
263         JSRopeString* m_jsString;
264         size_t m_index;
265     };
266
267 private:
268     JSRopeString(VM& vm)
269         : JSString(vm)
270     {
271     }
272
273     void finishCreation(VM& vm, JSString* s1, JSString* s2)
274     {
275         Base::finishCreation(vm);
276         m_length = s1->length() + s2->length();
277         setIs8Bit(s1->is8Bit() && s2->is8Bit());
278         setIsSubstring(false);
279         fiber(0).set(vm, this, s1);
280         fiber(1).set(vm, this, s2);
281         fiber(2).clear();
282     }
283
284     void finishCreation(VM& vm, JSString* s1, JSString* s2, JSString* s3)
285     {
286         Base::finishCreation(vm);
287         m_length = s1->length() + s2->length() + s3->length();
288         setIs8Bit(s1->is8Bit() && s2->is8Bit() &&  s3->is8Bit());
289         setIsSubstring(false);
290         fiber(0).set(vm, this, s1);
291         fiber(1).set(vm, this, s2);
292         fiber(2).set(vm, this, s3);
293     }
294
295     void finishCreation(VM& vm, JSString* base, unsigned offset, unsigned length)
296     {
297         Base::finishCreation(vm);
298         ASSERT(!base->isRope());
299         ASSERT(!sumOverflows<int32_t>(offset, length));
300         ASSERT(offset + length <= base->length());
301         m_length = length;
302         setIs8Bit(base->is8Bit());
303         setIsSubstring(true);
304         substringBase().set(vm, this, base);
305         substringOffset() = offset;
306     }
307
308     void finishCreation(VM& vm)
309     {
310         JSString::finishCreation(vm);
311         setIsSubstring(false);
312         fiber(0).clear();
313         fiber(1).clear();
314         fiber(2).clear();
315     }
316
317     void append(VM& vm, size_t index, JSString* jsString)
318     {
319         fiber(index).set(vm, this, jsString);
320         m_length += jsString->m_length;
321         RELEASE_ASSERT(static_cast<int32_t>(m_length) >= 0);
322         setIs8Bit(is8Bit() && jsString->is8Bit());
323     }
324
325     static JSRopeString* createNull(VM& vm)
326     {
327         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
328         newString->finishCreation(vm);
329         return newString;
330     }
331
332 public:
333     static JSString* create(VM& vm, JSString* s1, JSString* s2)
334     {
335         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
336         newString->finishCreation(vm, s1, s2);
337         return newString;
338     }
339     static JSString* create(VM& vm, JSString* s1, JSString* s2, JSString* s3)
340     {
341         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
342         newString->finishCreation(vm, s1, s2, s3);
343         return newString;
344     }
345
346     static JSString* create(VM& vm, JSString* base, unsigned offset, unsigned length)
347     {
348         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
349         newString->finishCreation(vm, base, offset, length);
350         return newString;
351     }
352
353     void visitFibers(SlotVisitor&);
354
355     static ptrdiff_t offsetOfFibers() { return OBJECT_OFFSETOF(JSRopeString, u); }
356
357     static const unsigned s_maxInternalRopeLength = 3;
358
359 private:
360     friend JSValue jsStringFromRegisterArray(ExecState*, Register*, unsigned);
361     friend JSValue jsStringFromArguments(ExecState*, JSValue);
362
363     JS_EXPORT_PRIVATE void resolveRope(ExecState*) const;
364     JS_EXPORT_PRIVATE void resolveRopeToAtomicString(ExecState*) const;
365     JS_EXPORT_PRIVATE AtomicStringImpl* resolveRopeToExistingAtomicString(ExecState*) const;
366     void resolveRopeSlowCase8(LChar*) const;
367     void resolveRopeSlowCase(UChar*) const;
368     void outOfMemory(ExecState*) const;
369     void resolveRopeInternal8(LChar*) const;
370     void resolveRopeInternal8NoSubstring(LChar*) const;
371     void resolveRopeInternal16(UChar*) const;
372     void resolveRopeInternal16NoSubstring(UChar*) const;
373     void clearFibers() const;
374
375     JS_EXPORT_PRIVATE JSString* getIndexSlowCase(ExecState*, unsigned);
376
377     WriteBarrierBase<JSString>& fiber(unsigned i) const
378     {
379         ASSERT(!isSubstring());
380         ASSERT(i < s_maxInternalRopeLength);
381         return u[i].string;
382     }
383
384     WriteBarrierBase<JSString>& substringBase() const
385     {
386         return u[1].string;
387     }
388
389     uintptr_t& substringOffset() const
390     {
391         return u[2].number;
392     }
393
394     static uintptr_t notSubstringSentinel()
395     {
396         return 0;
397     }
398
399     static uintptr_t substringSentinel()
400     {
401         return 1;
402     }
403
404     bool isSubstring() const
405     {
406         return u[0].number == substringSentinel();
407     }
408
409     void setIsSubstring(bool isSubstring)
410     {
411         u[0].number = isSubstring ? substringSentinel() : notSubstringSentinel();
412     }
413
414     mutable union {
415         uintptr_t number;
416         WriteBarrierBase<JSString> string;
417     } u[s_maxInternalRopeLength];
418 };
419
420
421 inline const StringImpl* JSString::tryGetValueImpl() const
422 {
423     return m_value.impl();
424 }
425
426 JSString* asString(JSValue);
427
428 inline JSString* asString(JSValue value)
429 {
430     ASSERT(value.asCell()->isString());
431     return jsCast<JSString*>(value.asCell());
432 }
433
434 inline JSString* jsEmptyString(VM* vm)
435 {
436     return vm->smallStrings.emptyString();
437 }
438
439 ALWAYS_INLINE JSString* jsSingleCharacterString(VM* vm, UChar c)
440 {
441     if (c <= maxSingleCharacterString)
442         return vm->smallStrings.singleCharacterString(c);
443     return JSString::create(*vm, String(&c, 1).impl());
444 }
445
446 ALWAYS_INLINE JSString* jsSingleCharacterSubstring(ExecState* exec, const String& s, unsigned offset)
447 {
448     VM* vm = &exec->vm();
449     ASSERT(offset < static_cast<unsigned>(s.length()));
450     UChar c = s.characterAt(offset);
451     if (c <= maxSingleCharacterString)
452         return vm->smallStrings.singleCharacterString(c);
453     return JSString::create(*vm, StringImpl::createSubstringSharingImpl(s.impl(), offset, 1));
454 }
455
456 inline JSString* jsNontrivialString(VM* vm, const String& s)
457 {
458     ASSERT(s.length() > 1);
459     return JSString::create(*vm, s.impl());
460 }
461
462 inline JSString* jsNontrivialString(VM* vm, String&& s)
463 {
464     ASSERT(s.length() > 1);
465     return JSString::create(*vm, s.releaseImpl());
466 }
467
468 ALWAYS_INLINE Identifier JSString::toIdentifier(ExecState* exec) const
469 {
470     return Identifier(exec, toAtomicString(exec));
471 }
472
473 ALWAYS_INLINE AtomicString JSString::toAtomicString(ExecState* exec) const
474 {
475     if (isRope())
476         static_cast<const JSRopeString*>(this)->resolveRopeToAtomicString(exec);
477     return AtomicString(m_value);
478 }
479
480 ALWAYS_INLINE AtomicStringImpl* JSString::toExistingAtomicString(ExecState* exec) const
481 {
482     if (isRope())
483         return static_cast<const JSRopeString*>(this)->resolveRopeToExistingAtomicString(exec);
484     if (m_value.impl()->isAtomic())
485         return static_cast<AtomicStringImpl*>(m_value.impl());
486     return AtomicString::find(m_value.impl());
487 }
488
489 inline const String& JSString::value(ExecState* exec) const
490 {
491     if (isRope())
492         static_cast<const JSRopeString*>(this)->resolveRope(exec);
493     return m_value;
494 }
495
496 inline const String& JSString::tryGetValue() const
497 {
498     if (isRope())
499         static_cast<const JSRopeString*>(this)->resolveRope(0);
500     return m_value;
501 }
502
503 inline JSString* JSString::getIndex(ExecState* exec, unsigned i)
504 {
505     ASSERT(canGetIndex(i));
506     if (isRope())
507         return static_cast<JSRopeString*>(this)->getIndexSlowCase(exec, i);
508     ASSERT(i < m_value.length());
509     return jsSingleCharacterSubstring(exec, m_value, i);
510 }
511
512 inline JSString* jsString(VM* vm, const String& s)
513 {
514     int size = s.length();
515     if (!size)
516         return vm->smallStrings.emptyString();
517     if (size == 1) {
518         UChar c = s.characterAt(0);
519         if (c <= maxSingleCharacterString)
520             return vm->smallStrings.singleCharacterString(c);
521     }
522     return JSString::create(*vm, s.impl());
523 }
524
525 inline JSString* jsSubstring(ExecState* exec, JSString* s, unsigned offset, unsigned length)
526 {
527     ASSERT(offset <= static_cast<unsigned>(s->length()));
528     ASSERT(length <= static_cast<unsigned>(s->length()));
529     ASSERT(offset + length <= static_cast<unsigned>(s->length()));
530     VM& vm = exec->vm();
531     if (!length)
532         return vm.smallStrings.emptyString();
533     s->value(exec); // For effect. We need to ensure that any string that is used as a substring base is not a rope.
534     return JSRopeString::create(vm, s, offset, length);
535 }
536
537 inline JSString* jsSubstring8(VM* vm, const String& s, unsigned offset, unsigned length)
538 {
539     ASSERT(offset <= static_cast<unsigned>(s.length()));
540     ASSERT(length <= static_cast<unsigned>(s.length()));
541     ASSERT(offset + length <= static_cast<unsigned>(s.length()));
542     if (!length)
543         return vm->smallStrings.emptyString();
544     if (length == 1) {
545         UChar c = s.characterAt(offset);
546         if (c <= maxSingleCharacterString)
547             return vm->smallStrings.singleCharacterString(c);
548     }
549     return JSString::createHasOtherOwner(*vm, StringImpl::createSubstringSharingImpl8(s.impl(), offset, length));
550 }
551
552 inline JSString* jsSubstring(VM* vm, const String& s, unsigned offset, unsigned length)
553 {
554     ASSERT(offset <= static_cast<unsigned>(s.length()));
555     ASSERT(length <= static_cast<unsigned>(s.length()));
556     ASSERT(offset + length <= static_cast<unsigned>(s.length()));
557     if (!length)
558         return vm->smallStrings.emptyString();
559     if (length == 1) {
560         UChar c = s.characterAt(offset);
561         if (c <= maxSingleCharacterString)
562             return vm->smallStrings.singleCharacterString(c);
563     }
564     return JSString::createHasOtherOwner(*vm, StringImpl::createSubstringSharingImpl(s.impl(), offset, length));
565 }
566
567 inline JSString* jsOwnedString(VM* vm, const String& s)
568 {
569     int size = s.length();
570     if (!size)
571         return vm->smallStrings.emptyString();
572     if (size == 1) {
573         UChar c = s.characterAt(0);
574         if (c <= maxSingleCharacterString)
575             return vm->smallStrings.singleCharacterString(c);
576     }
577     return JSString::createHasOtherOwner(*vm, s.impl());
578 }
579
580 inline JSRopeString* jsStringBuilder(VM* vm)
581 {
582     return JSRopeString::createNull(*vm);
583 }
584
585 inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->vm()); }
586 inline JSString* jsString(ExecState* exec, const String& s) { return jsString(&exec->vm(), s); }
587 inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->vm(), c); }
588 inline JSString* jsSubstring8(ExecState* exec, const String& s, unsigned offset, unsigned length) { return jsSubstring8(&exec->vm(), s, offset, length); }
589 inline JSString* jsSubstring(ExecState* exec, const String& s, unsigned offset, unsigned length) { return jsSubstring(&exec->vm(), s, offset, length); }
590 inline JSString* jsNontrivialString(ExecState* exec, const String& s) { return jsNontrivialString(&exec->vm(), s); }
591 inline JSString* jsNontrivialString(ExecState* exec, String&& s) { return jsNontrivialString(&exec->vm(), WTF::move(s)); }
592 inline JSString* jsOwnedString(ExecState* exec, const String& s) { return jsOwnedString(&exec->vm(), s); }
593
594 JS_EXPORT_PRIVATE JSString* jsStringWithCacheSlowCase(VM&, StringImpl&);
595
596 ALWAYS_INLINE JSString* jsStringWithCache(ExecState* exec, const String& s)
597 {
598     VM& vm = exec->vm();
599     StringImpl* stringImpl = s.impl();
600     if (!stringImpl || !stringImpl->length())
601         return jsEmptyString(&vm);
602
603     if (stringImpl->length() == 1) {
604         UChar singleCharacter = (*stringImpl)[0u];
605         if (singleCharacter <= maxSingleCharacterString)
606             return vm.smallStrings.singleCharacterString(static_cast<unsigned char>(singleCharacter));
607     }
608
609     if (JSString* lastCachedString = vm.lastCachedString.get()) {
610         if (lastCachedString->tryGetValueImpl() == stringImpl)
611             return lastCachedString;
612     }
613
614     return jsStringWithCacheSlowCase(vm, *stringImpl);
615 }
616
617 ALWAYS_INLINE JSString* jsStringWithCache(ExecState* exec, const AtomicString& s)
618 {
619     return jsStringWithCache(exec, s.string());
620 }
621
622 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
623 {
624     if (propertyName == exec->propertyNames().length) {
625         slot.setValue(this, DontEnum | DontDelete | ReadOnly, jsNumber(m_length));
626         return true;
627     }
628
629     unsigned i = propertyName.asIndex();
630     if (i < m_length) {
631         ASSERT(i != PropertyName::NotAnIndex); // No need for an explicit check, the above test would always fail!
632         slot.setValue(this, DontDelete | ReadOnly, getIndex(exec, i));
633         return true;
634     }
635
636     return false;
637 }
638
639 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
640 {
641     if (propertyName < m_length) {
642         slot.setValue(this, DontDelete | ReadOnly, getIndex(exec, propertyName));
643         return true;
644     }
645
646     return false;
647 }
648
649 inline bool isJSString(JSValue v) { return v.isCell() && v.asCell()->type() == StringType; }
650
651 // --- JSValue inlines ----------------------------
652
653 inline bool JSValue::toBoolean(ExecState* exec) const
654 {
655     if (isInt32())
656         return asInt32();
657     if (isDouble())
658         return asDouble() > 0.0 || asDouble() < 0.0; // false for NaN
659     if (isCell())
660         return asCell()->toBoolean(exec);
661     return isTrue(); // false, null, and undefined all convert to false.
662 }
663
664 inline JSString* JSValue::toString(ExecState* exec) const
665 {
666     if (isString())
667         return jsCast<JSString*>(asCell());
668     return toStringSlowCase(exec);
669 }
670
671 inline String JSValue::toWTFString(ExecState* exec) const
672 {
673     if (isString())
674         return static_cast<JSString*>(asCell())->value(exec);
675     return toWTFStringSlowCase(exec);
676 }
677
678 ALWAYS_INLINE String inlineJSValueNotStringtoString(const JSValue& value, ExecState* exec)
679 {
680     VM& vm = exec->vm();
681     if (value.isInt32())
682         return vm.numericStrings.add(value.asInt32());
683     if (value.isDouble())
684         return vm.numericStrings.add(value.asDouble());
685     if (value.isTrue())
686         return vm.propertyNames->trueKeyword.string();
687     if (value.isFalse())
688         return vm.propertyNames->falseKeyword.string();
689     if (value.isNull())
690         return vm.propertyNames->nullKeyword.string();
691     if (value.isUndefined())
692         return vm.propertyNames->undefinedKeyword.string();
693     return value.toString(exec)->value(exec);
694 }
695
696 ALWAYS_INLINE String JSValue::toWTFStringInline(ExecState* exec) const
697 {
698     if (isString())
699         return static_cast<JSString*>(asCell())->value(exec);
700
701     return inlineJSValueNotStringtoString(*this, exec);
702 }
703
704 } // namespace JSC
705
706 #endif // JSString_h