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