[JSC] Shrink size of VM by lazily allocating IsoSubspaces for non-common types
[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, SubspaceAccess>
93     static CompleteSubspace* subspaceFor(VM& vm)
94     {
95         return &vm.stringSpace;
96     }
97     
98     // We employ overflow checks in many places with the assumption that MaxLength
99     // is INT_MAX. Hence, it cannot be changed into another length value without
100     // breaking all the bounds and overflow checks that assume this.
101     static constexpr unsigned MaxLength = std::numeric_limits<int32_t>::max();
102     static_assert(MaxLength == String::MaxLength, "");
103
104 private:
105     JSString(VM& vm, Ref<StringImpl>&& value)
106         : JSCell(vm, vm.stringStructure.get())
107         , m_value(WTFMove(value))
108     {
109     }
110
111     JSString(VM& vm)
112         : JSCell(vm, vm.stringStructure.get())
113     {
114     }
115
116     void finishCreation(VM& vm, unsigned length)
117     {
118         ASSERT(!m_value.isNull());
119         Base::finishCreation(vm);
120         setLength(length);
121         setIs8Bit(m_value.impl()->is8Bit());
122     }
123
124     void finishCreation(VM& vm, unsigned length, size_t cost)
125     {
126         ASSERT(!m_value.isNull());
127         Base::finishCreation(vm);
128         setLength(length);
129         setIs8Bit(m_value.impl()->is8Bit());
130         vm.heap.reportExtraMemoryAllocated(cost);
131     }
132
133 protected:
134     void finishCreation(VM& vm)
135     {
136         Base::finishCreation(vm);
137         setLength(0);
138         setIs8Bit(true);
139     }
140
141 public:
142     static JSString* create(VM& vm, Ref<StringImpl>&& value)
143     {
144         unsigned length = value->length();
145         size_t cost = value->cost();
146         JSString* newString = new (NotNull, allocateCell<JSString>(vm.heap)) JSString(vm, WTFMove(value));
147         newString->finishCreation(vm, length, cost);
148         return newString;
149     }
150     static JSString* createHasOtherOwner(VM& vm, Ref<StringImpl>&& value)
151     {
152         unsigned 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     inline 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*, VM&);
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         ASSERT(length <= MaxLength);
217         m_length = length;
218     }
219
220 private:
221     // A string is represented either by a String or a rope of fibers.
222     unsigned m_length { 0 };
223     mutable uint16_t m_flags { 0 };
224     // The poison is strategically placed and holds a value such that the first
225     // 64 bits of JSString look like a double JSValue.
226     mutable String m_value;
227
228     friend class LLIntOffsetsExtractor;
229
230     static JSValue toThis(JSCell*, ExecState*, ECMAMode);
231
232     String& string() { ASSERT(!isRope()); return m_value; }
233     StringView unsafeView(ExecState*) const;
234
235     friend JSString* jsString(ExecState*, JSString*, JSString*);
236     friend JSString* jsSubstring(ExecState*, JSString*, unsigned offset, unsigned length);
237 };
238
239 // NOTE: This class cannot override JSString's destructor. JSString's destructor is called directly
240 // from JSStringSubspace::
241 class JSRopeString final : public JSString {
242     friend class JSString;
243
244     friend JSRopeString* jsStringBuilder(VM*);
245
246 public:
247     template <class OverflowHandler = CrashOnOverflow>
248     class RopeBuilder : public OverflowHandler {
249     public:
250         RopeBuilder(VM& vm)
251             : m_vm(vm)
252             , m_jsString(jsStringBuilder(&vm))
253             , m_index(0)
254         {
255         }
256
257         bool append(JSString* jsString)
258         {
259             if (UNLIKELY(this->hasOverflowed()))
260                 return false;
261             if (m_index == JSRopeString::s_maxInternalRopeLength)
262                 expand();
263
264             static_assert(JSString::MaxLength == std::numeric_limits<int32_t>::max(), "");
265             auto sum = checkedSum<int32_t>(m_jsString->length(), jsString->length());
266             if (sum.hasOverflowed()) {
267                 this->overflowed();
268                 return false;
269             }
270             ASSERT(static_cast<unsigned>(sum.unsafeGet()) <= MaxLength);
271             m_jsString->append(m_vm, m_index++, jsString);
272             return true;
273         }
274
275         JSRopeString* release()
276         {
277             RELEASE_ASSERT(!this->hasOverflowed());
278             JSRopeString* tmp = m_jsString;
279             m_jsString = nullptr;
280             return tmp;
281         }
282
283         unsigned length() const
284         {
285             ASSERT(!this->hasOverflowed());
286             return m_jsString->length();
287         }
288
289     private:
290         void expand();
291
292         VM& m_vm;
293         JSRopeString* m_jsString;
294         size_t m_index;
295     };
296
297 private:
298     ALWAYS_INLINE JSRopeString(VM& vm)
299         : JSString(vm)
300     {
301     }
302
303     void finishCreation(VM& vm, JSString* s1, JSString* s2)
304     {
305         Base::finishCreation(vm);
306         ASSERT(!sumOverflows<int32_t>(s1->length(), s2->length()));
307         setLength(s1->length() + s2->length());
308         setIs8Bit(s1->is8Bit() && s2->is8Bit());
309         setIsSubstring(false);
310         fiber(0).set(vm, this, s1);
311         fiber(1).set(vm, this, s2);
312         fiber(2).clear();
313     }
314
315     void finishCreation(VM& vm, JSString* s1, JSString* s2, JSString* s3)
316     {
317         Base::finishCreation(vm);
318         ASSERT(!sumOverflows<int32_t>(s1->length(), s2->length(), s3->length()));
319         setLength(s1->length() + s2->length() + s3->length());
320         setIs8Bit(s1->is8Bit() && s2->is8Bit() &&  s3->is8Bit());
321         setIsSubstring(false);
322         fiber(0).set(vm, this, s1);
323         fiber(1).set(vm, this, s2);
324         fiber(2).set(vm, this, s3);
325     }
326
327     void finishCreation(VM& vm, ExecState* exec, JSString* base, unsigned offset, unsigned length)
328     {
329         Base::finishCreation(vm);
330         RELEASE_ASSERT(!sumOverflows<int32_t>(offset, length));
331         RELEASE_ASSERT(offset + length <= base->length());
332         setLength(length);
333         setIs8Bit(base->is8Bit());
334         setIsSubstring(true);
335         if (base->isSubstring()) {
336             JSRopeString* baseRope = jsCast<JSRopeString*>(base);
337             substringBase().set(vm, this, baseRope->substringBase().get());
338             substringOffset() = baseRope->substringOffset() + offset;
339         } else {
340             substringBase().set(vm, this, base);
341             substringOffset() = offset;
342
343             // For now, let's not allow substrings with a rope base.
344             // Resolve non-substring rope bases so we don't have to deal with it.
345             // FIXME: Evaluate if this would be worth adding more branches.
346             if (base->isRope())
347                 jsCast<JSRopeString*>(base)->resolveRope(exec);
348         }
349     }
350
351     ALWAYS_INLINE void finishCreationSubstringOfResolved(VM& vm, JSString* base, unsigned offset, unsigned length)
352     {
353         Base::finishCreation(vm);
354         RELEASE_ASSERT(!sumOverflows<int32_t>(offset, length));
355         RELEASE_ASSERT(offset + length <= base->length());
356         setLength(length);
357         setIs8Bit(base->is8Bit());
358         setIsSubstring(true);
359         substringBase().set(vm, this, base);
360         substringOffset() = offset;
361     }
362
363     void finishCreation(VM& vm)
364     {
365         JSString::finishCreation(vm);
366         setIsSubstring(false);
367         fiber(0).clear();
368         fiber(1).clear();
369         fiber(2).clear();
370     }
371
372     void append(VM& vm, size_t index, JSString* jsString)
373     {
374         fiber(index).set(vm, this, jsString);
375         setLength(length() + jsString->length());
376         setIs8Bit(is8Bit() && jsString->is8Bit());
377     }
378
379     static JSRopeString* createNull(VM& vm)
380     {
381         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
382         newString->finishCreation(vm);
383         return newString;
384     }
385
386 public:
387     static JSString* create(VM& vm, ExecState* exec, JSString* base, unsigned offset, unsigned length)
388     {
389         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
390         newString->finishCreation(vm, exec, base, offset, length);
391         return newString;
392     }
393
394     ALWAYS_INLINE static JSString* createSubstringOfResolved(VM& vm, GCDeferralContext* deferralContext, JSString* base, unsigned offset, unsigned length)
395     {
396         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap, deferralContext)) JSRopeString(vm);
397         newString->finishCreationSubstringOfResolved(vm, base, offset, length);
398         return newString;
399     }
400
401     ALWAYS_INLINE static JSString* createSubstringOfResolved(VM& vm, JSString* base, unsigned offset, unsigned length)
402     {
403         return createSubstringOfResolved(vm, nullptr, base, offset, length);
404     }
405
406     void visitFibers(SlotVisitor&);
407
408     static ptrdiff_t offsetOfFibers() { return OBJECT_OFFSETOF(JSRopeString, u); }
409
410     static const unsigned s_maxInternalRopeLength = 3;
411
412 private:
413     static JSString* create(VM& vm, JSString* s1, JSString* s2)
414     {
415         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
416         newString->finishCreation(vm, s1, s2);
417         return newString;
418     }
419     static JSString* create(VM& vm, JSString* s1, JSString* s2, JSString* s3)
420     {
421         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
422         newString->finishCreation(vm, s1, s2, s3);
423         return newString;
424     }
425
426     friend JSValue jsStringFromRegisterArray(ExecState*, Register*, unsigned);
427     friend JSValue jsStringFromArguments(ExecState*, JSValue);
428
429     // If nullOrExecForOOM is null, resolveRope() will be do nothing in the event of an OOM error.
430     // The rope value will remain a null string in that case.
431     JS_EXPORT_PRIVATE void resolveRope(ExecState* nullOrExecForOOM) const;
432     JS_EXPORT_PRIVATE void resolveRopeToAtomicString(ExecState*) const;
433     JS_EXPORT_PRIVATE RefPtr<AtomicStringImpl> resolveRopeToExistingAtomicString(ExecState*) const;
434     void resolveRopeSlowCase8(LChar*) const;
435     void resolveRopeSlowCase(UChar*) const;
436     void outOfMemory(ExecState* nullOrExecForOOM) const;
437     void resolveRopeInternal8(LChar*) const;
438     void resolveRopeInternal8NoSubstring(LChar*) const;
439     void resolveRopeInternal16(UChar*) const;
440     void resolveRopeInternal16NoSubstring(UChar*) const;
441     void clearFibers() const;
442     StringView unsafeView(ExecState*) const;
443     StringViewWithUnderlyingString viewWithUnderlyingString(ExecState*) const;
444
445     WriteBarrierBase<JSString>& fiber(unsigned i) const
446     {
447         ASSERT(!isSubstring());
448         ASSERT(i < s_maxInternalRopeLength);
449         return u[i].string;
450     }
451
452     WriteBarrierBase<JSString>& substringBase() const
453     {
454         return u[1].string;
455     }
456
457     uintptr_t& substringOffset() const
458     {
459         return u[2].number;
460     }
461
462     static uintptr_t notSubstringSentinel()
463     {
464         return 0;
465     }
466
467     static uintptr_t substringSentinel()
468     {
469         return 1;
470     }
471
472     bool isSubstring() const
473     {
474         return u[0].number == substringSentinel();
475     }
476
477     void setIsSubstring(bool isSubstring)
478     {
479         u[0].number = isSubstring ? substringSentinel() : notSubstringSentinel();
480     }
481
482     mutable union {
483         uintptr_t number;
484         WriteBarrierBase<JSString> string;
485     } u[s_maxInternalRopeLength];
486
487
488     friend JSString* jsString(ExecState*, JSString*, JSString*);
489     friend JSString* jsString(ExecState*, JSString*, JSString*, JSString*);
490     friend JSString* jsString(ExecState*, const String&, const String&, const String&);
491 };
492
493 JS_EXPORT_PRIVATE JSString* jsStringWithCacheSlowCase(VM&, StringImpl&);
494
495 inline const StringImpl* JSString::tryGetValueImpl() const
496 {
497     return m_value.impl();
498 }
499
500 inline JSString* asString(JSValue value)
501 {
502     ASSERT(value.asCell()->isString());
503     return jsCast<JSString*>(value.asCell());
504 }
505
506 // This MUST NOT GC.
507 inline JSString* jsEmptyString(VM* vm)
508 {
509     return vm->smallStrings.emptyString();
510 }
511
512 ALWAYS_INLINE JSString* jsSingleCharacterString(VM* vm, UChar c)
513 {
514     if (c <= maxSingleCharacterString)
515         return vm->smallStrings.singleCharacterString(c);
516     return JSString::create(*vm, StringImpl::create(&c, 1));
517 }
518
519 inline JSString* jsNontrivialString(VM* vm, const String& s)
520 {
521     ASSERT(s.length() > 1);
522     return JSString::create(*vm, *s.impl());
523 }
524
525 inline JSString* jsNontrivialString(VM* vm, String&& s)
526 {
527     ASSERT(s.length() > 1);
528     return JSString::create(*vm, s.releaseImpl().releaseNonNull());
529 }
530
531 ALWAYS_INLINE Identifier JSString::toIdentifier(ExecState* exec) const
532 {
533     return Identifier::fromString(exec, toAtomicString(exec));
534 }
535
536 ALWAYS_INLINE AtomicString JSString::toAtomicString(ExecState* exec) const
537 {
538     if (isRope())
539         static_cast<const JSRopeString*>(this)->resolveRopeToAtomicString(exec);
540     return AtomicString(m_value);
541 }
542
543 ALWAYS_INLINE RefPtr<AtomicStringImpl> JSString::toExistingAtomicString(ExecState* exec) const
544 {
545     if (isRope())
546         return static_cast<const JSRopeString*>(this)->resolveRopeToExistingAtomicString(exec);
547     if (m_value.impl()->isAtomic())
548         return static_cast<AtomicStringImpl*>(m_value.impl());
549     return AtomicStringImpl::lookUp(m_value.impl());
550 }
551
552 inline const String& JSString::value(ExecState* exec) const
553 {
554     if (isRope())
555         static_cast<const JSRopeString*>(this)->resolveRope(exec);
556     return m_value;
557 }
558
559 inline const String& JSString::tryGetValue() const
560 {
561     if (isRope()) {
562         // Pass nullptr for the ExecState so that resolveRope does not throw in the event of an OOM error.
563         static_cast<const JSRopeString*>(this)->resolveRope(nullptr);
564     }
565     return m_value;
566 }
567
568 inline JSString* JSString::getIndex(ExecState* exec, unsigned i)
569 {
570     VM& vm = exec->vm();
571     auto scope = DECLARE_THROW_SCOPE(vm);
572     ASSERT(canGetIndex(i));
573     StringView view = unsafeView(exec);
574     RETURN_IF_EXCEPTION(scope, nullptr);
575     return jsSingleCharacterString(exec, view[i]);
576 }
577
578 inline JSString* jsString(VM* vm, const String& s)
579 {
580     int size = s.length();
581     if (!size)
582         return vm->smallStrings.emptyString();
583     if (size == 1) {
584         UChar c = s.characterAt(0);
585         if (c <= maxSingleCharacterString)
586             return vm->smallStrings.singleCharacterString(c);
587     }
588     return JSString::create(*vm, *s.impl());
589 }
590
591 inline JSString* jsSubstring(VM& vm, ExecState* exec, JSString* s, unsigned offset, unsigned length)
592 {
593     ASSERT(offset <= s->length());
594     ASSERT(length <= s->length());
595     ASSERT(offset + length <= s->length());
596     if (!length)
597         return vm.smallStrings.emptyString();
598     if (!offset && length == s->length())
599         return s;
600     return JSRopeString::create(vm, exec, s, offset, length);
601 }
602
603 inline JSString* jsSubstringOfResolved(VM& vm, GCDeferralContext* deferralContext, JSString* s, unsigned offset, unsigned length)
604 {
605     ASSERT(offset <= s->length());
606     ASSERT(length <= s->length());
607     ASSERT(offset + length <= s->length());
608     if (!length)
609         return vm.smallStrings.emptyString();
610     if (!offset && length == s->length())
611         return s;
612     return JSRopeString::createSubstringOfResolved(vm, deferralContext, s, offset, length);
613 }
614
615 inline JSString* jsSubstringOfResolved(VM& vm, JSString* s, unsigned offset, unsigned length)
616 {
617     return jsSubstringOfResolved(vm, nullptr, s, offset, length);
618 }
619
620 inline JSString* jsSubstring(ExecState* exec, JSString* s, unsigned offset, unsigned length)
621 {
622     return jsSubstring(exec->vm(), exec, s, offset, length);
623 }
624
625 inline JSString* jsSubstring(VM* vm, const String& s, unsigned offset, unsigned length)
626 {
627     ASSERT(offset <= s.length());
628     ASSERT(length <= s.length());
629     ASSERT(offset + length <= s.length());
630     if (!length)
631         return vm->smallStrings.emptyString();
632     if (length == 1) {
633         UChar c = s.characterAt(offset);
634         if (c <= maxSingleCharacterString)
635             return vm->smallStrings.singleCharacterString(c);
636     }
637     return JSString::createHasOtherOwner(*vm, StringImpl::createSubstringSharingImpl(*s.impl(), offset, length));
638 }
639
640 inline JSString* jsOwnedString(VM* vm, const String& s)
641 {
642     int size = s.length();
643     if (!size)
644         return vm->smallStrings.emptyString();
645     if (size == 1) {
646         UChar c = s.characterAt(0);
647         if (c <= maxSingleCharacterString)
648             return vm->smallStrings.singleCharacterString(c);
649     }
650     return JSString::createHasOtherOwner(*vm, *s.impl());
651 }
652
653 inline JSRopeString* jsStringBuilder(VM* vm)
654 {
655     return JSRopeString::createNull(*vm);
656 }
657
658 inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->vm()); }
659 inline JSString* jsString(ExecState* exec, const String& s) { return jsString(&exec->vm(), s); }
660 inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->vm(), c); }
661 inline JSString* jsSubstring(ExecState* exec, const String& s, unsigned offset, unsigned length) { return jsSubstring(&exec->vm(), s, offset, length); }
662 inline JSString* jsNontrivialString(ExecState* exec, const String& s) { return jsNontrivialString(&exec->vm(), s); }
663 inline JSString* jsNontrivialString(ExecState* exec, String&& s) { return jsNontrivialString(&exec->vm(), WTFMove(s)); }
664 inline JSString* jsOwnedString(ExecState* exec, const String& s) { return jsOwnedString(&exec->vm(), s); }
665
666 ALWAYS_INLINE JSString* jsStringWithCache(ExecState* exec, const String& s)
667 {
668     VM& vm = exec->vm();
669     StringImpl* stringImpl = s.impl();
670     if (!stringImpl || !stringImpl->length())
671         return jsEmptyString(&vm);
672
673     if (stringImpl->length() == 1) {
674         UChar singleCharacter = (*stringImpl)[0u];
675         if (singleCharacter <= maxSingleCharacterString)
676             return vm.smallStrings.singleCharacterString(static_cast<unsigned char>(singleCharacter));
677     }
678
679     if (JSString* lastCachedString = vm.lastCachedString.get()) {
680         if (lastCachedString->tryGetValueImpl() == stringImpl)
681             return lastCachedString;
682     }
683
684     return jsStringWithCacheSlowCase(vm, *stringImpl);
685 }
686
687 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
688 {
689     VM& vm = exec->vm();
690     auto scope = DECLARE_THROW_SCOPE(vm);
691
692     if (propertyName == vm.propertyNames->length) {
693         slot.setValue(this, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly, jsNumber(length()));
694         return true;
695     }
696
697     Optional<uint32_t> index = parseIndex(propertyName);
698     if (index && index.value() < length()) {
699         JSValue value = getIndex(exec, index.value());
700         RETURN_IF_EXCEPTION(scope, false);
701         slot.setValue(this, PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly, value);
702         return true;
703     }
704
705     return false;
706 }
707
708 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
709 {
710     VM& vm = exec->vm();
711     auto scope = DECLARE_THROW_SCOPE(vm);
712
713     if (propertyName < length()) {
714         JSValue value = getIndex(exec, propertyName);
715         RETURN_IF_EXCEPTION(scope, false);
716         slot.setValue(this, PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly, value);
717         return true;
718     }
719
720     return false;
721 }
722
723 inline bool isJSString(JSCell* cell)
724 {
725     return cell->type() == StringType;
726 }
727
728 inline bool isJSString(JSValue v)
729 {
730     return v.isCell() && isJSString(v.asCell());
731 }
732
733 ALWAYS_INLINE StringView JSRopeString::unsafeView(ExecState* exec) const
734 {
735     if (isSubstring()) {
736         if (is8Bit())
737             return StringView(substringBase()->m_value.characters8() + substringOffset(), length());
738         return StringView(substringBase()->m_value.characters16() + substringOffset(), length());
739     }
740     resolveRope(exec);
741     return m_value;
742 }
743
744 ALWAYS_INLINE StringViewWithUnderlyingString JSRopeString::viewWithUnderlyingString(ExecState* exec) const
745 {
746     if (isSubstring()) {
747         auto& base = substringBase()->m_value;
748         if (is8Bit())
749             return { { base.characters8() + substringOffset(), length() }, base };
750         return { { base.characters16() + substringOffset(), length() }, base };
751     }
752     resolveRope(exec);
753     return { m_value, m_value };
754 }
755
756 ALWAYS_INLINE StringView JSString::unsafeView(ExecState* exec) const
757 {
758     if (isRope())
759         return static_cast<const JSRopeString*>(this)->unsafeView(exec);
760     return m_value;
761 }
762
763 ALWAYS_INLINE StringViewWithUnderlyingString JSString::viewWithUnderlyingString(ExecState* exec) const
764 {
765     if (isRope())
766         return static_cast<const JSRopeString&>(*this).viewWithUnderlyingString(exec);
767     return { m_value, m_value };
768 }
769
770 inline bool JSString::isSubstring() const
771 {
772     return isRope() && static_cast<const JSRopeString*>(this)->isSubstring();
773 }
774
775 // --- JSValue inlines ----------------------------
776
777 inline bool JSValue::toBoolean(ExecState* exec) const
778 {
779     if (isInt32())
780         return asInt32();
781     if (isDouble())
782         return asDouble() > 0.0 || asDouble() < 0.0; // false for NaN
783     if (isCell())
784         return asCell()->toBoolean(exec);
785     return isTrue(); // false, null, and undefined all convert to false.
786 }
787
788 inline JSString* JSValue::toString(ExecState* exec) const
789 {
790     if (isString())
791         return asString(asCell());
792     bool returnEmptyStringOnError = true;
793     return toStringSlowCase(exec, returnEmptyStringOnError);
794 }
795
796 inline JSString* JSValue::toStringOrNull(ExecState* exec) const
797 {
798     if (isString())
799         return asString(asCell());
800     bool returnEmptyStringOnError = false;
801     return toStringSlowCase(exec, returnEmptyStringOnError);
802 }
803
804 inline String JSValue::toWTFString(ExecState* exec) const
805 {
806     if (isString())
807         return static_cast<JSString*>(asCell())->value(exec);
808     return toWTFStringSlowCase(exec);
809 }
810
811 } // namespace JSC