JSString.h 26.1 KB
Newer Older
1
/*
2 3
 *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
 *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
4
 *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this library; see the file COPYING.LIB.  If not, write to
mjs's avatar
mjs committed
18
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19
 *  Boston, MA 02110-1301, USA.
20
 *
21 22
 */

darin@apple.com's avatar
darin@apple.com committed
23 24
#ifndef JSString_h
#define JSString_h
25

ggaren@apple.com's avatar
ggaren@apple.com committed
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
#include "RopeImpl.h"
33
#include "Structure.h"
ggaren's avatar
ggaren committed
34

35
namespace JSC {
36

darin@apple.com's avatar
darin@apple.com committed
37 38
    class JSString;

darin@apple.com's avatar
darin@apple.com committed
39
    JSString* jsEmptyString(JSGlobalData*);
darin@apple.com's avatar
darin@apple.com committed
40
    JSString* jsEmptyString(ExecState*);
darin@apple.com's avatar
darin@apple.com committed
41
    JSString* jsString(JSGlobalData*, const UString&); // returns empty string if passed null string
darin@apple.com's avatar
darin@apple.com committed
42 43
    JSString* jsString(ExecState*, const UString&); // returns empty string if passed null string

darin@apple.com's avatar
darin@apple.com committed
44
    JSString* jsSingleCharacterString(JSGlobalData*, UChar);
darin@apple.com's avatar
darin@apple.com committed
45 46
    JSString* jsSingleCharacterString(ExecState*, UChar);
    JSString* jsSingleCharacterSubstring(ExecState*, const UString&, unsigned offset);
darin@apple.com's avatar
darin@apple.com committed
47
    JSString* jsSubstring(JSGlobalData*, const UString&, unsigned offset, unsigned length);
darin@apple.com's avatar
darin@apple.com committed
48 49 50 51
    JSString* jsSubstring(ExecState*, const UString&, unsigned offset, unsigned length);

    // Non-trivial strings are two or more characters long.
    // These functions are faster than just calling jsString.
darin@apple.com's avatar
darin@apple.com committed
52
    JSString* jsNontrivialString(JSGlobalData*, const UString&);
darin@apple.com's avatar
darin@apple.com committed
53
    JSString* jsNontrivialString(ExecState*, const UString&);
darin@apple.com's avatar
darin@apple.com committed
54
    JSString* jsNontrivialString(JSGlobalData*, const char*);
darin@apple.com's avatar
darin@apple.com committed
55 56 57 58 59
    JSString* jsNontrivialString(ExecState*, const char*);

    // Should be used for strings that are owned by an object that will
    // likely outlive the JSValue this makes, such as the parse tree or a
    // DOM object that contains a UString
darin@apple.com's avatar
darin@apple.com committed
60
    JSString* jsOwnedString(JSGlobalData*, const UString&); 
darin@apple.com's avatar
darin@apple.com committed
61 62
    JSString* jsOwnedString(ExecState*, const UString&); 

63 64 65
    typedef void (*JSStringFinalizerCallback)(JSString*, void* context);
    JSString* jsStringWithFinalizer(ExecState*, const UString&, JSStringFinalizerCallback callback, void* context);

ap@apple.com's avatar
ap@apple.com committed
66
    class JS_EXPORTCLASS JSString : public JSCell {
67
    public:
68
        friend class JIT;
ap@apple.com's avatar
ap@apple.com committed
69
        friend class JSGlobalData;
70 71
        friend class SpecializedThunkJIT;
        friend struct ThunkHelpers;
72

73
        class RopeBuilder {
74
        public:
75 76
            RopeBuilder(unsigned fiberCount)
                : m_index(0)
77
                , m_rope(RopeImpl::tryCreateUninitialized(fiberCount))
78 79
            {
            }
80

81
            bool isOutOfMemory() { return !m_rope; }
82

83
            void append(RopeImpl::Fiber& fiber)
84
            {
85 86
                ASSERT(m_rope);
                m_rope->initializeFiber(m_index, fiber);
87
            }
88
            void append(const UString& string)
89
            {
90
                ASSERT(m_rope);
91
                m_rope->initializeFiber(m_index, string.impl());
92
            }
93
            void append(JSString* jsString)
94
            {
95
                if (jsString->isRope()) {
96
                    for (unsigned i = 0; i < jsString->m_fiberCount; ++i)
97
                        append(jsString->m_other.m_fibers[i]);
98
                } else
99
                    append(jsString->string());
100 101
            }

102
            PassRefPtr<RopeImpl> release()
103 104 105 106
            {
                ASSERT(m_index == m_rope->fiberCount());
                return m_rope.release();
            }
107

108 109
            unsigned length() { return m_rope->length(); }

110
        private:
111
            unsigned m_index;
112
            RefPtr<RopeImpl> m_rope;
113 114
        };

115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
        class RopeIterator {
            public:
                RopeIterator() { }

