Home | History | Annotate | Download | only in runtime
      1 /*
      2  *  Copyright (C) 1999-2001 Harri Porten (porten (at) kde.org)
      3  *  Copyright (C) 2001 Peter Kelly (pmk (at) post.com)
      4  *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 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 "JSNumberCell.h"
     30 #include "PropertyDescriptor.h"
     31 #include "PropertySlot.h"
     32 
     33 namespace JSC {
     34 
     35     class JSString;
     36 
     37     JSString* jsEmptyString(JSGlobalData*);
     38     JSString* jsEmptyString(ExecState*);
     39     JSString* jsString(JSGlobalData*, const UString&); // returns empty string if passed null string
     40     JSString* jsString(ExecState*, const UString&); // returns empty string if passed null string
     41 
     42     JSString* jsSingleCharacterString(JSGlobalData*, UChar);
     43     JSString* jsSingleCharacterString(ExecState*, UChar);
     44     JSString* jsSingleCharacterSubstring(JSGlobalData*, const UString&, unsigned offset);
     45     JSString* jsSingleCharacterSubstring(ExecState*, const UString&, unsigned offset);
     46     JSString* jsSubstring(JSGlobalData*, const UString&, unsigned offset, unsigned length);
     47     JSString* jsSubstring(ExecState*, const UString&, unsigned offset, unsigned length);
     48 
     49     // Non-trivial strings are two or more characters long.
     50     // These functions are faster than just calling jsString.
     51     JSString* jsNontrivialString(JSGlobalData*, const UString&);
     52     JSString* jsNontrivialString(ExecState*, const UString&);
     53     JSString* jsNontrivialString(JSGlobalData*, const char*);
     54     JSString* jsNontrivialString(ExecState*, const char*);
     55 
     56     // Should be used for strings that are owned by an object that will
     57     // likely outlive the JSValue this makes, such as the parse tree or a
     58     // DOM object that contains a UString
     59     JSString* jsOwnedString(JSGlobalData*, const UString&);
     60     JSString* jsOwnedString(ExecState*, const UString&);
     61 
     62     typedef void (*JSStringFinalizerCallback)(JSString*, void* context);
     63     JSString* jsStringWithFinalizer(ExecState*, const UString&, JSStringFinalizerCallback callback, void* context);
     64 
     65     class JS_EXPORTCLASS JSString : public JSCell {
     66     public:
     67         friend class JIT;
     68         friend class JSGlobalData;
     69 
     70         // A Rope is a string composed of a set of substrings.
     71         class Rope : public RefCounted<Rope> {
     72         public:
     73             // A Rope is composed from a set of smaller strings called Fibers.
     74             // Each Fiber in a rope is either UString::Rep or another Rope.
     75             class Fiber {
     76             public:
     77                 Fiber() : m_value(0) {}
     78                 Fiber(UString::Rep* string) : m_value(reinterpret_cast<intptr_t>(string)) {}
     79                 Fiber(Rope* rope) : m_value(reinterpret_cast<intptr_t>(rope) | 1) {}
     80 
     81                 Fiber(void* nonFiber) : m_value(reinterpret_cast<intptr_t>(nonFiber)) {}
     82 
     83                 void deref()
     84                 {
     85                     if (isRope())
     86                         rope()->deref();
     87                     else
     88                         string()->deref();
     89                 }
     90 
     91                 Fiber& ref()
     92                 {
     93                     if (isString())
     94                         string()->ref();
     95                     else
     96                         rope()->ref();
     97                     return *this;
     98                 }
     99 
    100                 unsigned refAndGetLength()
    101                 {
    102                     if (isString()) {
    103                         UString::Rep* rep = string();
    104                         return rep->ref()->size();
    105                     } else {
    106                         Rope* r = rope();
    107                         r->ref();
    108                         return r->stringLength();
    109                     }
    110                 }
    111 
    112                 bool isRope() { return m_value & 1; }
    113                 Rope* rope() { return reinterpret_cast<Rope*>(m_value & ~1); }
    114                 bool isString() { return !isRope(); }
    115                 UString::Rep* string() { return reinterpret_cast<UString::Rep*>(m_value); }
    116 
    117                 void* nonFiber() { return reinterpret_cast<void*>(m_value); }
    118             private:
    119                 intptr_t m_value;
    120             };
    121 
    122             // Creates a Rope comprising of 'ropeLength' Fibers.
    123             // The Rope is constructed in an uninitialized state - initialize must be called for each Fiber in the Rope.
    124             static PassRefPtr<Rope> createOrNull(unsigned ropeLength)
    125             {
    126                 void* allocation;
    127                 if (tryFastMalloc(sizeof(Rope) + (ropeLength - 1) * sizeof(Fiber)).getValue(allocation))
    128                     return adoptRef(new (allocation) Rope(ropeLength));
    129                 return 0;
    130             }
    131 
    132             ~Rope();
    133             void destructNonRecursive();
    134 
    135             void append(unsigned &index, Fiber& fiber)
    136             {
    137                 m_fibers[index++] = fiber;
    138                 m_stringLength += fiber.refAndGetLength();
    139             }
    140             void append(unsigned &index, const UString& string)
    141             {
    142                 UString::Rep* rep = string.rep();
    143                 m_fibers[index++] = Fiber(rep);
    144                 m_stringLength += rep->ref()->size();
    145             }
    146             void append(unsigned& index, JSString* jsString)
    147             {
    148                 if (jsString->isRope()) {
    149                     for (unsigned i = 0; i < jsString->m_ropeLength; ++i)
    150                         append(index, jsString->m_fibers[i]);
    151                 } else
    152                     append(index, jsString->string());
    153             }
    154 
    155             unsigned ropeLength() { return m_ropeLength; }
    156             unsigned stringLength() { return m_stringLength; }
    157             Fiber& fibers(unsigned index) { return m_fibers[index]; }
    158 
    159         private:
    160             Rope(unsigned ropeLength) : m_ropeLength(ropeLength), m_stringLength(0) {}
    161             void* operator new(size_t, void* inPlace) { return inPlace; }
    162 
    163             unsigned m_ropeLength;
    164             unsigned m_stringLength;
    165             Fiber m_fibers[1];
    166         };
    167 
    168         ALWAYS_INLINE JSString(JSGlobalData* globalData, const UString& value)
    169             : JSCell(globalData->stringStructure.get())
    170             , m_stringLength(value.size())
    171             , m_value(value)
    172             , m_ropeLength(0)
    173         {
    174             Heap::heap(this)->reportExtraMemoryCost(value.cost());
    175         }
    176 
    177         enum HasOtherOwnerType { HasOtherOwner };
    178         JSString(JSGlobalData* globalData, const UString& value, HasOtherOwnerType)
    179             : JSCell(globalData->stringStructure.get())
    180             , m_stringLength(value.size())
    181             , m_value(value)
    182             , m_ropeLength(0)
    183         {
    184         }
    185         JSString(JSGlobalData* globalData, PassRefPtr<UString::Rep> value, HasOtherOwnerType)
    186             : JSCell(globalData->stringStructure.get())
    187             , m_stringLength(value->size())
    188             , m_value(value)
    189             , m_ropeLength(0)
    190         {
    191         }
    192         JSString(JSGlobalData* globalData, PassRefPtr<JSString::Rope> rope)
    193             : JSCell(globalData->stringStructure.get())
    194             , m_stringLength(rope->stringLength())
    195             , m_ropeLength(1)
    196         {
    197             m_fibers[0] = rope.releaseRef();
    198         }
    199         // This constructor constructs a new string by concatenating s1 & s2.
    200         // This should only be called with ropeLength <= 3.
    201         JSString(JSGlobalData* globalData, unsigned ropeLength, JSString* s1, JSString* s2)
    202             : JSCell(globalData->stringStructure.get())
    203             , m_stringLength(s1->length() + s2->length())
    204             , m_ropeLength(ropeLength)
    205         {
    206             ASSERT(ropeLength <= s_maxInternalRopeLength);
    207             unsigned index = 0;
    208             appendStringInConstruct(index, s1);
    209             appendStringInConstruct(index, s2);
    210             ASSERT(ropeLength == index);
    211         }
    212         // This constructor constructs a new string by concatenating s1 & s2.
    213         // This should only be called with ropeLength <= 3.
    214         JSString(JSGlobalData* globalData, unsigned ropeLength, JSString* s1, const UString& u2)
    215             : JSCell(globalData->stringStructure.get())
    216             , m_stringLength(s1->length() + u2.size())
    217             , m_ropeLength(ropeLength)
    218         {
    219             ASSERT(ropeLength <= s_maxInternalRopeLength);
    220             unsigned index = 0;
    221             appendStringInConstruct(index, s1);
    222             appendStringInConstruct(index, u2);
    223             ASSERT(ropeLength == index);
    224         }
    225         // This constructor constructs a new string by concatenating s1 & s2.
    226         // This should only be called with ropeLength <= 3.
    227         JSString(JSGlobalData* globalData, unsigned ropeLength, const UString& u1, JSString* s2)
    228             : JSCell(globalData->stringStructure.get())
    229             , m_stringLength(u1.size() + s2->length())
    230             , m_ropeLength(ropeLength)
    231         {
    232             ASSERT(ropeLength <= s_maxInternalRopeLength);
    233             unsigned index = 0;
    234             appendStringInConstruct(index, u1);
    235             appendStringInConstruct(index, s2);
    236             ASSERT(ropeLength == index);
    237         }
    238         // This constructor constructs a new string by concatenating v1, v2 & v3.
    239         // This should only be called with ropeLength <= 3 ... which since every
    240         // value must require a ropeLength of at least one implies that the length
    241         // for each value must be exactly 1!
    242         JSString(ExecState* exec, JSValue v1, JSValue v2, JSValue v3)
    243             : JSCell(exec->globalData().stringStructure.get())
    244             , m_stringLength(0)
    245             , m_ropeLength(s_maxInternalRopeLength)
    246         {
    247             unsigned index = 0;
    248             appendValueInConstructAndIncrementLength(exec, index, v1);
    249             appendValueInConstructAndIncrementLength(exec, index, v2);
    250             appendValueInConstructAndIncrementLength(exec, index, v3);
    251             ASSERT(index == s_maxInternalRopeLength);
    252         }
    253 
    254         JSString(JSGlobalData* globalData, const UString& value, JSStringFinalizerCallback finalizer, void* context)
    255             : JSCell(globalData->stringStructure.get())
    256             , m_stringLength(value.size())
    257             , m_value(value)
    258             , m_ropeLength(0)
    259         {
    260             // nasty hack because we can't union non-POD types
    261             m_fibers[0] = reinterpret_cast<void*>(reinterpret_cast<ptrdiff_t>(finalizer));
    262             m_fibers[1] = context;
    263             Heap::heap(this)->reportExtraMemoryCost(value.cost());
    264         }
    265 
    266         ~JSString()
    267         {
    268             ASSERT(vptr() == JSGlobalData::jsStringVPtr);
    269             for (unsigned i = 0; i < m_ropeLength; ++i)
    270                 m_fibers[i].deref();
    271 
    272             if (!m_ropeLength && m_fibers[0].nonFiber()) {
    273                 JSStringFinalizerCallback finalizer = reinterpret_cast<JSStringFinalizerCallback>(m_fibers[0].nonFiber());
    274                 finalizer(this, m_fibers[1].nonFiber());
    275             }
    276         }
    277 
    278         const UString& value(ExecState* exec) const
    279         {
    280             if (isRope())
    281                 resolveRope(exec);
    282             return m_value;
    283         }
    284         const UString tryGetValue() const
    285         {
    286             // If this is a rope, m_value should be null -
    287             // if this is not a rope, m_value should be non-null.
    288             ASSERT(isRope() == m_value.isNull());
    289             return m_value;
    290         }
    291         unsigned length() { return m_stringLength; }
    292 
    293         bool getStringPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
    294         bool getStringPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
    295         bool getStringPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor&);
    296 
    297         bool canGetIndex(unsigned i) { return i < m_stringLength; }
    298         JSString* getIndex(ExecState*, unsigned);
    299 
    300         static PassRefPtr<Structure> createStructure(JSValue proto) { return Structure::create(proto, TypeInfo(StringType, OverridesGetOwnPropertySlot | NeedsThisConversion), AnonymousSlotCount); }
    301 
    302     private:
    303         enum VPtrStealingHackType { VPtrStealingHack };
    304         JSString(VPtrStealingHackType)
    305             : JSCell(0)
    306             , m_ropeLength(0)
    307         {
    308         }
    309 
    310         void resolveRope(ExecState*) const;
    311 
    312         void appendStringInConstruct(unsigned& index, const UString& string)
    313         {
    314             m_fibers[index++] = Rope::Fiber(string.rep()->ref());
    315         }
    316 
    317         void appendStringInConstruct(unsigned& index, JSString* jsString)
    318         {
    319             if (jsString->isRope()) {
    320                 for (unsigned i = 0; i < jsString->m_ropeLength; ++i)
    321                     m_fibers[index++] = jsString->m_fibers[i].ref();
    322             } else
    323                 appendStringInConstruct(index, jsString->string());
    324         }
    325 
    326         void appendValueInConstructAndIncrementLength(ExecState* exec, unsigned& index, JSValue v)
    327         {
    328             if (v.isString()) {
    329                 ASSERT(asCell(v)->isString());
    330                 JSString* s = static_cast<JSString*>(asCell(v));
    331                 ASSERT(s->ropeLength() == 1);
    332                 appendStringInConstruct(index, s);
    333                 m_stringLength += s->length();
    334             } else {
    335                 UString u(v.toString(exec));
    336                 m_fibers[index++] = Rope::Fiber(u.rep()->ref());
    337                 m_stringLength += u.size();
    338             }
    339         }
    340 
    341         virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const;
    342         virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue& value);
    343         virtual bool toBoolean(ExecState*) const;
    344         virtual double toNumber(ExecState*) const;
    345         virtual JSObject* toObject(ExecState*) const;
    346         virtual UString toString(ExecState*) const;
    347 
    348         virtual JSObject* toThisObject(ExecState*) const;
    349         virtual UString toThisString(ExecState*) const;
    350         virtual JSString* toThisJSString(ExecState*);
    351 
    352         // Actually getPropertySlot, not getOwnPropertySlot (see JSCell).
    353         virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
    354         virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
    355         virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&);
    356 
    357         static const unsigned s_maxInternalRopeLength = 3;
    358 
    359         // A string is represented either by a UString or a Rope.
    360         unsigned m_stringLength;
    361         mutable UString m_value;
    362         mutable unsigned m_ropeLength;
    363         mutable Rope::Fiber m_fibers[s_maxInternalRopeLength];
    364 
    365         bool isRope() const { return m_ropeLength; }
    366         UString& string() { ASSERT(!isRope()); return m_value; }
    367         unsigned ropeLength() { return m_ropeLength ? m_ropeLength : 1; }
    368 
    369         friend JSValue jsString(ExecState* exec, JSString* s1, JSString* s2);
    370         friend JSValue jsString(ExecState* exec, const UString& u1, JSString* s2);
    371         friend JSValue jsString(ExecState* exec, JSString* s1, const UString& u2);
    372         friend JSValue jsString(ExecState* exec, Register* strings, unsigned count);
    373         friend JSValue jsString(ExecState* exec, JSValue thisValue, const ArgList& args);
    374         friend JSString* jsStringWithFinalizer(ExecState*, const UString&, JSStringFinalizerCallback callback, void* context);
    375     };
    376 
    377     JSString* asString(JSValue);
    378 
    379     // When an object is created from a different DLL, MSVC changes vptr to a "local" one right after invoking a constructor,
    380     // see <http://groups.google.com/group/microsoft.public.vc.language/msg/55cdcefeaf770212>.
    381     // This breaks isJSString(), and we don't need that hack anyway, so we change vptr back to primary one.
    382     // The below function must be called by any inline function that invokes a JSString constructor.
    383 #if COMPILER(MSVC) && !defined(BUILDING_JavaScriptCore)
    384     inline JSString* fixupVPtr(JSGlobalData* globalData, JSString* string) { string->setVPtr(globalData->jsStringVPtr); return string; }
    385 #else
    386     inline JSString* fixupVPtr(JSGlobalData*, JSString* string) { return string; }
    387 #endif
    388 
    389     inline JSString* asString(JSValue value)
    390     {
    391         ASSERT(asCell(value)->isString());
    392         return static_cast<JSString*>(asCell(value));
    393     }
    394 
    395     inline JSString* jsEmptyString(JSGlobalData* globalData)
    396     {
    397         return globalData->smallStrings.emptyString(globalData);
    398     }
    399 
    400     inline JSString* jsSingleCharacterString(JSGlobalData* globalData, UChar c)
    401     {
    402         if (c <= 0xFF)
    403             return globalData->smallStrings.singleCharacterString(globalData, c);
    404         return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(&c, 1)));
    405     }
    406 
    407     inline JSString* jsSingleCharacterSubstring(JSGlobalData* globalData, const UString& s, unsigned offset)
    408     {
    409         ASSERT(offset < static_cast<unsigned>(s.size()));
    410         UChar c = s.data()[offset];
    411         if (c <= 0xFF)
    412             return globalData->smallStrings.singleCharacterString(globalData, c);
    413         return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(UString::Rep::create(s.rep(), offset, 1))));
    414     }
    415 
    416     inline JSString* jsNontrivialString(JSGlobalData* globalData, const char* s)
    417     {
    418         ASSERT(s);
    419         ASSERT(s[0]);
    420         ASSERT(s[1]);
    421         return fixupVPtr(globalData, new (globalData) JSString(globalData, s));
    422     }
    423 
    424     inline JSString* jsNontrivialString(JSGlobalData* globalData, const UString& s)
    425     {
    426         ASSERT(s.size() > 1);
    427         return fixupVPtr(globalData, new (globalData) JSString(globalData, s));
    428     }
    429 
    430     inline JSString* JSString::getIndex(ExecState* exec, unsigned i)
    431     {
    432         ASSERT(canGetIndex(i));
    433         return jsSingleCharacterSubstring(&exec->globalData(), value(exec), i);
    434     }
    435 
    436     inline JSString* jsString(JSGlobalData* globalData, const UString& s)
    437     {
    438         int size = s.size();
    439         if (!size)
    440             return globalData->smallStrings.emptyString(globalData);
    441         if (size == 1) {
    442             UChar c = s.data()[0];
    443             if (c <= 0xFF)
    444                 return globalData->smallStrings.singleCharacterString(globalData, c);
    445         }
    446         return fixupVPtr(globalData, new (globalData) JSString(globalData, s));
    447     }
    448 
    449     inline JSString* jsStringWithFinalizer(ExecState* exec, const UString& s, JSStringFinalizerCallback callback, void* context)
    450     {
    451         ASSERT(s.size() && (s.size() > 1 || s.data()[0] > 0xFF));
    452         JSGlobalData* globalData = &exec->globalData();
    453         return fixupVPtr(globalData, new (globalData) JSString(globalData, s, callback, context));
    454     }
    455 
    456     inline JSString* jsSubstring(JSGlobalData* globalData, const UString& s, unsigned offset, unsigned length)
    457     {
    458         ASSERT(offset <= static_cast<unsigned>(s.size()));
    459         ASSERT(length <= static_cast<unsigned>(s.size()));
    460         ASSERT(offset + length <= static_cast<unsigned>(s.size()));
    461         if (!length)
    462             return globalData->smallStrings.emptyString(globalData);
    463         if (length == 1) {
    464             UChar c = s.data()[offset];
    465             if (c <= 0xFF)
    466                 return globalData->smallStrings.singleCharacterString(globalData, c);
    467         }
    468         return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(UString::Rep::create(s.rep(), offset, length)), JSString::HasOtherOwner));
    469     }
    470 
    471     inline JSString* jsOwnedString(JSGlobalData* globalData, const UString& s)
    472     {
    473         int size = s.size();
    474         if (!size)
    475             return globalData->smallStrings.emptyString(globalData);
    476         if (size == 1) {
    477             UChar c = s.data()[0];
    478             if (c <= 0xFF)
    479                 return globalData->smallStrings.singleCharacterString(globalData, c);
    480         }
    481         return fixupVPtr(globalData, new (globalData) JSString(globalData, s, JSString::HasOtherOwner));
    482     }
    483 
    484     inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->globalData()); }
    485     inline JSString* jsString(ExecState* exec, const UString& s) { return jsString(&exec->globalData(), s); }
    486     inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->globalData(), c); }
    487     inline JSString* jsSingleCharacterSubstring(ExecState* exec, const UString& s, unsigned offset) { return jsSingleCharacterSubstring(&exec->globalData(), s, offset); }
    488     inline JSString* jsSubstring(ExecState* exec, const UString& s, unsigned offset, unsigned length) { return jsSubstring(&exec->globalData(), s, offset, length); }
    489     inline JSString* jsNontrivialString(ExecState* exec, const UString& s) { return jsNontrivialString(&exec->globalData(), s); }
    490     inline JSString* jsNontrivialString(ExecState* exec, const char* s) { return jsNontrivialString(&exec->globalData(), s); }
    491     inline JSString* jsOwnedString(ExecState* exec, const UString& s) { return jsOwnedString(&exec->globalData(), s); }
    492 
    493     ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
    494     {
    495         if (propertyName == exec->propertyNames().length) {
    496             slot.setValue(jsNumber(exec, m_stringLength));
    497             return true;
    498         }
    499 
    500         bool isStrictUInt32;
    501         unsigned i = propertyName.toStrictUInt32(&isStrictUInt32);
    502         if (isStrictUInt32 && i < m_stringLength) {
    503             slot.setValue(jsSingleCharacterSubstring(exec, value(exec), i));
    504             return true;
    505         }
    506 
    507         return false;
    508     }
    509 
    510     ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
    511     {
    512         if (propertyName < m_stringLength) {
    513             slot.setValue(jsSingleCharacterSubstring(exec, value(exec), propertyName));
    514             return true;
    515         }
    516 
    517         return false;
    518     }
    519 
    520     inline bool isJSString(JSGlobalData* globalData, JSValue v) { return v.isCell() && v.asCell()->vptr() == globalData->jsStringVPtr; }
    521 
    522     // --- JSValue inlines ----------------------------
    523 
    524     inline JSString* JSValue::toThisJSString(ExecState* exec)
    525     {
    526         return isCell() ? asCell()->toThisJSString(exec) : jsString(exec, toString(exec));
    527     }
    528 
    529     inline UString JSValue::toString(ExecState* exec) const
    530     {
    531         if (isString())
    532             return static_cast<JSString*>(asCell())->value(exec);
    533         if (isInt32())
    534             return exec->globalData().numericStrings.add(asInt32());
    535         if (isDouble())
    536             return exec->globalData().numericStrings.add(asDouble());
    537         if (isTrue())
    538             return "true";
    539         if (isFalse())
    540             return "false";
    541         if (isNull())
    542             return "null";
    543         if (isUndefined())
    544             return "undefined";
    545         ASSERT(isCell());
    546         return asCell()->toString(exec);
    547     }
    548 
    549     inline UString JSValue::toPrimitiveString(ExecState* exec) const
    550     {
    551         if (isString())
    552             return static_cast<JSString*>(asCell())->value(exec);
    553         if (isInt32())
    554             return exec->globalData().numericStrings.add(asInt32());
    555         if (isDouble())
    556             return exec->globalData().numericStrings.add(asDouble());
    557         if (isTrue())
    558             return "true";
    559         if (isFalse())
    560             return "false";
    561         if (isNull())
    562             return "null";
    563         if (isUndefined())
    564             return "undefined";
    565         ASSERT(isCell());
    566         return asCell()->toPrimitive(exec, NoPreference).toString(exec);
    567     }
    568 
    569 } // namespace JSC
    570 
    571 #endif // JSString_h
    572