1/*
2 * Copyright (C) 1999-2001 Harri Porten ([email protected])
3 * Copyright (C) 2001 Peter Kelly ([email protected])
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
38namespace JSC {
39
40class JSString;
41class JSRopeString;
42class LLIntOffsetsExtractor;
43
44JSString* jsEmptyString(VM*);
45JSString* jsEmptyString(ExecState*);
46JSString* jsString(VM*, const String&); // returns empty string if passed null string
47JSString* jsString(ExecState*, const String&); // returns empty string if passed null string
48
49JSString* jsSingleCharacterString(VM*, UChar);
50JSString* jsSingleCharacterString(ExecState*, UChar);
51JSString* 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.
55JSString* jsNontrivialString(VM*, const String&);
56JSString* jsNontrivialString(ExecState*, const String&);
57JSString* 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
62JSString* jsOwnedString(VM*, const String&);
63JSString* jsOwnedString(ExecState*, const String&);
64
65bool isJSString(JSCell*);
66bool isJSString(JSValue);
67JSString* 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
85class JSString : public JSCell {
86public:
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
117private:
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
175protected:
176 void finishCreation(VM& vm)
177 {
178 Base::finishCreation(vm);
179 }
180
181public:
182 ~JSString();
183
184 Identifier toIdentifier(ExecState*) const;
185 AtomString toAtomString(ExecState*) const;
186 RefPtr<AtomStringImpl> toExistingAtomString(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
226protected:
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
234private:
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::
259class JSRopeString final : public JSString {
260 friend class JSString;
261public:
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
437private:
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
520public:
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
545private:
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 AtomString resolveRopeToAtomString(ExecState*) const;
580 JS_EXPORT_PRIVATE RefPtr<AtomStringImpl> resolveRopeToExistingAtomString(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
669JS_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.
673ALWAYS_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.
687ALWAYS_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
695inline 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
703inline JSString* asString(JSValue value)
704{
705 ASSERT(value.asCell()->isString());
706 return jsCast<JSString*>(value.asCell());
707}
708
709// This MUST NOT GC.
710inline JSString* jsEmptyString(VM* vm)
711{
712 return vm->smallStrings.emptyString();
713}
714
715ALWAYS_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
724inline JSString* jsNontrivialString(VM* vm, const String& s)
725{
726 ASSERT(s.length() > 1);
727 return JSString::create(*vm, *s.impl());
728}
729
730inline JSString* jsNontrivialString(VM* vm, String&& s)
731{
732 ASSERT(s.length() > 1);
733 return JSString::create(*vm, s.releaseImpl().releaseNonNull());
734}
735
736ALWAYS_INLINE Identifier JSString::toIdentifier(ExecState* exec) const
737{
738 return Identifier::fromString(exec, toAtomString(exec));
739}
740
741ALWAYS_INLINE AtomString JSString::toAtomString(ExecState* exec) const
742{
743 if (validateDFGDoesGC)
744 RELEASE_ASSERT(vm()->heap.expectDoesGC());
745 if (isRope())
746 return static_cast<const JSRopeString*>(this)->resolveRopeToAtomString(exec);
747 return AtomString(valueInternal());
748}
749
750ALWAYS_INLINE RefPtr<AtomStringImpl> JSString::toExistingAtomString(ExecState* exec) const
751{
752 if (validateDFGDoesGC)
753 RELEASE_ASSERT(vm()->heap.expectDoesGC());
754 if (isRope())
755 return static_cast<const JSRopeString*>(this)->resolveRopeToExistingAtomString(exec);
756 if (valueInternal().impl()->isAtom())
757 return static_cast<AtomStringImpl*>(valueInternal().impl());
758 return AtomStringImpl::lookUp(valueInternal().impl());
759}
760
761inline 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
770inline 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
784inline 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
794inline 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
807inline 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
834inline 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
853inline JSString* jsSubstringOfResolved(VM& vm, JSString* s, unsigned offset, unsigned length)
854{
855 return jsSubstringOfResolved(vm, nullptr, s, offset, length);
856}
857
858inline JSString* jsSubstring(ExecState* exec, JSString* s, unsigned offset, unsigned length)
859{
860 return jsSubstring(exec->vm(), exec, s, offset, length);
861}
862
863inline 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
881inline 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
894inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->vm()); }
895inline JSString* jsString(ExecState* exec, const String& s) { return jsString(&exec->vm(), s); }
896inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->vm(), c); }
897inline JSString* jsNontrivialString(ExecState* exec, const String& s) { return jsNontrivialString(&exec->vm(), s); }
898inline JSString* jsNontrivialString(ExecState* exec, String&& s) { return jsNontrivialString(&exec->vm(), WTFMove(s)); }
899inline JSString* jsOwnedString(ExecState* exec, const String& s) { return jsOwnedString(&exec->vm(), s); }
900
901ALWAYS_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
922ALWAYS_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
943ALWAYS_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
958inline bool isJSString(JSCell* cell)
959{
960 return cell->type() == StringType;
961}
962
963inline bool isJSString(JSValue v)
964{
965 return v.isCell() && isJSString(v.asCell());
966}
967
968ALWAYS_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
981ALWAYS_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
995ALWAYS_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
1004ALWAYS_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
1011inline bool JSString::isSubstring() const
1012{
1013 return m_fiber & JSRopeString::isSubstringInPointer;
1014}
1015
1016// --- JSValue inlines ----------------------------
1017
1018inline 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
1029inline 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
1037inline 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
1045inline 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
1053