                RopeIterator(RopeImpl::Fiber* fibers, size_t fiberCount)
                {
                    ASSERT(fiberCount);
                    m_workQueue.append(WorkItem(fibers, fiberCount));
                    skipRopes();
                }

                RopeIterator& operator++()
                {
                    WorkItem& item = m_workQueue.last();
                    ASSERT(!RopeImpl::isRope(item.fibers[item.i]));
                    if (++item.i == item.fiberCount)
                        m_workQueue.removeLast();
                    skipRopes();
                    return *this;
                }

136
                StringImpl* operator*()
137 138 139 140
                {
                    WorkItem& item = m_workQueue.last();
                    RopeImpl::Fiber fiber = item.fibers[item.i];
                    ASSERT(!RopeImpl::isRope(fiber));
141
                    return static_cast<StringImpl*>(fiber);
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
                }

                bool operator!=(const RopeIterator& other) const
                {
                    return m_workQueue != other.m_workQueue;
                }

            private:
                struct WorkItem {
                    WorkItem(RopeImpl::Fiber* fibers, size_t fiberCount)
                        : fibers(fibers)
                        , fiberCount(fiberCount)
                        , i(0)
                    {
                    }

                    bool operator!=(const WorkItem& other) const
                    {
                        return fibers != other.fibers || fiberCount != other.fiberCount || i != other.i;
                    }

                    RopeImpl::Fiber* fibers;
                    size_t fiberCount;
                    size_t i;
                };

                void skipRopes()
                {
                    if (m_workQueue.isEmpty())
                        return;

                    while (1) {
                        WorkItem& item = m_workQueue.last();
                        RopeImpl::Fiber fiber = item.fibers[item.i];
                        if (!RopeImpl::isRope(fiber))
                            break;
                        RopeImpl* rope = static_cast<RopeImpl*>(fiber);
                        if (++item.i == item.fiberCount)
                            m_workQueue.removeLast();
                        m_workQueue.append(WorkItem(rope->fibers(), rope->fiberCount()));
                    }
                }

                Vector<WorkItem, 16> m_workQueue;
        };

188
        ALWAYS_INLINE JSString(JSGlobalData* globalData, const UString& value)
darin@apple.com's avatar
darin@apple.com committed
189
            : JSCell(globalData->stringStructure.get())
190
            , m_length(value.length())
191
            , m_value(value)
192
            , m_fiberCount(0)
weinig@apple.com's avatar
weinig@apple.com committed
193
        {
194
            ASSERT(!m_value.isNull());
195
            Heap::heap(this)->reportExtraMemoryCost(value.impl()->cost());
weinig@apple.com's avatar
weinig@apple.com committed
196 197 198
        }

