[JSC] jsSubstring should resolve rope before calling JSRopeString::create
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSString.h
1 /*
2  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
4  *  Copyright (C) 2003-2019 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 #pragma once
24
25 #include "ArgList.h"
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 "ThrowScope.h"
33 #include <array>
34 #include <wtf/CheckedArithmetic.h>
35 #include <wtf/ForbidHeapAllocation.h>
36 #include <wtf/text/StringView.h>
37
38 namespace JSC {
39
40 class JSString;
41 class JSRopeString;
42 class LLIntOffsetsExtractor;
43
44 JSString* jsEmptyString(VM*);
45 JSString* jsEmptyString(ExecState*);
46 JSString* jsString(VM*, const String&); // returns empty string if passed null string
47 JSString* jsString(ExecState*, const String&); // returns empty string if passed null string
48
49 JSString* jsSingleCharacterString(VM*, UChar);
50 JSString* jsSingleCharacterString(ExecState*, UChar);
51 JSString* jsSubstring(VM*, const String&, unsigned offset, unsigned length);
52
53 // Non-trivial strings are two or more characters long.
54 // These functions are faster than just calling jsString.
55 JSString* jsNontrivialString(VM*, const String&);
56 JSString* jsNontrivialString(ExecState*, const String&);
57 JSString* jsNontrivialString(ExecState*, String&&);
58
59 // Should be used for strings that are owned by an object that will
60 // likely outlive the JSValue this makes, such as the parse tree or a
61 // DOM object that contains a String
62 JSString* jsOwnedString(VM*, const String&);
63 JSString* jsOwnedString(ExecState*, const String&);
64
65 bool isJSString(JSCell*);
66 bool isJSString(JSValue);
67 JSString* asString(JSValue);
68
69 // In 64bit architecture, JSString and JSRopeString have the following memory layout to make sizeof(JSString) == 16 and sizeof(JSRopeString) == 32.
70 // JSString has only one pointer. We use it for String. length() and is8Bit() queries go to StringImpl. In JSRopeString, we reuse the above pointer
71 // place for the 1st fiber. JSRopeString has three fibers so its size is 48. To keep length and is8Bit flag information in JSRopeString, JSRopeString
72 // encodes these information into the fiber pointers. is8Bit flag is encoded in the 1st fiber pointer. length is embedded directly, and two fibers
73 // are compressed into 12bytes. isRope information is encoded in the first fiber's LSB.
74 //
75 // Since length of JSRopeString should be frequently accessed compared to each fiber, we put length in contiguous 32byte field, and compress 2nd
76 // and 3rd fibers into the following 80byte fields. One problem is that now 2nd and 3rd fibers are split. Storing and loading 2nd and 3rd fibers
77 // are not one pointer load operation. To make concurrent collector work correctly, we must initialize 2nd and 3rd fibers at JSRopeString creation
78 // and we must not modify these part later.
79 //
80 //              0                        8        10               16                       32                                     48
81 // JSString     [   ID      ][  header  ][   String pointer      0]
82 // JSRopeString [   ID      ][  header  ][   1st fiber         xyz][  length  ][2nd lower32][2nd upper16][3rd lower16][3rd upper32]
83 //                                                               ^
84 //                                            x:(is8Bit),y:(isSubstring),z:(isRope) bit flags
85 class JSString : public JSCell {
86 public:
87     friend class JIT;
88     friend class VM;
89     friend class SpecializedThunkJIT;
90     friend class JSRopeString;
91     friend class MarkStack;
92     friend class SlotVisitor;
93     friend class SmallStrings;
94
95     typedef JSCell Base;
96     static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | StructureIsImmortal | OverridesToThis;
97
98     static const bool needsDestruction = true;
99     static void destroy(JSCell*);
100     
101     // We specialize the string subspace to get the fastest possible sweep. This wouldn't be
102     // necessary if JSString didn't have a destructor.
103     template<typename, SubspaceAccess>
104     static CompleteSubspace* subspaceFor(VM& vm)
105     {
106         return &vm.stringSpace;
107     }
108     
109     // We employ overflow checks in many places with the assumption that MaxLength
110     // is INT_MAX. Hence, it cannot be changed into another length value without
111     // breaking all the bounds and overflow checks that assume this.
112     static constexpr unsigned MaxLength = std::numeric_limits<int32_t>::max();
113     static_assert(MaxLength == String::MaxLength, "");
114
115     static constexpr uintptr_t isRopeInPointer = 0x1;
116
117 private:
118     String& uninitializedValueInternal() const
119     {
120         return *bitwise_cast<String*>(&m_fiber);
121     }
122
123     String& valueInternal() const
124     {
125         ASSERT(!isRope());
126         return uninitializedValueInternal();
127     }
128
129     JSString(VM& vm, Ref<StringImpl>&& value)
130         : JSCell(vm, vm.stringStructure.get())
131     {
132         new (&uninitializedValueInternal()) String(WTFMove(value));
133     }
134
135     JSString(VM& vm)
136         : JSCell(vm, vm.stringStructure.get())
137         , m_fiber(isRopeInPointer)
138     {
139     }
140
141     void finishCreation(VM& vm, unsigned length)
142     {
143         ASSERT_UNUSED(length, length > 0);
144         ASSERT(!valueInternal().isNull());
145         Base::finishCreation(vm);
146     }
147
148     void finishCreation(VM& vm, unsigned length, size_t cost)
149     {
150         ASSERT_UNUSED(length, length > 0);
151         ASSERT(!valueInternal().isNull());
152         Base::finishCreation(vm);
153         vm.heap.reportExtraMemoryAllocated(cost);
154     }
155
156     static JSString* createEmptyString(VM&);
157
158     static JSString* create(VM& vm, Ref<StringImpl>&& value)
159     {
160         unsigned length = value->length();
161         ASSERT(length > 0);
162         size_t cost = value->cost();
163         JSString* newString = new (NotNull, allocateCell<JSString>(vm.heap)) JSString(vm, WTFMove(value));
164         newString->finishCreation(vm, length, cost);
165         return newString;
166     }
167     static JSString* createHasOtherOwner(VM& vm, Ref<StringImpl>&& value)
168     {
169         unsigned length = value->length();
170         JSString* newString = new (NotNull, allocateCell<JSString>(vm.heap)) JSString(vm, WTFMove(value));
171         newString->finishCreation(vm, length);
172         return newString;
173     }
174
175 protected:
176     void finishCreation(VM& vm)
177     {
178         Base::finishCreation(vm);
179     }
180
181 public:
182     ~JSString();
183
184     Identifier toIdentifier(ExecState*) const;
185     AtomicString toAtomicString(ExecState*) const;
186     RefPtr<AtomicStringImpl> toExistingAtomicString(ExecState*) const;
187
188     StringViewWithUnderlyingString viewWithUnderlyingString(ExecState*) const;
189
190     inline bool equal(ExecState*, JSString* other) const;
191     const String& value(ExecState*) const;
192     inline const String& tryGetValue(bool allocationAllowed = true) const;
193     const StringImpl* tryGetValueImpl() const;
194     ALWAYS_INLINE unsigned length() const;
195
196     JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const;
197     bool toBoolean() const { return !!length(); }
198     bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const;
199     JSObject* toObject(ExecState*, JSGlobalObject*) const;
200     double toNumber(ExecState*) const;
201
202     bool getStringPropertySlot(ExecState*, PropertyName, PropertySlot&);
203     bool getStringPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
204     bool getStringPropertyDescriptor(ExecState*, PropertyName, PropertyDescriptor&);
205
206     bool canGetIndex(unsigned i) { return i < length(); }
207     JSString* getIndex(ExecState*, unsigned);
208
209     static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
210
211     static ptrdiff_t offsetOfValue() { return OBJECT_OFFSETOF(JSString, m_fiber); }
212
213     DECLARE_EXPORT_INFO;
214
215     static void dumpToStream(const JSCell*, PrintStream&);
216     static size_t estimatedSize(JSCell*, VM&);
217     static void visitChildren(JSCell*, SlotVisitor&);
218
219     ALWAYS_INLINE bool isRope() const
220     {
221         return m_fiber & isRopeInPointer;
222     }
223
224     bool is8Bit() const;
225
226 protected:
227     friend class JSValue;
228
229     JS_EXPORT_PRIVATE bool equalSlowCase(ExecState*, JSString* other) const;
230     bool isSubstring() const;
231
232     mutable uintptr_t m_fiber;
233
234 private:
235     friend class LLIntOffsetsExtractor;
236
237     static JSValue toThis(JSCell*, ExecState*, ECMAMode);
238
239     StringView unsafeView(ExecState*) const;
240
241     friend JSString* jsString(VM*, const String&);
242     friend JSString* jsString(ExecState*, JSString*, JSString*);
243     friend JSString* jsString(ExecState*, const String&, JSString*);
244     friend JSString* jsString(ExecState*, JSString*, const String&);
245     friend JSString* jsString(ExecState*, const String&, const String&);
246     friend JSString* jsString(ExecState*, JSString*, JSString*, JSString*);
247     friend JSString* jsString(ExecState*, const String&, const String&, const String&);
248     friend JSString* jsSingleCharacterString(VM*, UChar);
249     friend JSString* jsNontrivialString(VM*, const String&);
250     friend JSString* jsNontrivialString(VM*, String&&);
251     friend JSString* jsSubstring(VM*, const String&, unsigned, unsigned);
252     friend JSString* jsSubstring(VM&, ExecState*, JSString*, unsigned, unsigned);
253     friend JSString* jsSubstringOfResolved(VM&, GCDeferralContext*, JSString*, unsigned, unsigned);
254     friend JSString* jsOwnedString(VM*, const String&);
255 };
256
257 // NOTE: This class cannot override JSString's destructor. JSString's destructor is called directly
258 // from JSStringSubspace::
259 class JSRopeString final : public JSString {
260     friend class JSString;
261 public:
262     // We use lower 3bits of fiber0 for flags. These bits are usable due to alignment, and it is OK even in 32bit architecture.
263     static constexpr uintptr_t is8BitInPointer = static_cast<uintptr_t>(StringImpl::flagIs8Bit());
264     static constexpr uintptr_t isSubstringInPointer = 0x2;
265     static_assert(is8BitInPointer == 0b100, "");
266     static_assert(isSubstringInPointer == 0b010, "");
267     static_assert(isRopeInPointer == 0b001, "");
268     static constexpr uintptr_t stringMask = ~(isRopeInPointer | is8BitInPointer | isSubstringInPointer);
269 #if CPU(ADDRESS64)
270     static_assert(sizeof(uintptr_t) == sizeof(uint64_t), "");
271     class CompactFibers {
272     public:
273         JSString* fiber1() const
274         {
275             return bitwise_cast<JSString*>(static_cast<uintptr_t>(m_fiber1Lower) | (static_cast<uintptr_t>(m_fiber1Upper) << 32));
276         }
277
278         void initializeFiber1(JSString* fiber)
279         {
280             uintptr_t pointer = bitwise_cast<uintptr_t>(fiber);
281             m_fiber1Lower = static_cast<uint32_t>(pointer);
282             m_fiber1Upper = static_cast<uint16_t>(pointer >> 32);
283         }
284
285         JSString* fiber2() const
286         {
287             return bitwise_cast<JSString*>(static_cast<uintptr_t>(m_fiber2Lower) | (static_cast<uintptr_t>(m_fiber2Upper) << 16));
288         }
289         void initializeFiber2(JSString* fiber)
290         {
291             uintptr_t pointer = bitwise_cast<uintptr_t>(fiber);
292             m_fiber2Lower = static_cast<uint16_t>(pointer);
293             m_fiber2Upper = static_cast<uint32_t>(pointer >> 16);
294         }
295
296         unsigned length() const { return m_length; }
297         void initializeLength(unsigned length)
298         {
299             m_length = length;
300         }
301
302         static ptrdiff_t offsetOfLength() { return OBJECT_OFFSETOF(CompactFibers, m_length); }
303         static ptrdiff_t offsetOfFiber1() { return OBJECT_OFFSETOF(CompactFibers, m_length); }
304         static ptrdiff_t offsetOfFiber2() { return OBJECT_OFFSETOF(CompactFibers, m_fiber1Upper); }
305
306     private:
307         friend class LLIntOffsetsExtractor;
308
309         uint32_t m_length { 0 };
310         uint32_t m_fiber1Lower { 0 };
311         uint16_t m_fiber1Upper { 0 };
312         uint16_t m_fiber2Lower { 0 };
313         uint32_t m_fiber2Upper { 0 };
314     };
315     static_assert(sizeof(CompactFibers) == sizeof(void*) * 2, "");
316 #else
317     class CompactFibers {
318     public:
319         JSString* fiber1() const
320         {
321             return m_fiber1;
322         }
323         void initializeFiber1(JSString* fiber)
324         {
325             m_fiber1 = fiber;
326         }
327
328         JSString* fiber2() const
329         {
330             return m_fiber2;
331         }
332         void initializeFiber2(JSString* fiber)
333         {
334             m_fiber2 = fiber;
335         }
336
337         unsigned length() const { return m_length; }
338         void initializeLength(unsigned length)
339         {
340             m_length = length;
341         }
342
343         static ptrdiff_t offsetOfLength() { return OBJECT_OFFSETOF(CompactFibers, m_length); }
344         static ptrdiff_t offsetOfFiber1() { return OBJECT_OFFSETOF(CompactFibers, m_fiber1); }
345         static ptrdiff_t offsetOfFiber2() { return OBJECT_OFFSETOF(CompactFibers, m_fiber2); }
346
347     private:
348         friend class LLIntOffsetsExtractor;
349
350         uint32_t m_length { 0 };
351         JSString* m_fiber1 { nullptr };
352         JSString* m_fiber2 { nullptr };
353     };
354 #endif
355
356     template <class OverflowHandler = CrashOnOverflow>
357     class RopeBuilder : public OverflowHandler {
358         WTF_FORBID_HEAP_ALLOCATION;
359     public:
360         RopeBuilder(VM& vm)
361             : m_vm(vm)
362         {
363         }
364
365         bool append(JSString* jsString)
366         {
367             if (UNLIKELY(this->hasOverflowed()))
368                 return false;
369             if (!jsString->length())
370                 return true;
371             if (m_strings.size() == JSRopeString::s_maxInternalRopeLength)
372                 expand();
373
374             static_assert(JSString::MaxLength == std::numeric_limits<int32_t>::max(), "");
375             auto sum = checkedSum<int32_t>(m_length, jsString->length());
376             if (sum.hasOverflowed()) {
377                 this->overflowed();
378                 return false;
379             }
380             ASSERT(static_cast<unsigned>(sum.unsafeGet()) <= MaxLength);
381             m_strings.append(jsString);
382             m_length = static_cast<unsigned>(sum.unsafeGet());
383             return true;
384         }
385
386         JSString* release()
387         {
388             RELEASE_ASSERT(!this->hasOverflowed());
389             JSString* result = nullptr;
390             switch (m_strings.size()) {
391             case 0: {
392                 ASSERT(!m_length);
393                 result = jsEmptyString(&m_vm);
394                 break;
395             }
396             case 1: {
397                 result = asString(m_strings.at(0));
398                 break;
399             }
400             case 2: {
401                 result = JSRopeString::create(m_vm, asString(m_strings.at(0)), asString(m_strings.at(1)));
402                 break;
403             }
404             case 3: {
405                 result = JSRopeString::create(m_vm, asString(m_strings.at(0)), asString(m_strings.at(1)), asString(m_strings.at(2)));
406                 break;
407             }
408             default:
409                 ASSERT_NOT_REACHED();
410                 break;
411             }
412             ASSERT(result->length() == m_length);
413             m_strings.clear();
414             m_length = 0;
415             return result;
416         }
417
418         unsigned length() const
419         {
420             ASSERT(!this->hasOverflowed());
421             return m_length;
422         }
423
424     private:
425         void expand();
426
427         VM& m_vm;
428         MarkedArgumentBuffer m_strings;
429         unsigned m_length { 0 };
430     };
431
432     inline unsigned length() const
433     {
434         return m_compactFibers.length();
435     }
436
437 private:
438     friend class LLIntOffsetsExtractor;
439
440     void convertToNonRope(String&&) const;
441
442     void initializeIs8Bit(bool flag) const
443     {
444         if (flag)
445             m_fiber |= is8BitInPointer;
446         else
447             m_fiber &= ~is8BitInPointer;
448     }
449
450     void initializeIsSubstring(bool flag) const
451     {
452         if (flag)
453             m_fiber |= isSubstringInPointer;
454         else
455             m_fiber &= ~isSubstringInPointer;
456     }
457
458     ALWAYS_INLINE void initializeLength(unsigned length)
459     {
460         ASSERT(length <= MaxLength);
461         m_compactFibers.initializeLength(length);
462     }
463
464     JSRopeString(VM& vm)
465         : JSString(vm)
466     {
467         initializeIsSubstring(false);
468         initializeLength(0);
469         initializeIs8Bit(true);
470         initializeFiber0(nullptr);
471         initializeFiber1(nullptr);
472         initializeFiber2(nullptr);
473     }
474
475     JSRopeString(VM& vm, JSString* s1, JSString* s2)
476         : JSString(vm)
477     {
478         ASSERT(!sumOverflows<int32_t>(s1->length(), s2->length()));
479         initializeIsSubstring(false);
480         initializeLength(s1->length() + s2->length());
481         initializeIs8Bit(s1->is8Bit() && s2->is8Bit());
482         initializeFiber0(s1);
483         initializeFiber1(s2);
484         initializeFiber2(nullptr);
485         ASSERT((s1->length() + s2->length()) == length());
486     }
487
488     JSRopeString(VM& vm, JSString* s1, JSString* s2, JSString* s3)
489         : JSString(vm)
490     {
491         ASSERT(!sumOverflows<int32_t>(s1->length(), s2->length(), s3->length()));
492         initializeIsSubstring(false);
493         initializeLength(s1->length() + s2->length() + s3->length());
494         initializeIs8Bit(s1->is8Bit() && s2->is8Bit() &&  s3->is8Bit());
495         initializeFiber0(s1);
496         initializeFiber1(s2);
497         initializeFiber2(s3);
498         ASSERT((s1->length() + s2->length() + s3->length()) == length());
499     }
500
501     JSRopeString(VM& vm, JSString* base, unsigned offset, unsigned length)
502         : JSString(vm)
503     {
504         RELEASE_ASSERT(!sumOverflows<int32_t>(offset, length));
505         RELEASE_ASSERT(offset + length <= base->length());
506         initializeIsSubstring(true);
507         initializeLength(length);
508         initializeIs8Bit(base->is8Bit());
509         initializeSubstringBase(base);
510         initializeSubstringOffset(offset);
511         ASSERT(length == this->length());
512         ASSERT(!base->isRope());
513     }
514
515     ALWAYS_INLINE void finishCreationSubstringOfResolved(VM& vm)
516     {
517         Base::finishCreation(vm);
518     }
519
520 public:
521     static ptrdiff_t offsetOfLength() { return OBJECT_OFFSETOF(JSRopeString, m_compactFibers) + CompactFibers::offsetOfLength(); } // 32byte width.
522     static ptrdiff_t offsetOfFlags() { return offsetOfValue(); }
523     static ptrdiff_t offsetOfFiber0() { return offsetOfValue(); }
524     static ptrdiff_t offsetOfFiber1() { return OBJECT_OFFSETOF(JSRopeString, m_compactFibers) + CompactFibers::offsetOfFiber1(); }
525     static ptrdiff_t offsetOfFiber2() { return OBJECT_OFFSETOF(JSRopeString, m_compactFibers) + CompactFibers::offsetOfFiber2(); }
526
527     static constexpr unsigned s_maxInternalRopeLength = 3;
528
529     // This JSRopeString is only used to simulate half-baked JSRopeString in DFG and FTL MakeRope. If OSR exit happens in
530     // the middle of MakeRope due to string length overflow, we have half-baked JSRopeString which is the same to the result
531     // of this function. This half-baked JSRopeString will not be exposed to users, but still collectors can see it due to
532     // the conservative stack scan. This JSRopeString is used to test the collector with such a half-baked JSRopeString.
533     // Because this JSRopeString breaks the JSString's invariant (only one singleton JSString can be zero length), almost all the
534     // operations in JS fail to handle this string correctly.
535     static JSRopeString* createNullForTesting(VM& vm)
536     {
537         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
538         newString->finishCreation(vm);
539         ASSERT(!newString->length());
540         ASSERT(newString->isRope());
541         ASSERT(newString->fiber0() == nullptr);
542         return newString;
543     }
544
545 private:
546     static JSRopeString* create(VM& vm, JSString* s1, JSString* s2)
547     {
548         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm, s1, s2);
549         newString->finishCreation(vm);
550         ASSERT(newString->length());
551         ASSERT(newString->isRope());
552         return newString;
553     }
554     static JSRopeString* create(VM& vm, JSString* s1, JSString* s2, JSString* s3)
555     {
556         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm, s1, s2, s3);
557         newString->finishCreation(vm);
558         ASSERT(newString->length());
559         ASSERT(newString->isRope());
560         return newString;
561     }
562
563     ALWAYS_INLINE static JSRopeString* createSubstringOfResolved(VM& vm, GCDeferralContext* deferralContext, JSString* base, unsigned offset, unsigned length)
564     {
565         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap, deferralContext)) JSRopeString(vm, base, offset, length);
566         newString->finishCreationSubstringOfResolved(vm);
567         ASSERT(newString->length());
568         ASSERT(newString->isRope());
569         return newString;
570     }
571
572     friend JSValue jsStringFromRegisterArray(ExecState*, Register*, unsigned);
573     friend JSValue jsStringFromArguments(ExecState*, JSValue);
574
575     // If nullOrExecForOOM is null, resolveRope() will be do nothing in the event of an OOM error.
576     // The rope value will remain a null string in that case.
577     JS_EXPORT_PRIVATE const String& resolveRope(ExecState* nullOrExecForOOM) const;
578     template<typename Function> const String& resolveRopeWithFunction(ExecState* nullOrExecForOOM, Function&&) const;
579     JS_EXPORT_PRIVATE AtomicString resolveRopeToAtomicString(ExecState*) const;
580     JS_EXPORT_PRIVATE RefPtr<AtomicStringImpl> resolveRopeToExistingAtomicString(ExecState*) const;
581     void resolveRopeSlowCase8(LChar*) const;
582     void resolveRopeSlowCase(UChar*) const;
583     void outOfMemory(ExecState* nullOrExecForOOM) const;
584     void resolveRopeInternal8(LChar*) const;
585     void resolveRopeInternal8NoSubstring(LChar*) const;
586     void resolveRopeInternal16(UChar*) const;
587     void resolveRopeInternal16NoSubstring(UChar*) const;
588     StringView unsafeView(ExecState*) const;
589     StringViewWithUnderlyingString viewWithUnderlyingString(ExecState*) const;
590
591     JSString* fiber0() const
592     {
593         return bitwise_cast<JSString*>(m_fiber & stringMask);
594     }
595
596     JSString* fiber1() const
597     {
598         return m_compactFibers.fiber1();
599     }
600
601     JSString* fiber2() const
602     {
603         return m_compactFibers.fiber2();
604     }
605
606     JSString* fiber(unsigned i) const
607     {
608         ASSERT(!isSubstring());
609         ASSERT(i < s_maxInternalRopeLength);
610         switch (i) {
611         case 0:
612             return fiber0();
613         case 1:
614             return fiber1();
615         case 2:
616             return fiber2();
617         }
618         ASSERT_NOT_REACHED();
619         return nullptr;
620     }
621
622     void initializeFiber0(JSString* fiber)
623     {
624         uintptr_t pointer = bitwise_cast<uintptr_t>(fiber);
625         ASSERT(!(pointer & ~stringMask));
626         m_fiber = (pointer | (m_fiber & ~stringMask));
627     }
628
629     void initializeFiber1(JSString* fiber)
630     {
631         m_compactFibers.initializeFiber1(fiber);
632     }
633
634     void initializeFiber2(JSString* fiber)
635     {
636         m_compactFibers.initializeFiber2(fiber);
637     }
638
639     void initializeSubstringBase(JSString* fiber)
640     {
641         initializeFiber1(fiber);
642     }
643
644     JSString* substringBase() const { return fiber1(); }
645
646     void initializeSubstringOffset(unsigned offset)
647     {
648         m_compactFibers.initializeFiber2(bitwise_cast<JSString*>(static_cast<uintptr_t>(offset)));
649     }
650
651     unsigned substringOffset() const
652     {
653         return static_cast<unsigned>(bitwise_cast<uintptr_t>(fiber2()));
654     }
655
656     static_assert(s_maxInternalRopeLength >= 2, "");
657     mutable CompactFibers m_compactFibers;
658
659     friend JSString* jsString(ExecState*, JSString*, JSString*);
660     friend JSString* jsString(ExecState*, const String&, JSString*);
661     friend JSString* jsString(ExecState*, JSString*, const String&);
662     friend JSString* jsString(ExecState*, const String&, const String&);
663     friend JSString* jsString(ExecState*, JSString*, JSString*, JSString*);
664     friend JSString* jsString(ExecState*, const String&, const String&, const String&);
665     friend JSString* jsSubstringOfResolved(VM&, GCDeferralContext*, JSString*, unsigned, unsigned);
666     friend JSString* jsSubstring(VM&, ExecState*, JSString*, unsigned, unsigned);
667 };
668
669 JS_EXPORT_PRIVATE JSString* jsStringWithCacheSlowCase(VM&, StringImpl&);
670
671 // JSString::is8Bit is safe to be called concurrently. Concurrent threads can access is8Bit even if the main thread
672 // is in the middle of converting JSRopeString to JSString.
673 ALWAYS_INLINE bool JSString::is8Bit() const
674 {
675     uintptr_t pointer = m_fiber;
676     if (pointer & isRopeInPointer) {
677         // Do not load m_fiber twice. We should use the information in pointer.
678         // Otherwise, JSRopeString may be converted to JSString between the first and second accesses.
679         return pointer & JSRopeString::is8BitInPointer;
680     }
681     return bitwise_cast<StringImpl*>(pointer)->is8Bit();
682 }
683
684 // JSString::length is safe to be called concurrently. Concurrent threads can access length even if the main thread
685 // is in the middle of converting JSRopeString to JSString. This is OK because we never override the length bits
686 // when we resolve a JSRopeString.
687 ALWAYS_INLINE unsigned JSString::length() const
688 {
689     uintptr_t pointer = m_fiber;
690     if (pointer & isRopeInPointer)
691         return jsCast<const JSRopeString*>(this)->length();
692     return bitwise_cast<StringImpl*>(pointer)->length();
693 }
694
695 inline const StringImpl* JSString::tryGetValueImpl() const
696 {
697     uintptr_t pointer = m_fiber;
698     if (pointer & isRopeInPointer)
699         return nullptr;
700     return bitwise_cast<StringImpl*>(pointer);
701 }
702
703 inline JSString* asString(JSValue value)
704 {
705     ASSERT(value.asCell()->isString());
706     return jsCast<JSString*>(value.asCell());
707 }
708
709 // This MUST NOT GC.
710 inline JSString* jsEmptyString(VM* vm)
711 {
712     return vm->smallStrings.emptyString();
713 }
714
715 ALWAYS_INLINE JSString* jsSingleCharacterString(VM* vm, UChar c)
716 {
717     if (validateDFGDoesGC)
718         RELEASE_ASSERT(vm->heap.expectDoesGC());
719     if (c <= maxSingleCharacterString)
720         return vm->smallStrings.singleCharacterString(c);
721     return JSString::create(*vm, StringImpl::create(&c, 1));
722 }
723
724 inline JSString* jsNontrivialString(VM* vm, const String& s)
725 {
726     ASSERT(s.length() > 1);
727     return JSString::create(*vm, *s.impl());
728 }
729
730 inline JSString* jsNontrivialString(VM* vm, String&& s)
731 {
732     ASSERT(s.length() > 1);
733     return JSString::create(*vm, s.releaseImpl().releaseNonNull());
734 }
735
736 ALWAYS_INLINE Identifier JSString::toIdentifier(ExecState* exec) const
737 {
738     return Identifier::fromString(exec, toAtomicString(exec));
739 }
740
741 ALWAYS_INLINE AtomicString JSString::toAtomicString(ExecState* exec) const
742 {
743     if (validateDFGDoesGC)
744         RELEASE_ASSERT(vm()->heap.expectDoesGC());
745     if (isRope())
746         return static_cast<const JSRopeString*>(this)->resolveRopeToAtomicString(exec);
747     return AtomicString(valueInternal());
748 }
749
750 ALWAYS_INLINE RefPtr<AtomicStringImpl> JSString::toExistingAtomicString(ExecState* exec) const
751 {
752     if (validateDFGDoesGC)
753         RELEASE_ASSERT(vm()->heap.expectDoesGC());
754     if (isRope())
755         return static_cast<const JSRopeString*>(this)->resolveRopeToExistingAtomicString(exec);
756     if (valueInternal().impl()->isAtomic())
757         return static_cast<AtomicStringImpl*>(valueInternal().impl());
758     return AtomicStringImpl::lookUp(valueInternal().impl());
759 }
760
761 inline const String& JSString::value(ExecState* exec) const
762 {
763     if (validateDFGDoesGC)
764         RELEASE_ASSERT(vm()->heap.expectDoesGC());
765     if (isRope())
766         return static_cast<const JSRopeString*>(this)->resolveRope(exec);
767     return valueInternal();
768 }
769
770 inline const String& JSString::tryGetValue(bool allocationAllowed) const
771 {
772     if (allocationAllowed) {
773         if (validateDFGDoesGC)
774             RELEASE_ASSERT(vm()->heap.expectDoesGC());
775         if (isRope()) {
776             // Pass nullptr for the ExecState so that resolveRope does not throw in the event of an OOM error.
777             return static_cast<const JSRopeString*>(this)->resolveRope(nullptr);
778         }
779     } else
780         RELEASE_ASSERT(!isRope());
781     return valueInternal();
782 }
783
784 inline JSString* JSString::getIndex(ExecState* exec, unsigned i)
785 {
786     VM& vm = exec->vm();
787     auto scope = DECLARE_THROW_SCOPE(vm);
788     ASSERT(canGetIndex(i));
789     StringView view = unsafeView(exec);
790     RETURN_IF_EXCEPTION(scope, nullptr);
791     return jsSingleCharacterString(exec, view[i]);
792 }
793
794 inline JSString* jsString(VM* vm, const String& s)
795 {
796     int size = s.length();
797     if (!size)
798         return vm->smallStrings.emptyString();
799     if (size == 1) {
800         UChar c = s.characterAt(0);
801         if (c <= maxSingleCharacterString)
802             return vm->smallStrings.singleCharacterString(c);
803     }
804     return JSString::create(*vm, *s.impl());
805 }
806
807 inline JSString* jsSubstring(VM& vm, ExecState* exec, JSString* base, unsigned offset, unsigned length)
808 {
809     auto scope = DECLARE_THROW_SCOPE(vm);
810
811     ASSERT(offset <= base->length());
812     ASSERT(length <= base->length());
813     ASSERT(offset + length <= base->length());
814     if (!length)
815         return vm.smallStrings.emptyString();
816     if (!offset && length == base->length())
817         return base;
818
819     // For now, let's not allow substrings with a rope base.
820     // Resolve non-substring rope bases so we don't have to deal with it.
821     // FIXME: Evaluate if this would be worth adding more branches.
822     if (base->isSubstring()) {
823         JSRopeString* baseRope = jsCast<JSRopeString*>(base);
824         base = baseRope->substringBase();
825         offset = baseRope->substringOffset() + offset;
826         ASSERT(!base->isRope());
827     } else if (base->isRope()) {
828         jsCast<JSRopeString*>(base)->resolveRope(exec);
829         RETURN_IF_EXCEPTION(scope, nullptr);
830     }
831     return jsSubstringOfResolved(vm, nullptr, base, offset, length);
832 }
833
834 inline JSString* jsSubstringOfResolved(VM& vm, GCDeferralContext* deferralContext, JSString* s, unsigned offset, unsigned length)
835 {
836     ASSERT(offset <= s->length());
837     ASSERT(length <= s->length());
838     ASSERT(offset + length <= s->length());
839     ASSERT(!s->isRope());
840     if (!length)
841         return vm.smallStrings.emptyString();
842     if (!offset && length == s->length())
843         return s;
844     if (length == 1) {
845         auto& base = s->valueInternal();
846         UChar character = base.characterAt(offset);
847         if (character <= maxSingleCharacterString)
848             return vm.smallStrings.singleCharacterString(character);
849     }
850     return JSRopeString::createSubstringOfResolved(vm, deferralContext, s, offset, length);
851 }
852
853 inline JSString* jsSubstringOfResolved(VM& vm, JSString* s, unsigned offset, unsigned length)
854 {
855     return jsSubstringOfResolved(vm, nullptr, s, offset, length);
856 }
857
858 inline JSString* jsSubstring(ExecState* exec, JSString* s, unsigned offset, unsigned length)
859 {
860     return jsSubstring(exec->vm(), exec, s, offset, length);
861 }
862
863 inline JSString* jsSubstring(VM* vm, const String& s, unsigned offset, unsigned length)
864 {
865     ASSERT(offset <= s.length());
866     ASSERT(length <= s.length());
867     ASSERT(offset + length <= s.length());
868     if (!length)
869         return vm->smallStrings.emptyString();
870     if (length == 1) {
871         UChar c = s.characterAt(offset);
872         if (c <= maxSingleCharacterString)
873             return vm->smallStrings.singleCharacterString(c);
874     }
875     auto impl = StringImpl::createSubstringSharingImpl(*s.impl(), offset, length);
876     if (impl->isSubString())
877         return JSString::createHasOtherOwner(*vm, WTFMove(impl));
878     return JSString::create(*vm, WTFMove(impl));
879 }
880
881 inline JSString* jsOwnedString(VM* vm, const String& s)
882 {
883     int size = s.length();
884     if (!size)
885         return vm->smallStrings.emptyString();
886     if (size == 1) {
887         UChar c = s.characterAt(0);
888         if (c <= maxSingleCharacterString)
889             return vm->smallStrings.singleCharacterString(c);
890     }
891     return JSString::createHasOtherOwner(*vm, *s.impl());
892 }
893
894 inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->vm()); }
895 inline JSString* jsString(ExecState* exec, const String& s) { return jsString(&exec->vm(), s); }
896 inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->vm(), c); }
897 inline JSString* jsNontrivialString(ExecState* exec, const String& s) { return jsNontrivialString(&exec->vm(), s); }
898 inline JSString* jsNontrivialString(ExecState* exec, String&& s) { return jsNontrivialString(&exec->vm(), WTFMove(s)); }
899 inline JSString* jsOwnedString(ExecState* exec, const String& s) { return jsOwnedString(&exec->vm(), s); }
900
901 ALWAYS_INLINE JSString* jsStringWithCache(ExecState* exec, const String& s)
902 {
903     VM& vm = exec->vm();
904     StringImpl* stringImpl = s.impl();
905     if (!stringImpl || !stringImpl->length())
906         return jsEmptyString(&vm);
907
908     if (stringImpl->length() == 1) {
909         UChar singleCharacter = (*stringImpl)[0u];
910         if (singleCharacter <= maxSingleCharacterString)
911             return vm.smallStrings.singleCharacterString(static_cast<unsigned char>(singleCharacter));
912     }
913
914     if (JSString* lastCachedString = vm.lastCachedString.get()) {
915         if (lastCachedString->tryGetValueImpl() == stringImpl)
916             return lastCachedString;
917     }
918
919     return jsStringWithCacheSlowCase(vm, *stringImpl);
920 }
921
922 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
923 {
924     VM& vm = exec->vm();
925     auto scope = DECLARE_THROW_SCOPE(vm);
926
927     if (propertyName == vm.propertyNames->length) {
928         slot.setValue(this, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly, jsNumber(length()));
929         return true;
930     }
931
932     Optional<uint32_t> index = parseIndex(propertyName);
933     if (index && index.value() < length()) {
934         JSValue value = getIndex(exec, index.value());
935         RETURN_IF_EXCEPTION(scope, false);
936         slot.setValue(this, PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly, value);
937         return true;
938     }
939
940     return false;
941 }
942
943 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
944 {
945     VM& vm = exec->vm();
946     auto scope = DECLARE_THROW_SCOPE(vm);
947
948     if (propertyName < length()) {
949         JSValue value = getIndex(exec, propertyName);
950         RETURN_IF_EXCEPTION(scope, false);
951         slot.setValue(this, PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly, value);
952         return true;
953     }
954
955     return false;
956 }
957
958 inline bool isJSString(JSCell* cell)
959 {
960     return cell->type() == StringType;
961 }
962
963 inline bool isJSString(JSValue v)
964 {
965     return v.isCell() && isJSString(v.asCell());
966 }
967
968 ALWAYS_INLINE StringView JSRopeString::unsafeView(ExecState* exec) const
969 {
970     if (validateDFGDoesGC)
971         RELEASE_ASSERT(vm()->heap.expectDoesGC());
972     if (isSubstring()) {
973         auto& base = substringBase()->valueInternal();
974         if (base.is8Bit())
975             return StringView(base.characters8() + substringOffset(), length());
976         return StringView(base.characters16() + substringOffset(), length());
977     }
978     return resolveRope(exec);
979 }
980
981 ALWAYS_INLINE StringViewWithUnderlyingString JSRopeString::viewWithUnderlyingString(ExecState* exec) const
982 {
983     if (validateDFGDoesGC)
984         RELEASE_ASSERT(vm()->heap.expectDoesGC());
985     if (isSubstring()) {
986         auto& base = substringBase()->valueInternal();
987         if (base.is8Bit())
988             return { { base.characters8() + substringOffset(), length() }, base };
989         return { { base.characters16() + substringOffset(), length() }, base };
990     }
991     auto& string = resolveRope(exec);
992     return { string, string };
993 }
994
995 ALWAYS_INLINE StringView JSString::unsafeView(ExecState* exec) const
996 {
997     if (validateDFGDoesGC)
998         RELEASE_ASSERT(vm()->heap.expectDoesGC());
999     if (isRope())
1000         return static_cast<const JSRopeString*>(this)->unsafeView(exec);
1001     return valueInternal();
1002 }
1003
1004 ALWAYS_INLINE StringViewWithUnderlyingString JSString::viewWithUnderlyingString(ExecState* exec) const
1005 {
1006     if (isRope())
1007         return static_cast<const JSRopeString&>(*this).viewWithUnderlyingString(exec);
1008     return { valueInternal(), valueInternal() };
1009 }
1010
1011 inline bool JSString::isSubstring() const
1012 {
1013     return m_fiber & JSRopeString::isSubstringInPointer;
1014 }
1015
1016 // --- JSValue inlines ----------------------------
1017
1018 inline bool JSValue::toBoolean(ExecState* exec) const
1019 {
1020     if (isInt32())
1021         return asInt32();
1022     if (isDouble())
1023         return asDouble() > 0.0 || asDouble() < 0.0; // false for NaN
1024     if (isCell())
1025         return asCell()->toBoolean(exec);
1026     return isTrue(); // false, null, and undefined all convert to false.
1027 }
1028
1029 inline JSString* JSValue::toString(ExecState* exec) const
1030 {
1031     if (isString())
1032         return asString(asCell());
1033     bool returnEmptyStringOnError = true;
1034     return toStringSlowCase(exec, returnEmptyStringOnError);
1035 }
1036
1037 inline JSString* JSValue::toStringOrNull(ExecState* exec) const
1038 {
1039     if (isString())
1040         return asString(asCell());
1041     bool returnEmptyStringOnError = false;
1042     return toStringSlowCase(exec, returnEmptyStringOnError);
1043 }
1044
1045 inline String JSValue::toWTFString(ExecState* exec) const
1046 {
1047     if (isString())
1048         return static_cast<JSString*>(asCell())->value(exec);
1049     return toWTFStringSlowCase(exec);
1050 }
1051
1052 } // namespace JSC