efb3bb933eab51e8b94d20b35f1bbb8331559d71
[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-2018 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 "CallFrame.h"
26 #include "CommonIdentifiers.h"
27 #include "Identifier.h"
28 #include "PropertyDescriptor.h"
29 #include "PropertySlot.h"
30 #include "Structure.h"
31 #include "ThrowScope.h"
32 #include <array>
33 #include <wtf/CheckedArithmetic.h>
34 #include <wtf/text/StringView.h>
35
36 namespace JSC {
37
38 class JSString;
39 class JSRopeString;
40 class LLIntOffsetsExtractor;
41
42 JSString* jsEmptyString(VM*);
43 JSString* jsEmptyString(ExecState*);
44 JSString* jsString(VM*, const String&); // returns empty string if passed null string
45 JSString* jsString(ExecState*, const String&); // returns empty string if passed null string
46
47 JSString* jsSingleCharacterString(VM*, UChar);
48 JSString* jsSingleCharacterString(ExecState*, UChar);
49 JSString* jsSubstring(VM*, const String&, unsigned offset, unsigned length);
50 JSString* jsSubstring(ExecState*, const String&, unsigned offset, unsigned length);
51
52 // Non-trivial strings are two or more characters long.
53 // These functions are faster than just calling jsString.
54 JSString* jsNontrivialString(VM*, const String&);
55 JSString* jsNontrivialString(ExecState*, const String&);
56 JSString* jsNontrivialString(ExecState*, String&&);
57
58 // Should be used for strings that are owned by an object that will
59 // likely outlive the JSValue this makes, such as the parse tree or a
60 // DOM object that contains a String
61 JSString* jsOwnedString(VM*, const String&);
62 JSString* jsOwnedString(ExecState*, const String&);
63
64 JSRopeString* jsStringBuilder(VM*);
65
66 bool isJSString(JSCell*);
67 bool isJSString(JSValue);
68 JSString* asString(JSValue);
69
70 struct StringViewWithUnderlyingString {
71     StringView view;
72     String underlyingString;
73 };
74
75 class JSString : public JSCell {
76 public:
77     friend class JIT;
78     friend class VM;
79     friend class SpecializedThunkJIT;
80     friend class JSRopeString;
81     friend class MarkStack;
82     friend class SlotVisitor;
83
84     typedef JSCell Base;
85     static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | StructureIsImmortal | OverridesToThis;
86
87     static const bool needsDestruction = true;
88     static void destroy(JSCell*);
89     
90     // We specialize the string subspace to get the fastest possible sweep. This wouldn't be
91     // necessary if JSString didn't have a destructor.
92     template<typename>
93     static CompleteSubspace* subspaceFor(VM& vm)
94     {
95         return &vm.stringSpace;
96     }
97     
98     static const unsigned MaxLength = std::numeric_limits<int32_t>::max();
99     
100 private:
101     JSString(VM& vm, Ref<StringImpl>&& value)
102         : JSCell(vm, vm.stringStructure.get())
103         , m_value(WTFMove(value))
104     {
105     }
106
107     JSString(VM& vm)
108         : JSCell(vm, vm.stringStructure.get())
109     {
110     }
111
112     void finishCreation(VM& vm, size_t length)
113     {
114         ASSERT(!m_value.isNull());
115         Base::finishCreation(vm);
116         setLength(length);
117         setIs8Bit(m_value.impl()->is8Bit());
118     }
119
120     void finishCreation(VM& vm, size_t length, size_t cost)
121     {
122         ASSERT(!m_value.isNull());
123         Base::finishCreation(vm);
124         setLength(length);
125         setIs8Bit(m_value.impl()->is8Bit());
126         vm.heap.reportExtraMemoryAllocated(cost);
127     }
128
129 protected:
130     void finishCreation(VM& vm)
131     {
132         Base::finishCreation(vm);
133         setLength(0);
134         setIs8Bit(true);
135     }
136
137 public:
138     static JSString* create(VM& vm, Ref<StringImpl>&& value)
139     {
140         unsigned length = value->length();
141         size_t cost = value->cost();
142         JSString* newString = new (NotNull, allocateCell<JSString>(vm.heap)) JSString(vm, WTFMove(value));
143         newString->finishCreation(vm, length, cost);
144         return newString;
145     }
146     static JSString* createHasOtherOwner(VM& vm, Ref<StringImpl>&& value)
147     {
148         size_t length = value->length();
149         JSString* newString = new (NotNull, allocateCell<JSString>(vm.heap)) JSString(vm, WTFMove(value));
150         newString->finishCreation(vm, length);
151         return newString;
152     }
153
154     Identifier toIdentifier(ExecState*) const;
155     AtomicString toAtomicString(ExecState*) const;
156     RefPtr<AtomicStringImpl> toExistingAtomicString(ExecState*) const;
157
158     StringViewWithUnderlyingString viewWithUnderlyingString(ExecState*) const;
159
160     inline bool equal(ExecState*, JSString* other) const;
161     const String& value(ExecState*) const;
162     inline const String& tryGetValue() const;
163     const StringImpl* tryGetValueImpl() const;
164     ALWAYS_INLINE unsigned length() const { return m_length; }
165
166     JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const;
167     bool toBoolean() const { return !!length(); }
168     bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const;
169     JSObject* toObject(ExecState*, JSGlobalObject*) const;
170     double toNumber(ExecState*) const;
171
172     bool getStringPropertySlot(ExecState*, PropertyName, PropertySlot&);
173     bool getStringPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
174     bool getStringPropertyDescriptor(ExecState*, PropertyName, PropertyDescriptor&);
175
176     bool canGetIndex(unsigned i) { return i < length(); }
177     JSString* getIndex(ExecState*, unsigned);
178
179     static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
180
181     static size_t offsetOfLength() { return OBJECT_OFFSETOF(JSString, m_length); }
182     static size_t offsetOfFlags() { return OBJECT_OFFSETOF(JSString, m_flags); }
183     static size_t offsetOfValue() { return OBJECT_OFFSETOF(JSString, m_value); }
184
185     DECLARE_EXPORT_INFO;
186
187     static void dumpToStream(const JSCell*, PrintStream&);
188     static size_t estimatedSize(JSCell*, VM&);
189     static void visitChildren(JSCell*, SlotVisitor&);
190
191     enum {
192         Is8Bit = 1u
193     };
194
195 protected:
196     friend class JSValue;
197
198     JS_EXPORT_PRIVATE bool equalSlowCase(ExecState*, JSString* other) const;
199     bool isRope() const { return m_value.isNull(); }
200     bool isSubstring() const;
201     bool is8Bit() const { return m_flags & Is8Bit; }
202     void setIs8Bit(bool flag) const
203     {
204         if (flag)
205             m_flags |= Is8Bit;
206         else
207             m_flags &= ~Is8Bit;
208     }
209
210     ALWAYS_INLINE void setLength(unsigned length)
211     {
212         m_length = length;
213     }
214
215 private:
216     // A string is represented either by a String or a rope of fibers.
217     unsigned m_length { 0 };
218     mutable uint16_t m_flags { 0 };
219     // The poison is strategically placed and holds a value such that the first
220     // 64 bits of JSString look like a double JSValue.
221     mutable String m_value;
222
223     friend class LLIntOffsetsExtractor;
224
225     static JSValue toThis(JSCell*, ExecState*, ECMAMode);
226
227     String& string() { ASSERT(!isRope()); return m_value; }
228     StringView unsafeView(ExecState*) const;
229
230     friend JSString* jsString(ExecState*, JSString*, JSString*);
231     friend JSString* jsSubstring(ExecState*, JSString*, unsigned offset, unsigned length);
232 };
233
234 // NOTE: This class cannot override JSString's destructor. JSString's destructor is called directly
235 // from JSStringSubspace::
236 class JSRopeString final : public JSString {
237     friend class JSString;
238
239     friend JSRopeString* jsStringBuilder(VM*);
240
241 public:
242     template <class OverflowHandler = CrashOnOverflow>
243     class RopeBuilder : public OverflowHandler {
244     public:
245         RopeBuilder(VM& vm)
246             : m_vm(vm)
247             , m_jsString(jsStringBuilder(&vm))
248             , m_index(0)
249         {
250         }
251
252         bool append(JSString* jsString)
253         {
254             if (UNLIKELY(this->hasOverflowed()))
255                 return false;
256             if (m_index == JSRopeString::s_maxInternalRopeLength)
257                 expand();
258             if (static_cast<int32_t>(m_jsString->length() + jsString->length()) < 0) {
259                 this->overflowed();
260                 return false;
261             }
262             m_jsString->append(m_vm, m_index++, jsString);
263             return true;
264         }
265
266         JSRopeString* release()
267         {
268             RELEASE_ASSERT(!this->hasOverflowed());
269             JSRopeString* tmp = m_jsString;
270             m_jsString = nullptr;
271             return tmp;
272         }
273
274         unsigned length() const
275         {
276             ASSERT(!this->hasOverflowed());
277             return m_jsString->length();
278         }
279
280     private:
281         void expand();
282
283         VM& m_vm;
284         JSRopeString* m_jsString;
285         size_t m_index;
286     };
287
288 private:
289     ALWAYS_INLINE JSRopeString(VM& vm)
290         : JSString(vm)
291     {
292     }
293
294     void finishCreation(VM& vm, JSString* s1, JSString* s2)
295     {
296         Base::finishCreation(vm);
297         ASSERT(!sumOverflows<int32_t>(s1->length(), s2->length()));
298         setLength(s1->length() + s2->length());
299         setIs8Bit(s1->is8Bit() && s2->is8Bit());
300         setIsSubstring(false);
301         fiber(0).set(vm, this, s1);
302         fiber(1).set(vm, this, s2);
303         fiber(2).clear();
304     }
305
306     void finishCreation(VM& vm, JSString* s1, JSString* s2, JSString* s3)
307     {
308         Base::finishCreation(vm);
309         ASSERT(!sumOverflows<int32_t>(s1->length(), s2->length(), s3->length()));
310         setLength(s1->length() + s2->length() + s3->length());
311         setIs8Bit(s1->is8Bit() && s2->is8Bit() &&  s3->is8Bit());
312         setIsSubstring(false);
313         fiber(0).set(vm, this, s1);
314         fiber(1).set(vm, this, s2);
315         fiber(2).set(vm, this, s3);
316     }
317
318     void finishCreation(VM& vm, ExecState* exec, JSString* base, unsigned offset, unsigned length)
319     {
320         Base::finishCreation(vm);
321         RELEASE_ASSERT(!sumOverflows<int32_t>(offset, length));
322         RELEASE_ASSERT(offset + length <= base->length());
323         setLength(length);
324         setIs8Bit(base->is8Bit());
325         setIsSubstring(true);
326         if (base->isSubstring()) {
327             JSRopeString* baseRope = jsCast<JSRopeString*>(base);
328             substringBase().set(vm, this, baseRope->substringBase().get());
329             substringOffset() = baseRope->substringOffset() + offset;
330         } else {
331             substringBase().set(vm, this, base);
332             substringOffset() = offset;
333
334             // For now, let's not allow substrings with a rope base.
335             // Resolve non-substring rope bases so we don't have to deal with it.
336             // FIXME: Evaluate if this would be worth adding more branches.
337             if (base->isRope())
338                 jsCast<JSRopeString*>(base)->resolveRope(exec);
339         }
340     }
341
342     ALWAYS_INLINE void finishCreationSubstringOfResolved(VM& vm, JSString* base, unsigned offset, unsigned length)
343     {
344         Base::finishCreation(vm);
345         RELEASE_ASSERT(!sumOverflows<int32_t>(offset, length));
346         RELEASE_ASSERT(offset + length <= base->length());
347         setLength(length);
348         setIs8Bit(base->is8Bit());
349         setIsSubstring(true);
350         substringBase().set(vm, this, base);
351         substringOffset() = offset;
352     }
353
354     void finishCreation(VM& vm)
355     {
356         JSString::finishCreation(vm);
357         setIsSubstring(false);
358         fiber(0).clear();
359         fiber(1).clear();
360         fiber(2).clear();
361     }
362
363     void append(VM& vm, size_t index, JSString* jsString)
364     {
365         fiber(index).set(vm, this, jsString);
366         setLength(length() + jsString->length());
367         setIs8Bit(is8Bit() && jsString->is8Bit());
368     }
369
370     static JSRopeString* createNull(VM& vm)
371     {
372         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
373         newString->finishCreation(vm);
374         return newString;
375     }
376
377 public:
378     static JSString* create(VM& vm, ExecState* exec, JSString* base, unsigned offset, unsigned length)
379     {
380         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
381         newString->finishCreation(vm, exec, base, offset, length);
382         return newString;
383     }
384
385     ALWAYS_INLINE static JSString* createSubstringOfResolved(VM& vm, GCDeferralContext* deferralContext, JSString* base, unsigned offset, unsigned length)
386     {
387         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap, deferralContext)) JSRopeString(vm);
388         newString->finishCreationSubstringOfResolved(vm, base, offset, length);
389         return newString;
390     }
391
392     ALWAYS_INLINE static JSString* createSubstringOfResolved(VM& vm, JSString* base, unsigned offset, unsigned length)
393     {
394         return createSubstringOfResolved(vm, nullptr, base, offset, length);
395     }
396
397     void visitFibers(SlotVisitor&);
398
399     static ptrdiff_t offsetOfFibers() { return OBJECT_OFFSETOF(JSRopeString, u); }
400
401     static const unsigned s_maxInternalRopeLength = 3;
402
403 private:
404     static JSString* create(VM& vm, JSString* s1, JSString* s2)
405     {
406         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
407         newString->finishCreation(vm, s1, s2);
408         return newString;
409     }
410     static JSString* create(VM& vm, JSString* s1, JSString* s2, JSString* s3)
411     {
412         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
413         newString->finishCreation(vm, s1, s2, s3);
414         return newString;
415     }
416
417     friend JSValue jsStringFromRegisterArray(ExecState*, Register*, unsigned);
418     friend JSValue jsStringFromArguments(ExecState*, JSValue);
419
420     // If nullOrExecForOOM is null, resolveRope() will be do nothing in the event of an OOM error.
421     // The rope value will remain a null string in that case.
422     JS_EXPORT_PRIVATE void resolveRope(ExecState* nullOrExecForOOM) const;
423     JS_EXPORT_PRIVATE void resolveRopeToAtomicString(ExecState*) const;
424     JS_EXPORT_PRIVATE RefPtr<AtomicStringImpl> resolveRopeToExistingAtomicString(ExecState*) const;
425     void resolveRopeSlowCase8(LChar*) const;
426     void resolveRopeSlowCase(UChar*) const;
427     void outOfMemory(ExecState* nullOrExecForOOM) const;
428     void resolveRopeInternal8(LChar*) const;
429     void resolveRopeInternal8NoSubstring(LChar*) const;
430     void resolveRopeInternal16(UChar*) const;
431     void resolveRopeInternal16NoSubstring(UChar*) const;
432     void clearFibers() const;
433     StringView unsafeView(ExecState*) const;
434     StringViewWithUnderlyingString viewWithUnderlyingString(ExecState*) const;
435
436     WriteBarrierBase<JSString>& fiber(unsigned i) const
437     {
438         ASSERT(!isSubstring());
439         ASSERT(i < s_maxInternalRopeLength);
440         return u[i].string;
441     }
442
443     WriteBarrierBase<JSString>& substringBase() const
444     {
445         return u[1].string;
446     }
447
448     uintptr_t& substringOffset() const
449     {
450         return u[2].number;
451     }
452
453     static uintptr_t notSubstringSentinel()
454     {
455         return 0;
456     }
457
458     static uintptr_t substringSentinel()
459     {
460         return 1;
461     }
462
463     bool isSubstring() const
464     {
465         return u[0].number == substringSentinel();
466     }
467
468     void setIsSubstring(bool isSubstring)
469     {
470         u[0].number = isSubstring ? substringSentinel() : notSubstringSentinel();
471     }
472
473     mutable union {
474         uintptr_t number;
475         WriteBarrierBase<JSString> string;
476     } u[s_maxInternalRopeLength];
477
478
479     friend JSString* jsString(ExecState*, JSString*, JSString*);
480     friend JSString* jsString(ExecState*, JSString*, JSString*, JSString*);
481     friend JSString* jsString(ExecState*, const String&, const String&, const String&);
482 };
483
484 JS_EXPORT_PRIVATE JSString* jsStringWithCacheSlowCase(VM&, StringImpl&);
485
486 inline const StringImpl* JSString::tryGetValueImpl() const
487 {
488     return m_value.impl();
489 }
490
491 inline JSString* asString(JSValue value)
492 {
493     ASSERT(value.asCell()->isString());
494     return jsCast<JSString*>(value.asCell());
495 }
496
497 // This MUST NOT GC.
498 inline JSString* jsEmptyString(VM* vm)
499 {
500     return vm->smallStrings.emptyString();
501 }
502
503 ALWAYS_INLINE JSString* jsSingleCharacterString(VM* vm, UChar c)
504 {
505     if (c <= maxSingleCharacterString)
506         return vm->smallStrings.singleCharacterString(c);
507     return JSString::create(*vm, StringImpl::create(&c, 1));
508 }
509
510 inline JSString* jsNontrivialString(VM* vm, const String& s)
511 {
512     ASSERT(s.length() > 1);
513     return JSString::create(*vm, *s.impl());
514 }
515
516 inline JSString* jsNontrivialString(VM* vm, String&& s)
517 {
518     ASSERT(s.length() > 1);
519     return JSString::create(*vm, s.releaseImpl().releaseNonNull());
520 }
521
522 ALWAYS_INLINE Identifier JSString::toIdentifier(ExecState* exec) const
523 {
524     return Identifier::fromString(exec, toAtomicString(exec));
525 }
526
527 ALWAYS_INLINE AtomicString JSString::toAtomicString(ExecState* exec) const
528 {
529     if (isRope())
530         static_cast<const JSRopeString*>(this)->resolveRopeToAtomicString(exec);
531     return AtomicString(m_value);
532 }
533
534 ALWAYS_INLINE RefPtr<AtomicStringImpl> JSString::toExistingAtomicString(ExecState* exec) const
535 {
536     if (isRope())
537         return static_cast<const JSRopeString*>(this)->resolveRopeToExistingAtomicString(exec);
538     if (m_value.impl()->isAtomic())
539         return static_cast<AtomicStringImpl*>(m_value.impl());
540     return AtomicStringImpl::lookUp(m_value.impl());
541 }
542
543 inline const String& JSString::value(ExecState* exec) const
544 {
545     if (isRope())
546         static_cast<const JSRopeString*>(this)->resolveRope(exec);
547     return m_value;
548 }
549
550 inline const String& JSString::tryGetValue() const
551 {
552     if (isRope()) {
553         // Pass nullptr for the ExecState so that resolveRope does not throw in the event of an OOM error.
554         static_cast<const JSRopeString*>(this)->resolveRope(nullptr);
555     }
556     return m_value;
557 }
558
559 inline JSString* JSString::getIndex(ExecState* exec, unsigned i)
560 {
561     VM& vm = exec->vm();
562     auto scope = DECLARE_THROW_SCOPE(vm);
563     ASSERT(canGetIndex(i));
564     StringView view = unsafeView(exec);
565     RETURN_IF_EXCEPTION(scope, nullptr);
566     return jsSingleCharacterString(exec, view[i]);
567 }
568
569 inline JSString* jsString(VM* vm, const String& s)
570 {
571     int size = s.length();
572     if (!size)
573         return vm->smallStrings.emptyString();
574     if (size == 1) {
575         UChar c = s.characterAt(0);
576         if (c <= maxSingleCharacterString)
577             return vm->smallStrings.singleCharacterString(c);
578     }
579     return JSString::create(*vm, *s.impl());
580 }
581
582 inline JSString* jsSubstring(VM& vm, ExecState* exec, JSString* s, unsigned offset, unsigned length)
583 {
584     ASSERT(offset <= s->length());
585     ASSERT(length <= s->length());
586     ASSERT(offset + length <= s->length());
587     if (!length)
588         return vm.smallStrings.emptyString();
589     if (!offset && length == s->length())
590         return s;
591     return JSRopeString::create(vm, exec, s, offset, length);
592 }
593
594 inline JSString* jsSubstringOfResolved(VM& vm, GCDeferralContext* deferralContext, JSString* s, unsigned offset, unsigned length)
595 {
596     ASSERT(offset <= s->length());
597     ASSERT(length <= s->length());
598     ASSERT(offset + length <= s->length());
599     if (!length)
600         return vm.smallStrings.emptyString();
601     if (!offset && length == s->length())
602         return s;
603     return JSRopeString::createSubstringOfResolved(vm, deferralContext, s, offset, length);
604 }
605
606 inline JSString* jsSubstringOfResolved(VM& vm, JSString* s, unsigned offset, unsigned length)
607 {
608     return jsSubstringOfResolved(vm, nullptr, s, offset, length);
609 }
610
611 inline JSString* jsSubstring(ExecState* exec, JSString* s, unsigned offset, unsigned length)
612 {
613     return jsSubstring(exec->vm(), exec, s, offset, length);
614 }
615
616 inline JSString* jsSubstring(VM* vm, const String& s, unsigned offset, unsigned length)
617 {
618     ASSERT(offset <= s.length());
619     ASSERT(length <= s.length());
620     ASSERT(offset + length <= s.length());
621     if (!length)
622         return vm->smallStrings.emptyString();
623     if (length == 1) {
624         UChar c = s.characterAt(offset);
625         if (c <= maxSingleCharacterString)
626             return vm->smallStrings.singleCharacterString(c);
627     }
628     return JSString::createHasOtherOwner(*vm, StringImpl::createSubstringSharingImpl(*s.impl(), offset, length));
629 }
630
631 inline JSString* jsOwnedString(VM* vm, const String& s)
632 {
633     int size = s.length();
634     if (!size)
635         return vm->smallStrings.emptyString();
636     if (size == 1) {
637         UChar c = s.characterAt(0);
638         if (c <= maxSingleCharacterString)
639             return vm->smallStrings.singleCharacterString(c);
640     }
641     return JSString::createHasOtherOwner(*vm, *s.impl());
642 }
643
644 inline JSRopeString* jsStringBuilder(VM* vm)
645 {
646     return JSRopeString::createNull(*vm);
647 }
648
649 inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->vm()); }
650 inline JSString* jsString(ExecState* exec, const String& s) { return jsString(&exec->vm(), s); }
651 inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->vm(), c); }
652 inline JSString* jsSubstring(ExecState* exec, const String& s, unsigned offset, unsigned length) { return jsSubstring(&exec->vm(), s, offset, length); }
653 inline JSString* jsNontrivialString(ExecState* exec, const String& s) { return jsNontrivialString(&exec->vm(), s); }
654 inline JSString* jsNontrivialString(ExecState* exec, String&& s) { return jsNontrivialString(&exec->vm(), WTFMove(s)); }
655 inline JSString* jsOwnedString(ExecState* exec, const String& s) { return jsOwnedString(&exec->vm(), s); }
656
657 ALWAYS_INLINE JSString* jsStringWithCache(ExecState* exec, const String& s)
658 {
659     VM& vm = exec->vm();
660     StringImpl* stringImpl = s.impl();
661     if (!stringImpl || !stringImpl->length())
662         return jsEmptyString(&vm);
663
664     if (stringImpl->length() == 1) {
665         UChar singleCharacter = (*stringImpl)[0u];
666         if (singleCharacter <= maxSingleCharacterString)
667             return vm.smallStrings.singleCharacterString(static_cast<unsigned char>(singleCharacter));
668     }
669
670     if (JSString* lastCachedString = vm.lastCachedString.get()) {
671         if (lastCachedString->tryGetValueImpl() == stringImpl)
672             return lastCachedString;
673     }
674
675     return jsStringWithCacheSlowCase(vm, *stringImpl);
676 }
677
678 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
679 {
680     VM& vm = exec->vm();
681     if (propertyName == vm.propertyNames->length) {
682         slot.setValue(this, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly, jsNumber(length()));
683         return true;
684     }
685
686     std::optional<uint32_t> index = parseIndex(propertyName);
687     if (index && index.value() < length()) {
688         slot.setValue(this, PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly, getIndex(exec, index.value()));
689         return true;
690     }
691
692     return false;
693 }
694
695 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
696 {
697     if (propertyName < length()) {
698         slot.setValue(this, PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly, getIndex(exec, propertyName));
699         return true;
700     }
701
702     return false;
703 }
704
705 inline bool isJSString(JSCell* cell)
706 {
707     return cell->type() == StringType;
708 }
709
710 inline bool isJSString(JSValue v)
711 {
712     return v.isCell() && isJSString(v.asCell());
713 }
714
715 ALWAYS_INLINE StringView JSRopeString::unsafeView(ExecState* exec) const
716 {
717     if (isSubstring()) {
718         if (is8Bit())
719             return StringView(substringBase()->m_value.characters8() + substringOffset(), length());
720         return StringView(substringBase()->m_value.characters16() + substringOffset(), length());
721     }
722     resolveRope(exec);
723     return m_value;
724 }
725
726 ALWAYS_INLINE StringViewWithUnderlyingString JSRopeString::viewWithUnderlyingString(ExecState* exec) const
727 {
728     if (isSubstring()) {
729         auto& base = substringBase()->m_value;
730         if (is8Bit())
731             return { { base.characters8() + substringOffset(), length() }, base };
732         return { { base.characters16() + substringOffset(), length() }, base };
733     }
734     resolveRope(exec);
735     return { m_value, m_value };
736 }
737
738 ALWAYS_INLINE StringView JSString::unsafeView(ExecState* exec) const
739 {
740     if (isRope())
741         return static_cast<const JSRopeString*>(this)->unsafeView(exec);
742     return m_value;
743 }
744
745 ALWAYS_INLINE StringViewWithUnderlyingString JSString::viewWithUnderlyingString(ExecState* exec) const
746 {
747     if (isRope())
748         return static_cast<const JSRopeString&>(*this).viewWithUnderlyingString(exec);
749     return { m_value, m_value };
750 }
751
752 inline bool JSString::isSubstring() const
753 {
754     return isRope() && static_cast<const JSRopeString*>(this)->isSubstring();
755 }
756
757 // --- JSValue inlines ----------------------------
758
759 inline bool JSValue::toBoolean(ExecState* exec) const
760 {
761     if (isInt32())
762         return asInt32();
763     if (isDouble())
764         return asDouble() > 0.0 || asDouble() < 0.0; // false for NaN
765     if (isCell())
766         return asCell()->toBoolean(exec);
767     return isTrue(); // false, null, and undefined all convert to false.
768 }
769
770 inline JSString* JSValue::toString(ExecState* exec) const
771 {
772     if (isString())
773         return asString(asCell());
774     bool returnEmptyStringOnError = true;
775     return toStringSlowCase(exec, returnEmptyStringOnError);
776 }
777
778 inline JSString* JSValue::toStringOrNull(ExecState* exec) const
779 {
780     if (isString())
781         return asString(asCell());
782     bool returnEmptyStringOnError = false;
783     return toStringSlowCase(exec, returnEmptyStringOnError);
784 }
785
786 inline String JSValue::toWTFString(ExecState* exec) const
787 {
788     if (isString())
789         return static_cast<JSString*>(asCell())->value(exec);
790     return toWTFStringSlowCase(exec);
791 }
792
793 } // namespace JSC