        enum HasOtherOwnerType { HasOtherOwner };
darin@apple.com's avatar
darin@apple.com committed
199
        JSString(JSGlobalData* globalData, const UString& value, HasOtherOwnerType)
darin@apple.com's avatar
darin@apple.com committed
200
            : JSCell(globalData->stringStructure.get())
201
            , m_length(value.length())
202
            , m_value(value)
203
            , m_fiberCount(0)
weinig@apple.com's avatar
weinig@apple.com committed
204
        {
205
            ASSERT(!m_value.isNull());
weinig@apple.com's avatar
weinig@apple.com committed
206
        }
207
        JSString(JSGlobalData* globalData, PassRefPtr<StringImpl> value, HasOtherOwnerType)
darin@apple.com's avatar
darin@apple.com committed
208
            : JSCell(globalData->stringStructure.get())
209
            , m_length(value->length())
210
            , m_value(value)
211
            , m_fiberCount(0)
darin@apple.com's avatar
darin@apple.com committed
212
        {
213
            ASSERT(!m_value.isNull());
darin@apple.com's avatar
darin@apple.com committed
214
        }
215
        JSString(JSGlobalData* globalData, PassRefPtr<RopeImpl> rope)
216
            : JSCell(globalData->stringStructure.get())
217 218
            , m_length(rope->length())
            , m_fiberCount(1)
219
        {
220
            m_other.m_fibers[0] = rope.leakRef();
221 222
        }
        // This constructor constructs a new string by concatenating s1 & s2.
223 224
        // This should only be called with fiberCount <= 3.
        JSString(JSGlobalData* globalData, unsigned fiberCount, JSString* s1, JSString* s2)
225
            : JSCell(globalData->stringStructure.get())
226 227
            , m_length(s1->length() + s2->length())
            , m_fiberCount(fiberCount)
228
        {
229
            ASSERT(fiberCount <= s_maxInternalRopeLength);
230 231 232
            unsigned index = 0;
            appendStringInConstruct(index, s1);
            appendStringInConstruct(index, s2);
233
            ASSERT(fiberCount == index);
234
        }
235
        // This constructor constructs a new string by concatenating s1 & s2.
236 237
        // This should only be called with fiberCount <= 3.
        JSString(JSGlobalData* globalData, unsigned fiberCount, JSString* s1, const UString& u2)
238
            : JSCell(globalData->stringStructure.get())
239
            , m_length(s1->length() + u2.length())
240
            , m_fiberCount(fiberCount)
241
        {
242
            ASSERT(fiberCount <= s_maxInternalRopeLength);
243 244 245
            unsigned index = 0;
            appendStringInConstruct(index, s1);
            appendStringInConstruct(index, u2);
246
            ASSERT(fiberCount == index);
247 248
        }
        // This constructor constructs a new string by concatenating s1 & s2.
249 250
        // This should only be called with fiberCount <= 3.
        JSString(JSGlobalData* globalData, unsigned fiberCount, const UString& u1, JSString* s2)
251
            : JSCell(globalData->stringStructure.get())
252
            , m_length(u1.length() + s2->length())
253
            , m_fiberCount(fiberCount)
254
        {
255
            ASSERT(fiberCount <= s_maxInternalRopeLength);
256 257 258
            unsigned index = 0;
            appendStringInConstruct(index, u1);
            appendStringInConstruct(index, s2);
259
            ASSERT(fiberCount == index);
260
        }
261
        // This constructor constructs a new string by concatenating v1, v2 & v3.
262 263
        // This should only be called with fiberCount <= 3 ... which since every
        // value must require a fiberCount of at least one implies that the length
264 265 266
        // for each value must be exactly 1!
        JSString(ExecState* exec, JSValue v1, JSValue v2, JSValue v3)
            : JSCell(exec->globalData().stringStructure.get())
267 268
            , m_length(0)
            , m_fiberCount(s_maxInternalRopeLength)
269 270 271 272 273 274 275 276
        {
            unsigned index = 0;
            appendValueInConstructAndIncrementLength(exec, index, v1);
            appendValueInConstructAndIncrementLength(exec, index, v2);
            appendValueInConstructAndIncrementLength(exec, index, v3);
            ASSERT(index == s_maxInternalRopeLength);
        }

277 278 279
        // This constructor constructs a new string by concatenating u1 & u2.
        JSString(JSGlobalData* globalData, const UString& u1, const UString& u2)
            : JSCell(globalData->stringStructure.get())
280
            , m_length(u1.length() + u2.length())
281 282 283 284 285 286 287 288 289 290 291
            , m_fiberCount(2)
        {
            unsigned index = 0;
            appendStringInConstruct(index, u1);
            appendStringInConstruct(index, u2);
            ASSERT(index <= s_maxInternalRopeLength);
        }

