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