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