        // This constructor constructs a new string by concatenating u1, u2 & u3.
        JSString(JSGlobalData* globalData, const UString& u1, const UString& u2, const UString& u3)
            : JSCell(globalData->stringStructure.get())
292
            , m_length(u1.length() + u2.length() + u3.length())
293 294 295 296 297 298 299 300 301
            , m_fiberCount(s_maxInternalRopeLength)
        {
            unsigned index = 0;
            appendStringInConstruct(index, u1);
            appendStringInConstruct(index, u2);
            appendStringInConstruct(index, u3);
            ASSERT(index <= s_maxInternalRopeLength);
        }

302 303
        JSString(JSGlobalData* globalData, const UString& value, JSStringFinalizerCallback finalizer, void* context)
            : JSCell(globalData->stringStructure.get())
304
            , m_length(value.length())
305
            , m_value(value)
306
            , m_fiberCount(0)
307
        {
308
            ASSERT(!m_value.isNull());
309
            // nasty hack because we can't union non-POD types
310 311
            m_other.m_finalizerCallback = finalizer;
            m_other.m_finalizerContext = context;
312
            Heap::heap(this)->reportExtraMemoryCost(value.impl()->cost());
313 314
        }

315 316
        ~JSString()
        {
ap@apple.com's avatar
ap@apple.com committed
317
            ASSERT(vptr() == JSGlobalData::jsStringVPtr);
318 319 320 321 322 323 324 325 326
            if (!m_fiberCount) {
                if (m_other.m_finalizerCallback)
                    m_other.m_finalizerCallback(this, m_other.m_finalizerContext);
            } else {
                unsigned i = 0;
                do
                    RopeImpl::deref(m_other.m_fibers[i]);
                while (++i < m_fiberCount);
            }
327
        }
328 329 330

        const UString& value(ExecState* exec) const
        {
331
            if (isRope())
332 333 334
                resolveRope(exec);
            return m_value;
        }
barraclough@apple.com's avatar
barraclough@apple.com committed
335
        const UString& tryGetValue() const
336
        {
barraclough@apple.com's avatar
barraclough@apple.com committed
337 338
            if (isRope())
                resolveRope(0);
339 340
            return m_value;
        }
341
        unsigned length() { return m_length; }
weinig@apple.com's avatar
weinig@apple.com committed
342 343

