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