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#include "CallFrame.h"
26#include "CommonIdentifiers.h"
27#include "Identifier.h"
28#include "PropertyDescriptor.h"
29#include "PropertySlot.h"
30#include "Structure.h"
31
32namespace JSC {
33
34class JSString;
35class JSRopeString;
36class LLIntOffsetsExtractor;
37
38JSString* jsEmptyString(VM*);
39JSString* jsEmptyString(ExecState*);
40JSString* jsString(VM*, const String&); // returns empty string if passed null string
41JSString* jsString(ExecState*, const String&); // returns empty string if passed null string
42
43JSString* jsSingleCharacterString(VM*, UChar);
44JSString* jsSingleCharacterString(ExecState*, UChar);
45JSString* jsSingleCharacterSubstring(ExecState*, const String&, unsigned offset);
46JSString* jsSubstring(VM*, const String&, unsigned offset, unsigned length);
47JSString* jsSubstring(ExecState*, const String&, unsigned offset, unsigned length);
48
49// Non-trivial strings are two or more characters long.
50// These functions are faster than just calling jsString.
51JSString* jsNontrivialString(VM*, const String&);
52JSString* jsNontrivialString(ExecState*, const String&);
53
54// Should be used for strings that are owned by an object that will
55// likely outlive the JSValue this makes, such as the parse tree or a
56// DOM object that contains a String
57JSString* jsOwnedString(VM*, const String&);
58JSString* jsOwnedString(ExecState*, const String&);
59
60JSRopeString* jsStringBuilder(VM*);
61
62class JSString : public JSCell {
63public:
64    friend class JIT;
65    friend class VM;
66    friend class SpecializedThunkJIT;
67    friend class JSRopeString;
68    friend class MarkStack;
69    friend class SlotVisitor;
70    friend struct ThunkHelpers;
71
72    typedef JSCell Base;
73
74    static const bool needsDestruction = true;
75    static const bool hasImmortalStructure = true;
76    static void destroy(JSCell*);
77
78private:
79    JSString(VM& vm, PassRefPtr<StringImpl> value)
80        : JSCell(vm, vm.stringStructure.get())
81        , m_flags(0)
82        , m_value(value)
83    {
84    }
85
86    JSString(VM& vm)
87        : JSCell(vm, vm.stringStructure.get())
88        , m_flags(0)
89    {
90    }
91
92    void finishCreation(VM& vm, size_t length)
93    {
94        ASSERT(!m_value.isNull());
95        Base::finishCreation(vm);
96        m_length = length;
97        setIs8Bit(m_value.impl()->is8Bit());
98        vm.m_newStringsSinceLastHashCons++;
99    }
100
101    void finishCreation(VM& vm, size_t length, size_t cost)
102    {
103        ASSERT(!m_value.isNull());
104        Base::finishCreation(vm);
105        m_length = length;
106        setIs8Bit(m_value.impl()->is8Bit());
107        Heap::heap(this)->reportExtraMemoryCost(cost);
108        vm.m_newStringsSinceLastHashCons++;
109    }
110
111protected:
112    void finishCreation(VM& vm)
113    {
114        Base::finishCreation(vm);
115        m_length = 0;
116        setIs8Bit(true);
117        vm.m_newStringsSinceLastHashCons++;
118    }
119
120public:
121    static JSString* create(VM& vm, PassRefPtr<StringImpl> value)
122    {
123        ASSERT(value);
124        int32_t length = value->length();
125        RELEASE_ASSERT(length >= 0);
126        size_t cost = value->cost();
127        JSString* newString = new (NotNull, allocateCell<JSString>(vm.heap)) JSString(vm, value);
128        newString->finishCreation(vm, length, cost);
129        return newString;
130    }
131    static JSString* createHasOtherOwner(VM& vm, PassRefPtr<StringImpl> value)
132    {
133        ASSERT(value);
134        size_t length = value->length();
135        JSString* newString = new (NotNull, allocateCell<JSString>(vm.heap)) JSString(vm, value);
136        newString->finishCreation(vm, length);
137        return newString;
138    }
139
140    const String& value(ExecState*) const;
141    const String& tryGetValue() const;
142    unsigned length() { return m_length; }
143
144    JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const;
145    JS_EXPORT_PRIVATE bool toBoolean() const;
146    bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const;
147    JSObject* toObject(ExecState*, JSGlobalObject*) const;
148    double toNumber(ExecState*) const;
149
150    bool getStringPropertySlot(ExecState*, PropertyName, PropertySlot&);
151    bool getStringPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
152    bool getStringPropertyDescriptor(ExecState*, PropertyName, PropertyDescriptor&);
153
154    bool canGetIndex(unsigned i) { return i < m_length; }
155    JSString* getIndex(ExecState*, unsigned);
156
157    static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto)
158    {
159        return Structure::create(vm, globalObject, proto, TypeInfo(StringType, OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero), &s_info);
160    }
161
162    static size_t offsetOfLength() { return OBJECT_OFFSETOF(JSString, m_length); }
163    static size_t offsetOfFlags() { return OBJECT_OFFSETOF(JSString, m_flags); }
164    static size_t offsetOfValue() { return OBJECT_OFFSETOF(JSString, m_value); }
165
166    static JS_EXPORTDATA const ClassInfo s_info;
167
168    static void visitChildren(JSCell*, SlotVisitor&);
169
170    enum {
171        HashConsLock = 1u << 2,
172        IsHashConsSingleton = 1u << 1,
173        Is8Bit = 1u
174    };
175
176protected:
177    friend class JSValue;
178
179    bool isRope() const { return m_value.isNull(); }
180    bool is8Bit() const { return m_flags & Is8Bit; }
181    void setIs8Bit(bool flag)
182    {
183        if (flag)
184            m_flags |= Is8Bit;
185        else
186            m_flags &= ~Is8Bit;
187    }
188    bool shouldTryHashCons();
189    bool isHashConsSingleton() const { return m_flags & IsHashConsSingleton; }
190    void clearHashConsSingleton() { m_flags &= ~IsHashConsSingleton; }
191    void setHashConsSingleton() { m_flags |= IsHashConsSingleton; }
192    bool tryHashConsLock();
193    void releaseHashConsLock();
194
195    unsigned m_flags;
196
197    // A string is represented either by a String or a rope of fibers.
198    unsigned m_length;
199    mutable String m_value;
200
201private:
202    friend class LLIntOffsetsExtractor;
203
204    static JSObject* toThisObject(JSCell*, ExecState*);
205
206    // Actually getPropertySlot, not getOwnPropertySlot (see JSCell).
207    static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&);
208    static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&);
209
210    String& string() { ASSERT(!isRope()); return m_value; }
211
212    friend JSValue jsString(ExecState*, JSString*, JSString*);
213    friend JSString* jsSubstring(ExecState*, JSString*, unsigned offset, unsigned length);
214};
215
216class JSRopeString : public JSString {
217    friend class JSString;
218
219    friend JSRopeString* jsStringBuilder(VM*);
220
221    class RopeBuilder {
222    public:
223        RopeBuilder(VM& vm)
224            : m_vm(vm)
225            , m_jsString(jsStringBuilder(&vm))
226            , m_index(0)
227        {
228        }
229
230        bool append(JSString* jsString)
231        {
232            if (m_index == JSRopeString::s_maxInternalRopeLength)
233                expand();
234            if (static_cast<int32_t>(m_jsString->length() + jsString->length()) < 0) {
235                m_jsString = nullptr;
236                return false;
237            }
238            m_jsString->append(m_vm, m_index++, jsString);
239            return true;
240        }
241
242        JSRopeString* release()
243        {
244            RELEASE_ASSERT(m_jsString);
245            JSRopeString* tmp = m_jsString;
246            m_jsString = 0;
247            return tmp;
248        }
249
250        unsigned length() { return m_jsString->m_length; }
251
252    private:
253        void expand();
254
255        VM& m_vm;
256        JSRopeString* m_jsString;
257        size_t m_index;
258    };
259
260private:
261    JSRopeString(VM& vm)
262        : JSString(vm)
263    {
264    }
265
266    void finishCreation(VM& vm, JSString* s1, JSString* s2)
267    {
268        Base::finishCreation(vm);
269        m_length = s1->length() + s2->length();
270        setIs8Bit(s1->is8Bit() && s2->is8Bit());
271        m_fibers[0].set(vm, this, s1);
272        m_fibers[1].set(vm, this, s2);
273    }
274
275    void finishCreation(VM& vm, JSString* s1, JSString* s2, JSString* s3)
276    {
277        Base::finishCreation(vm);
278        m_length = s1->length() + s2->length() + s3->length();
279        setIs8Bit(s1->is8Bit() && s2->is8Bit() &&  s3->is8Bit());
280        m_fibers[0].set(vm, this, s1);
281        m_fibers[1].set(vm, this, s2);
282        m_fibers[2].set(vm, this, s3);
283    }
284
285    void finishCreation(VM& vm)
286    {
287        JSString::finishCreation(vm);
288    }
289
290    void append(VM& vm, size_t index, JSString* jsString)
291    {
292        m_fibers[index].set(vm, this, jsString);
293        m_length += jsString->m_length;
294        RELEASE_ASSERT(static_cast<int32_t>(m_length) >= 0);
295        setIs8Bit(is8Bit() && jsString->is8Bit());
296    }
297
298    static JSRopeString* createNull(VM& vm)
299    {
300        JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
301        newString->finishCreation(vm);
302        return newString;
303    }
304
305public:
306    static JSString* create(VM& vm, JSString* s1, JSString* s2)
307    {
308        JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
309        newString->finishCreation(vm, s1, s2);
310        return newString;
311    }
312    static JSString* create(VM& vm, JSString* s1, JSString* s2, JSString* s3)
313    {
314        JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
315        newString->finishCreation(vm, s1, s2, s3);
316        return newString;
317    }
318
319    void visitFibers(SlotVisitor&);
320
321    static ptrdiff_t offsetOfFibers() { return OBJECT_OFFSETOF(JSRopeString, m_fibers); }
322
323    static const unsigned s_maxInternalRopeLength = 3;
324
325private:
326    friend JSValue jsString(ExecState*, Register*, unsigned);
327    friend JSValue jsStringFromArguments(ExecState*, JSValue);
328
329    JS_EXPORT_PRIVATE void resolveRope(ExecState*) const;
330    void resolveRopeSlowCase8(LChar*) const;
331    void resolveRopeSlowCase(UChar*) const;
332    void outOfMemory(ExecState*) const;
333
334    JSString* getIndexSlowCase(ExecState*, unsigned);
335
336    mutable FixedArray<WriteBarrier<JSString>, s_maxInternalRopeLength> m_fibers;
337};
338
339JSString* asString(JSValue);
340
341inline JSString* asString(JSValue value)
342{
343    ASSERT(value.asCell()->isString());
344    return jsCast<JSString*>(value.asCell());
345}
346
347inline JSString* jsEmptyString(VM* vm)
348{
349    return vm->smallStrings.emptyString();
350}
351
352ALWAYS_INLINE JSString* jsSingleCharacterString(VM* vm, UChar c)
353{
354    if (c <= maxSingleCharacterString)
355        return vm->smallStrings.singleCharacterString(vm, c);
356    return JSString::create(*vm, String(&c, 1).impl());
357}
358
359ALWAYS_INLINE JSString* jsSingleCharacterSubstring(ExecState* exec, const String& s, unsigned offset)
360{
361    VM* vm = &exec->vm();
362    ASSERT(offset < static_cast<unsigned>(s.length()));
363    UChar c = s.characterAt(offset);
364    if (c <= maxSingleCharacterString)
365        return vm->smallStrings.singleCharacterString(vm, c);
366    return JSString::create(*vm, StringImpl::create(s.impl(), offset, 1));
367}
368
369inline JSString* jsNontrivialString(VM* vm, const String& s)
370{
371    ASSERT(s.length() > 1);
372    return JSString::create(*vm, s.impl());
373}
374
375inline const String& JSString::value(ExecState* exec) const
376{
377    if (isRope())
378        static_cast<const JSRopeString*>(this)->resolveRope(exec);
379    return m_value;
380}
381
382inline const String& JSString::tryGetValue() const
383{
384    if (isRope())
385        static_cast<const JSRopeString*>(this)->resolveRope(0);
386    return m_value;
387}
388
389inline JSString* JSString::getIndex(ExecState* exec, unsigned i)
390{
391    ASSERT(canGetIndex(i));
392    if (isRope())
393        return static_cast<JSRopeString*>(this)->getIndexSlowCase(exec, i);
394    ASSERT(i < m_value.length());
395    return jsSingleCharacterSubstring(exec, m_value, i);
396}
397
398inline JSString* jsString(VM* vm, const String& s)
399{
400    int size = s.length();
401    if (!size)
402        return vm->smallStrings.emptyString();
403    if (size == 1) {
404        UChar c = s.characterAt(0);
405        if (c <= maxSingleCharacterString)
406            return vm->smallStrings.singleCharacterString(vm, c);
407    }
408    return JSString::create(*vm, s.impl());
409}
410
411inline JSString* jsSubstring(ExecState* exec, JSString* s, unsigned offset, unsigned length)
412{
413    ASSERT(offset <= static_cast<unsigned>(s->length()));
414    ASSERT(length <= static_cast<unsigned>(s->length()));
415    ASSERT(offset + length <= static_cast<unsigned>(s->length()));
416    VM* vm = &exec->vm();
417    if (!length)
418        return vm->smallStrings.emptyString();
419    return jsSubstring(vm, s->value(exec), offset, length);
420}
421
422inline JSString* jsSubstring8(VM* vm, const String& s, unsigned offset, unsigned length)
423{
424    ASSERT(offset <= static_cast<unsigned>(s.length()));
425    ASSERT(length <= static_cast<unsigned>(s.length()));
426    ASSERT(offset + length <= static_cast<unsigned>(s.length()));
427    if (!length)
428        return vm->smallStrings.emptyString();
429    if (length == 1) {
430        UChar c = s.characterAt(offset);
431        if (c <= maxSingleCharacterString)
432            return vm->smallStrings.singleCharacterString(vm, c);
433    }
434    return JSString::createHasOtherOwner(*vm, StringImpl::create8(s.impl(), offset, length));
435}
436
437inline JSString* jsSubstring(VM* vm, const String& s, unsigned offset, unsigned length)
438{
439    ASSERT(offset <= static_cast<unsigned>(s.length()));
440    ASSERT(length <= static_cast<unsigned>(s.length()));
441    ASSERT(offset + length <= static_cast<unsigned>(s.length()));
442    if (!length)
443        return vm->smallStrings.emptyString();
444    if (length == 1) {
445        UChar c = s.characterAt(offset);
446        if (c <= maxSingleCharacterString)
447            return vm->smallStrings.singleCharacterString(vm, c);
448    }
449    return JSString::createHasOtherOwner(*vm, StringImpl::create(s.impl(), offset, length));
450}
451
452inline JSString* jsOwnedString(VM* vm, const String& s)
453{
454    int size = s.length();
455    if (!size)
456        return vm->smallStrings.emptyString();
457    if (size == 1) {
458        UChar c = s.characterAt(0);
459        if (c <= maxSingleCharacterString)
460            return vm->smallStrings.singleCharacterString(vm, c);
461    }
462    return JSString::createHasOtherOwner(*vm, s.impl());
463}
464
465inline JSRopeString* jsStringBuilder(VM* vm)
466{
467    return JSRopeString::createNull(*vm);
468}
469
470inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->vm()); }
471inline JSString* jsString(ExecState* exec, const String& s) { return jsString(&exec->vm(), s); }
472inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->vm(), c); }
473inline JSString* jsSubstring8(ExecState* exec, const String& s, unsigned offset, unsigned length) { return jsSubstring8(&exec->vm(), s, offset, length); }
474inline JSString* jsSubstring(ExecState* exec, const String& s, unsigned offset, unsigned length) { return jsSubstring(&exec->vm(), s, offset, length); }
475inline JSString* jsNontrivialString(ExecState* exec, const String& s) { return jsNontrivialString(&exec->vm(), s); }
476inline JSString* jsOwnedString(ExecState* exec, const String& s) { return jsOwnedString(&exec->vm(), s); }
477
478ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
479{
480    if (propertyName == exec->propertyNames().length) {
481        slot.setValue(jsNumber(m_length));
482        return true;
483    }
484
485    unsigned i = propertyName.asIndex();
486    if (i < m_length) {
487        ASSERT(i != PropertyName::NotAnIndex); // No need for an explicit check, the above test would always fail!
488        slot.setValue(getIndex(exec, i));
489        return true;
490    }
491
492    return false;
493}
494
495ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
496{
497    if (propertyName < m_length) {
498        slot.setValue(getIndex(exec, propertyName));
499        return true;
500    }
501
502    return false;
503}
504
505inline bool isJSString(JSValue v) { return v.isCell() && v.asCell()->classInfo() == &JSString::s_info; }
506
507// --- JSValue inlines ----------------------------
508
509inline bool JSValue::toBoolean(ExecState* exec) const
510{
511    if (isInt32())
512        return asInt32();
513    if (isDouble())
514        return asDouble() > 0.0 || asDouble() < 0.0; // false for NaN
515    if (isCell())
516        return asCell()->toBoolean(exec);
517    return isTrue(); // false, null, and undefined all convert to false.
518}
519
520inline JSString* JSValue::toString(ExecState* exec) const
521{
522    if (isString())
523        return jsCast<JSString*>(asCell());
524    return toStringSlowCase(exec);
525}
526
527inline String JSValue::toWTFString(ExecState* exec) const
528{
529    if (isString())
530        return static_cast<JSString*>(asCell())->value(exec);
531    return toWTFStringSlowCase(exec);
532}
533
534ALWAYS_INLINE String inlineJSValueNotStringtoString(const JSValue& value, ExecState* exec)
535{
536    VM& vm = exec->vm();
537    if (value.isInt32())
538        return vm.numericStrings.add(value.asInt32());
539    if (value.isDouble())
540        return vm.numericStrings.add(value.asDouble());
541    if (value.isTrue())
542        return vm.propertyNames->trueKeyword.string();
543    if (value.isFalse())
544        return vm.propertyNames->falseKeyword.string();
545    if (value.isNull())
546        return vm.propertyNames->nullKeyword.string();
547    if (value.isUndefined())
548        return vm.propertyNames->undefinedKeyword.string();
549    return value.toString(exec)->value(exec);
550}
551
552ALWAYS_INLINE String JSValue::toWTFStringInline(ExecState* exec) const
553{
554    if (isString())
555        return static_cast<JSString*>(asCell())->value(exec);
556
557    return inlineJSValueNotStringtoString(*this, exec);
558}
559
560} // namespace JSC
561
562#endif // JSString_h
563