        bool getStringPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
344
        bool getStringPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
345
        bool getStringPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor&);
weinig@apple.com's avatar
weinig@apple.com committed
346

347
        bool canGetIndex(unsigned i) { return i < m_length; }
348
        JSString* getIndex(ExecState*, unsigned);
349
        JSString* getIndexSlowCase(ExecState*, unsigned);
weinig@apple.com's avatar
weinig@apple.com committed
350

351 352
        JSValue replaceCharacter(ExecState*, UChar, const UString& replacement);

353
        static PassRefPtr<Structure> createStructure(JSGlobalData& globalData, JSValue proto) { return Structure::create(globalData, proto, TypeInfo(StringType, OverridesGetOwnPropertySlot | NeedsThisConversion), AnonymousSlotCount, 0); }
mjs@apple.com's avatar
mjs@apple.com committed
354

weinig@apple.com's avatar
weinig@apple.com committed
355
    private:
356 357 358
        enum VPtrStealingHackType { VPtrStealingHack };
        JSString(VPtrStealingHackType) 
            : JSCell(0)
359
            , m_fiberCount(0)
360 361
        {
        }
weinig@apple.com's avatar
weinig@apple.com committed
362

363
        void resolveRope(ExecState*) const;
364
        JSString* substringFromRope(ExecState*, unsigned offset, unsigned length);
365

366 367
        void appendStringInConstruct(unsigned& index, const UString& string)
        {
368
            StringImpl* impl = string.impl();
369 370
            impl->ref();
            m_other.m_fibers[index++] = impl;
371 372
        }

373 374 375
        void appendStringInConstruct(unsigned& index, JSString* jsString)
        {
            if (jsString->isRope()) {
376
                for (unsigned i = 0; i < jsString->m_fiberCount; ++i) {
377
                    RopeImpl::Fiber fiber = jsString->m_other.m_fibers[i];
378 379 380
                    fiber->ref();
                    m_other.m_fibers[index++] = fiber;
                }
381
            } else
382
                appendStringInConstruct(index, jsString->string());
383 384 385 386 387
        }

        void appendValueInConstructAndIncrementLength(ExecState* exec, unsigned& index, JSValue v)
        {
            if (v.isString()) {
388 389
                ASSERT(v.asCell()->isString());
                JSString* s = static_cast<JSString*>(v.asCell());
390
                ASSERT(s->fiberCount() == 1);
391
                appendStringInConstruct(index, s);
392
                m_length += s->length();
393 394
            } else {
                UString u(v.toString(exec));
395
                StringImpl* impl = u.impl();
396 397
                impl->ref();
                m_other.m_fibers[index++] = impl;
398
                m_length += u.length();
399 400 401
            }
        }

ggaren@apple.com's avatar
ggaren@apple.com committed
402 403
        virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const;
        virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue& value);
mjs@apple.com's avatar
mjs@apple.com committed
404
        virtual bool toBoolean(ExecState*) const;
weinig@apple.com's avatar
weinig@apple.com committed
405
        virtual double toNumber(ExecState*) const;
406
        virtual JSObject* toObject(ExecState*, JSGlobalObject*) const;
weinig@apple.com's avatar
weinig@apple.com committed
407 408 409 410 411 412 413
        virtual UString toString(ExecState*) const;

        virtual JSObject* toThisObject(ExecState*) const;

        // Actually getPropertySlot, not getOwnPropertySlot (see JSCell).
        virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
        virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
414
        virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&);
weinig@apple.com's avatar
weinig@apple.com committed
415

416 417
        static const unsigned s_maxInternalRopeLength = 3;

418
        // A string is represented either by a UString or a RopeImpl.
419
        unsigned m_length;
420
        mutable UString m_value;
421
        mutable unsigned m_fiberCount;
422 423 424 425
        // This structure exists to support a temporary workaround for a GC issue.
        struct JSStringFinalizerStruct {
            JSStringFinalizerStruct() : m_finalizerCallback(0) {}
            union {
426
                mutable FixedArray<RopeImpl::Fiber, s_maxInternalRopeLength> m_fibers;
427 428 429 430 431 432
                struct {
                    JSStringFinalizerCallback m_finalizerCallback;
                    void* m_finalizerContext;
                };
            };
        } m_other;
433

434
        bool isRope() const { return m_fiberCount; }
435
        UString& string() { ASSERT(!isRope()); return m_value; }
436
        unsigned fiberCount() { return m_fiberCount ? m_fiberCount : 1; }
437 438

        friend JSValue jsString(ExecState* exec, JSString* s1, JSString* s2);
439 440
        friend JSValue jsString(ExecState* exec, const UString& u1, JSString* s2);
        friend JSValue jsString(ExecState* exec, JSString* s1, const UString& u2);
