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