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