441
        friend JSValue jsString(ExecState* exec, Register* strings, unsigned count);
442
        friend JSValue jsString(ExecState* exec, JSValue thisValue);
443
        friend JSString* jsStringWithFinalizer(ExecState*, const UString&, JSStringFinalizerCallback callback, void* context);
444
        friend JSString* jsSubstring(ExecState* exec, JSString* s, unsigned offset, unsigned length);
weinig@apple.com's avatar
weinig@apple.com committed
445 446
    };

ggaren@apple.com's avatar
ggaren@apple.com committed
447
    JSString* asString(JSValue);
darin@apple.com's avatar
darin@apple.com committed
448

ap@apple.com's avatar
ap@apple.com committed
449 450 451 452 453 454 455 456 457 458
    // When an object is created from a different DLL, MSVC changes vptr to a "local" one right after invoking a constructor,
    // see <http://groups.google.com/group/microsoft.public.vc.language/msg/55cdcefeaf770212>.
    // This breaks isJSString(), and we don't need that hack anyway, so we change vptr back to primary one.
    // The below function must be called by any inline function that invokes a JSString constructor.
#if COMPILER(MSVC) && !defined(BUILDING_JavaScriptCore)
    inline JSString* fixupVPtr(JSGlobalData* globalData, JSString* string) { string->setVPtr(globalData->jsStringVPtr); return string; }
#else
    inline JSString* fixupVPtr(JSGlobalData*, JSString* string) { return string; }
#endif

ggaren@apple.com's avatar
ggaren@apple.com committed
459
    inline JSString* asString(JSValue value)
darin@apple.com's avatar
darin@apple.com committed
460
    {
461 462
        ASSERT(value.asCell()->isString());
        return static_cast<JSString*>(value.asCell());
darin@apple.com's avatar
darin@apple.com committed
463 464
    }

darin@apple.com's avatar
darin@apple.com committed
465
    inline JSString* jsEmptyString(JSGlobalData* globalData)
darin@apple.com's avatar
darin@apple.com committed
466
    {
darin@apple.com's avatar
darin@apple.com committed
467
        return globalData->smallStrings.emptyString(globalData);
darin@apple.com's avatar
darin@apple.com committed
468
    }
weinig@apple.com's avatar
weinig@apple.com committed
469

darin@apple.com's avatar
darin@apple.com committed
470
    inline JSString* jsSingleCharacterString(JSGlobalData* globalData, UChar c)
darin@apple.com's avatar
darin@apple.com committed
471
    {
472
        if (c <= maxSingleCharacterString)
darin@apple.com's avatar
darin@apple.com committed
473
            return globalData->smallStrings.singleCharacterString(globalData, c);
ap@apple.com's avatar
ap@apple.com committed
474
        return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(&c, 1)));
darin@apple.com's avatar
darin@apple.com committed
475 476
    }

477
    inline JSString* jsSingleCharacterSubstring(ExecState* exec, const UString& s, unsigned offset)
darin@apple.com's avatar
darin@apple.com committed
478
    {
479
        JSGlobalData* globalData = &exec->globalData();
480 481
        ASSERT(offset < static_cast<unsigned>(s.length()));
        UChar c = s.characters()[offset];
482
        if (c <= maxSingleCharacterString)
darin@apple.com's avatar
darin@apple.com committed
483
            return globalData->smallStrings.singleCharacterString(globalData, c);
484
        return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(StringImpl::create(s.impl(), offset, 1))));
darin@apple.com's avatar
darin@apple.com committed
485 486
    }

darin@apple.com's avatar
darin@apple.com committed
487
    inline JSString* jsNontrivialString(JSGlobalData* globalData, const char* s)
darin@apple.com's avatar
darin@apple.com committed
488 489 490 491
    {
        ASSERT(s);
        ASSERT(s[0]);
        ASSERT(s[1]);
ap@apple.com's avatar
ap@apple.com committed
492
        return fixupVPtr(globalData, new (globalData) JSString(globalData, s));
darin@apple.com's avatar
darin@apple.com committed
493 494
    }

darin@apple.com's avatar
darin@apple.com committed
495
    inline JSString* jsNontrivialString(JSGlobalData* globalData, const UString& s)
darin@apple.com's avatar
darin@apple.com committed
496
    {
497
        ASSERT(s.length() > 1);
ap@apple.com's avatar
ap@apple.com committed
498
        return fixupVPtr(globalData, new (globalData) JSString(globalData, s));
darin@apple.com's avatar
darin@apple.com committed
499 500
    }

501
    inline JSString* JSString::getIndex(ExecState* exec, unsigned i)
darin@apple.com's avatar
darin@apple.com committed
502 503
    {
        ASSERT(canGetIndex(i));
504 505
        if (isRope())
            return getIndexSlowCase(exec, i);
506
        ASSERT(i < m_value.length());
barraclough@apple.com's avatar
barraclough@apple.com committed
507
        return jsSingleCharacterSubstring(exec, m_value, i);
darin@apple.com's avatar
darin@apple.com committed
508
    }
weinig@apple.com's avatar
weinig@apple.com committed
509

510 511
    inline JSString* jsString(JSGlobalData* globalData, const UString& s)
    {
512
        int size = s.length();
513 514 515
        if (!size)
            return globalData->smallStrings.emptyString(globalData);
        if (size == 1) {
516
            UChar c = s.characters()[0];
517
            if (c <= maxSingleCharacterString)
518 519
                return globalData->smallStrings.singleCharacterString(globalData, c);
        }
ap@apple.com's avatar
ap@apple.com committed
520
        return fixupVPtr(globalData, new (globalData) JSString(globalData, s));
521
    }
522

523 524
    inline JSString* jsStringWithFinalizer(ExecState* exec, const UString& s, JSStringFinalizerCallback callback, void* context)
    {
525
        ASSERT(s.length() && (s.length() > 1 || s.characters()[0] > maxSingleCharacterString));
526 527 528
        JSGlobalData* globalData = &exec->globalData();
        return fixupVPtr(globalData, new (globalData) JSString(globalData, s, callback, context));
    }
529 530 531 532 533 534 535 536 537 538 539 540 541
    
    inline JSString* jsSubstring(ExecState* exec, JSString* s, unsigned offset, unsigned length)
    {
        ASSERT(offset <= static_cast<unsigned>(s->length()));
        ASSERT(length <= static_cast<unsigned>(s->length()));
        ASSERT(offset + length <= static_cast<unsigned>(s->length()));
        JSGlobalData* globalData = &exec->globalData();
        if (!length)
            return globalData->smallStrings.emptyString(globalData);
        if (s->isRope())
            return s->substringFromRope(exec, offset, length);
        return jsSubstring(globalData, s->m_value, offset, length);
    }
542

543 544
    inline JSString* jsSubstring(JSGlobalData* globalData, const UString& s, unsigned offset, unsigned length)
    {
545 546 547
        ASSERT(offset <= static_cast<unsigned>(s.length()));
        ASSERT(length <= static_cast<unsigned>(s.length()));
        ASSERT(offset + length <= static_cast<unsigned>(s.length()));
548 549 550
        if (!length)
            return globalData->smallStrings.emptyString(globalData);
        if (length == 1) {
551
            UChar c = s.characters()[offset];
552
            if (c <= maxSingleCharacterString)
553 554
                return globalData->smallStrings.singleCharacterString(globalData, c);
        }
555
        return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(StringImpl::create(s.impl(), offset, length)), JSString::HasOtherOwner));
556 557 558 559
    }

    inline JSString* jsOwnedString(JSGlobalData* globalData, const UString& s)
    {
560
        int size = s.length();
561 562 563
        if (!size)
            return globalData->smallStrings.emptyString(globalData);
        if (size == 1) {
564
            UChar c = s.characters()[0];
565
            if (c <= maxSingleCharacterString)
566 567
                return globalData->smallStrings.singleCharacterString(globalData, c);
        }
ap@apple.com's avatar
ap@apple.com committed
568
        return fixupVPtr(globalData, new (globalData) JSString(globalData, s, JSString::HasOtherOwner));
569 570
    }

darin@apple.com's avatar
darin@apple.com committed
571 572 573 574 575 576 577 578
    inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->globalData()); }
    inline JSString* jsString(ExecState* exec, const UString& s) { return jsString(&exec->globalData(), s); }
    inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->globalData(), c); }
    inline JSString* jsSubstring(ExecState* exec, const UString& s, unsigned offset, unsigned length) { return jsSubstring(&exec->globalData(), s, offset, length); }
    inline JSString* jsNontrivialString(ExecState* exec, const UString& s) { return jsNontrivialString(&exec->globalData(), s); }
    inline JSString* jsNontrivialString(ExecState* exec, const char* s) { return jsNontrivialString(&exec->globalData(), s); }
    inline JSString* jsOwnedString(ExecState* exec, const UString& s) { return jsOwnedString(&exec->globalData(), s); } 

weinig@apple.com's avatar
weinig@apple.com committed
579
    ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
580
    {
weinig@apple.com's avatar
weinig@apple.com committed
581
        if (propertyName == exec->propertyNames().length) {
582
            slot.setValue(jsNumber(m_length));
weinig@apple.com's avatar
weinig@apple.com committed
583 584 585 586
            return true;
        }

        bool isStrictUInt32;
587
        unsigned i = propertyName.toUInt32(isStrictUInt32);
588
        if (isStrictUInt32 && i < m_length) {
589
            slot.setValue(getIndex(exec, i));
weinig@apple.com's avatar
weinig@apple.com committed
590 591 592 593
            return true;
        }

        return false;
594
    }
weinig@apple.com's avatar
weinig@apple.com committed
595
        
596
    ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
weinig@apple.com's avatar
weinig@apple.com committed
597
    {
598
        if (propertyName < m_length) {
599
            slot.setValue(getIndex(exec, propertyName));
weinig@apple.com's avatar
weinig@apple.com committed
600 601
            return true;
        }
602

weinig@apple.com's avatar
weinig@apple.com committed
603
        return false;
604 605
    }

ggaren@apple.com's avatar
ggaren@apple.com committed
606
    inline bool isJSString(JSGlobalData* globalData, JSValue v) { return v.isCell() && v.asCell()->vptr() == globalData->jsStringVPtr; }
607

weinig@apple.com's avatar
weinig@apple.com committed
608
    // --- JSValue inlines ----------------------------
609

610 611 612
    inline UString JSValue::toString(ExecState* exec) const
    {
        if (isString())
613
            return static_cast<JSString*>(asCell())->value(exec);
614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629
        if (isInt32())
            return exec->globalData().numericStrings.add(asInt32());
        if (isDouble())
            return exec->globalData().numericStrings.add(asDouble());
        if (isTrue())
            return "true";
        if (isFalse())
            return "false";
        if (isNull())
            return "null";
        if (isUndefined())
            return "undefined";
        ASSERT(isCell());
        return asCell()->toString(exec);
    }

630 631
    inline UString JSValue::toPrimitiveString(ExecState* exec) const
    {
632
        ASSERT(!isString());
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648
        if (isInt32())
            return exec->globalData().numericStrings.add(asInt32());
        if (isDouble())
            return exec->globalData().numericStrings.add(asDouble());
        if (isTrue())
            return "true";
        if (isFalse())
            return "false";
        if (isNull())
            return "null";
        if (isUndefined())
            return "undefined";
        ASSERT(isCell());
        return asCell()->toPrimitive(exec, NoPreference).toString(exec);
    }

649
} // namespace JSC
650

651
#endif // JSString_h