[JSC] JSString::getIndex() should avoid reifying substrings.
[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 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 class JSString : public JSCell {
66 public:
67     friend class JIT;
68     friend class VM;
69     friend class SpecializedThunkJIT;
70     friend class JSRopeString;
71     friend class MarkStack;
72     friend class SlotVisitor;
73     friend struct ThunkHelpers;
74
75     typedef JSCell Base;
76     static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | StructureIsImmortal;
77
78     static const bool needsDestruction = true;
79     static void destroy(JSCell*);
80
81 private:
82     JSString(VM& vm, PassRefPtr<StringImpl> value)
83         : JSCell(vm, vm.stringStructure.get())
84         , m_flags(0)
85         , m_value(value)
86     {
87     }
88
89     JSString(VM& vm)
90         : JSCell(vm, vm.stringStructure.get())
91         , m_flags(0)
92     {
93     }
94
95     void finishCreation(VM& vm, size_t length)
96     {
97         ASSERT(!m_value.isNull());
98         Base::finishCreation(vm);
99         m_length = length;
100         setIs8Bit(m_value.impl()->is8Bit());
101         vm.m_newStringsSinceLastHashCons++;
102     }
103
104     void finishCreation(VM& vm, size_t length, size_t cost)
105     {
106         ASSERT(!m_value.isNull());
107         Base::finishCreation(vm);
108         m_length = length;
109         setIs8Bit(m_value.impl()->is8Bit());
110         Heap::heap(this)->reportExtraMemoryAllocated(cost);
111         vm.m_newStringsSinceLastHashCons++;
112     }
113
114 protected:
115     void finishCreation(VM& vm)
116     {
117         Base::finishCreation(vm);
118         m_length = 0;
119         setIs8Bit(true);
120         vm.m_newStringsSinceLastHashCons++;
121     }
122
123 public:
124     static JSString* create(VM& vm, PassRefPtr<StringImpl> value)
125     {
126         ASSERT(value);
127         int32_t length = value->length();
128         RELEASE_ASSERT(length >= 0);
129         size_t cost = value->cost();
130         JSString* newString = new (NotNull, allocateCell<JSString>(vm.heap)) JSString(vm, value);
131         newString->finishCreation(vm, length, cost);
132         return newString;
133     }
134     static JSString* createHasOtherOwner(VM& vm, PassRefPtr<StringImpl> value)
135     {
136         ASSERT(value);
137         size_t length = value->length();
138         JSString* newString = new (NotNull, allocateCell<JSString>(vm.heap)) JSString(vm, value);
139         newString->finishCreation(vm, length);
140         return newString;
141     }
142
143     Identifier toIdentifier(ExecState*) const;
144     AtomicString toAtomicString(ExecState*) const;
145     RefPtr<AtomicStringImpl> toExistingAtomicString(ExecState*) const;
146     StringView view(ExecState*) const;
147     const String& value(ExecState*) const;
148     const String& tryGetValue() const;
149     const StringImpl* tryGetValueImpl() const;
150     unsigned length() const { return m_length; }
151
152     JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const;
153     bool toBoolean() const { return !!m_length; }
154     bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const;
155     JSObject* toObject(ExecState*, JSGlobalObject*) const;
156     double toNumber(ExecState*) const;
157
158     bool getStringPropertySlot(ExecState*, PropertyName, PropertySlot&);
159     bool getStringPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
160     bool getStringPropertyDescriptor(ExecState*, PropertyName, PropertyDescriptor&);
161
162     bool canGetIndex(unsigned i) { return i < m_length; }
163     JSString* getIndex(ExecState*, unsigned);
164
165     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto)
166     {
167         return Structure::create(vm, globalObject, proto, TypeInfo(StringType, StructureFlags), info());
168     }
169
170     static size_t offsetOfLength() { return OBJECT_OFFSETOF(JSString, m_length); }
171     static size_t offsetOfFlags() { return OBJECT_OFFSETOF(JSString, m_flags); }
172     static size_t offsetOfValue() { return OBJECT_OFFSETOF(JSString, m_value); }
173
174     DECLARE_EXPORT_INFO;
175
176     static void dumpToStream(const JSCell*, PrintStream&);
177     static void visitChildren(JSCell*, SlotVisitor&);
178
179     enum {
180         HashConsLock = 1u << 2,
181         IsHashConsSingleton = 1u << 1,
182         Is8Bit = 1u
183     };
184
185 protected:
186     friend class JSValue;
187
188     bool isRope() const { return m_value.isNull(); }
189     bool is8Bit() const { return m_flags & Is8Bit; }
190     void setIs8Bit(bool flag) const
191     {
192         if (flag)
193             m_flags |= Is8Bit;
194         else
195             m_flags &= ~Is8Bit;
196     }
197     bool shouldTryHashCons();
198     bool isHashConsSingleton() const { return m_flags & IsHashConsSingleton; }
199     void clearHashConsSingleton() { m_flags &= ~IsHashConsSingleton; }
200     void setHashConsSingleton() { m_flags |= IsHashConsSingleton; }
201     bool tryHashConsLock();
202     void releaseHashConsLock();
203
204     mutable unsigned m_flags;
205
206     // A string is represented either by a String or a rope of fibers.
207     unsigned m_length;
208     mutable String m_value;
209
210 private:
211     friend class LLIntOffsetsExtractor;
212
213     static JSValue toThis(JSCell*, ExecState*, ECMAMode);
214
215     String& string() { ASSERT(!isRope()); return m_value; }
216
217     friend JSValue jsString(ExecState*, JSString*, JSString*);
218     friend JSString* jsSubstring(ExecState*, JSString*, unsigned offset, unsigned length);
219 };
220
221 class JSRopeString final : public JSString {
222     friend class JSString;
223
224     friend JSRopeString* jsStringBuilder(VM*);
225
226 public:
227     class RopeBuilder {
228     public:
229         RopeBuilder(VM& vm)
230             : m_vm(vm)
231             , m_jsString(jsStringBuilder(&vm))
232             , m_index(0)
233         {
234         }
235
236         bool append(JSString* jsString)
237         {
238             if (m_index == JSRopeString::s_maxInternalRopeLength)
239                 expand();
240             if (static_cast<int32_t>(m_jsString->length() + jsString->length()) < 0) {
241                 m_jsString = nullptr;
242                 return false;
243             }
244             m_jsString->append(m_vm, m_index++, jsString);
245             return true;
246         }
247
248         JSRopeString* release()
249         {
250             RELEASE_ASSERT(m_jsString);
251             JSRopeString* tmp = m_jsString;
252             m_jsString = 0;
253             return tmp;
254         }
255
256         unsigned length() const { return m_jsString->m_length; }
257
258     private:
259         void expand();
260
261         VM& m_vm;
262         JSRopeString* m_jsString;
263         size_t m_index;
264     };
265
266 private:
267     JSRopeString(VM& vm)
268         : JSString(vm)
269     {
270     }
271
272     void finishCreation(VM& vm, JSString* s1, JSString* s2)
273     {
274         Base::finishCreation(vm);
275         m_length = s1->length() + s2->length();
276         setIs8Bit(s1->is8Bit() && s2->is8Bit());
277         setIsSubstring(false);
278         fiber(0).set(vm, this, s1);
279         fiber(1).set(vm, this, s2);
280         fiber(2).clear();
281     }
282
283     void finishCreation(VM& vm, JSString* s1, JSString* s2, JSString* s3)
284     {
285         Base::finishCreation(vm);
286         m_length = s1->length() + s2->length() + s3->length();
287         setIs8Bit(s1->is8Bit() && s2->is8Bit() &&  s3->is8Bit());
288         setIsSubstring(false);
289         fiber(0).set(vm, this, s1);
290         fiber(1).set(vm, this, s2);
291         fiber(2).set(vm, this, s3);
292     }
293
294     void finishCreation(VM& vm, JSString* base, unsigned offset, unsigned length)
295     {
296         Base::finishCreation(vm);
297         ASSERT(!base->isRope());
298         ASSERT(!sumOverflows<int32_t>(offset, length));
299         ASSERT(offset + length <= base->length());
300         m_length = length;
301         setIs8Bit(base->is8Bit());
302         setIsSubstring(true);
303         substringBase().set(vm, this, base);
304         substringOffset() = offset;
305     }
306
307     void finishCreation(VM& vm)
308     {
309         JSString::finishCreation(vm);
310         setIsSubstring(false);
311         fiber(0).clear();
312         fiber(1).clear();
313         fiber(2).clear();
314     }
315
316     void append(VM& vm, size_t index, JSString* jsString)
317     {
318         fiber(index).set(vm, this, jsString);
319         m_length += jsString->m_length;
320         RELEASE_ASSERT(static_cast<int32_t>(m_length) >= 0);
321         setIs8Bit(is8Bit() && jsString->is8Bit());
322     }
323
324     static JSRopeString* createNull(VM& vm)
325     {
326         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
327         newString->finishCreation(vm);
328         return newString;
329     }
330
331 public:
332     static JSString* create(VM& vm, JSString* s1, JSString* s2)
333     {
334         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
335         newString->finishCreation(vm, s1, s2);
336         return newString;
337     }
338     static JSString* create(VM& vm, JSString* s1, JSString* s2, JSString* s3)
339     {
340         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
341         newString->finishCreation(vm, s1, s2, s3);
342         return newString;
343     }
344
345     static JSString* create(VM& vm, JSString* base, unsigned offset, unsigned length)
346     {
347         JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
348         newString->finishCreation(vm, base, offset, length);
349         return newString;
350     }
351
352     void visitFibers(SlotVisitor&);
353
354     static ptrdiff_t offsetOfFibers() { return OBJECT_OFFSETOF(JSRopeString, u); }
355
356     static const unsigned s_maxInternalRopeLength = 3;
357
358 private:
359     friend JSValue jsStringFromRegisterArray(ExecState*, Register*, unsigned);
360     friend JSValue jsStringFromArguments(ExecState*, JSValue);
361
362     JS_EXPORT_PRIVATE void resolveRope(ExecState*) const;
363     JS_EXPORT_PRIVATE void resolveRopeToAtomicString(ExecState*) const;
364     JS_EXPORT_PRIVATE RefPtr<AtomicStringImpl> resolveRopeToExistingAtomicString(ExecState*) const;
365     void resolveRopeSlowCase8(LChar*) const;
366     void resolveRopeSlowCase(UChar*) const;
367     void outOfMemory(ExecState*) const;
368     void resolveRopeInternal8(LChar*) const;
369     void resolveRopeInternal8NoSubstring(LChar*) const;
370     void resolveRopeInternal16(UChar*) const;
371     void resolveRopeInternal16NoSubstring(UChar*) const;
372     void clearFibers() const;
373     StringView view(ExecState*) const;
374
375     WriteBarrierBase<JSString>& fiber(unsigned i) const
376     {
377         ASSERT(!isSubstring());
378         ASSERT(i < s_maxInternalRopeLength);
379         return u[i].string;
380     }
381
382     WriteBarrierBase<JSString>& substringBase() const
383     {
384         return u[1].string;
385     }
386
387     uintptr_t& substringOffset() const
388     {
389         return u[2].number;
390     }
391
392     static uintptr_t notSubstringSentinel()
393     {
394         return 0;
395     }
396
397     static uintptr_t substringSentinel()
398     {
399         return 1;
400     }
401
402     bool isSubstring() const
403     {
404         return u[0].number == substringSentinel();
405     }
406
407     void setIsSubstring(bool isSubstring)
408     {
409         u[0].number = isSubstring ? substringSentinel() : notSubstringSentinel();
410     }
411
412     mutable union {
413         uintptr_t number;
414         WriteBarrierBase<JSString> string;
415     } u[s_maxInternalRopeLength];
416 };
417
418
419 inline const StringImpl* JSString::tryGetValueImpl() const
420 {
421     return m_value.impl();
422 }
423
424 JSString* asString(JSValue);
425
426 inline JSString* asString(JSValue value)
427 {
428     ASSERT(value.asCell()->isString());
429     return jsCast<JSString*>(value.asCell());
430 }
431
432 inline JSString* jsEmptyString(VM* vm)
433 {
434     return vm->smallStrings.emptyString();
435 }
436
437 ALWAYS_INLINE JSString* jsSingleCharacterString(VM* vm, UChar c)
438 {
439     if (c <= maxSingleCharacterString)
440         return vm->smallStrings.singleCharacterString(c);
441     return JSString::create(*vm, String(&c, 1).impl());
442 }
443
444 inline JSString* jsNontrivialString(VM* vm, const String& s)
445 {
446     ASSERT(s.length() > 1);
447     return JSString::create(*vm, s.impl());
448 }
449
450 inline JSString* jsNontrivialString(VM* vm, String&& s)
451 {
452     ASSERT(s.length() > 1);
453     return JSString::create(*vm, s.releaseImpl());
454 }
455
456 ALWAYS_INLINE Identifier JSString::toIdentifier(ExecState* exec) const
457 {
458     return Identifier::fromString(exec, toAtomicString(exec));
459 }
460
461 ALWAYS_INLINE AtomicString JSString::toAtomicString(ExecState* exec) const
462 {
463     if (isRope())
464         static_cast<const JSRopeString*>(this)->resolveRopeToAtomicString(exec);
465     return AtomicString(m_value);
466 }
467
468 ALWAYS_INLINE RefPtr<AtomicStringImpl> JSString::toExistingAtomicString(ExecState* exec) const
469 {
470     if (isRope())
471         return static_cast<const JSRopeString*>(this)->resolveRopeToExistingAtomicString(exec);
472     if (m_value.impl()->isAtomic())
473         return static_cast<AtomicStringImpl*>(m_value.impl());
474     return AtomicStringImpl::lookUp(m_value.impl());
475 }
476
477 inline const String& JSString::value(ExecState* exec) const
478 {
479     if (isRope())
480         static_cast<const JSRopeString*>(this)->resolveRope(exec);
481     return m_value;
482 }
483
484 inline const String& JSString::tryGetValue() const
485 {
486     if (isRope())
487         static_cast<const JSRopeString*>(this)->resolveRope(0);
488     return m_value;
489 }
490
491 inline JSString* JSString::getIndex(ExecState* exec, unsigned i)
492 {
493     ASSERT(canGetIndex(i));
494     return jsSingleCharacterString(exec, view(exec)[i]);
495 }
496
497 inline JSString* jsString(VM* vm, const String& s)
498 {
499     int size = s.length();
500     if (!size)
501         return vm->smallStrings.emptyString();
502     if (size == 1) {
503         UChar c = s.characterAt(0);
504         if (c <= maxSingleCharacterString)
505             return vm->smallStrings.singleCharacterString(c);
506     }
507     return JSString::create(*vm, s.impl());
508 }
509
510 inline JSString* jsSubstring(ExecState* exec, JSString* s, unsigned offset, unsigned length)
511 {
512     ASSERT(offset <= static_cast<unsigned>(s->length()));
513     ASSERT(length <= static_cast<unsigned>(s->length()));
514     ASSERT(offset + length <= static_cast<unsigned>(s->length()));
515     VM& vm = exec->vm();
516     if (!length)
517         return vm.smallStrings.emptyString();
518     s->value(exec); // For effect. We need to ensure that any string that is used as a substring base is not a rope.
519     return JSRopeString::create(vm, s, offset, length);
520 }
521
522 inline JSString* jsSubstring8(VM* vm, const String& s, unsigned offset, unsigned length)
523 {
524     ASSERT(offset <= static_cast<unsigned>(s.length()));
525     ASSERT(length <= static_cast<unsigned>(s.length()));
526     ASSERT(offset + length <= static_cast<unsigned>(s.length()));
527     if (!length)
528         return vm->smallStrings.emptyString();
529     if (length == 1) {
530         UChar c = s.characterAt(offset);
531         if (c <= maxSingleCharacterString)
532             return vm->smallStrings.singleCharacterString(c);
533     }
534     return JSString::createHasOtherOwner(*vm, StringImpl::createSubstringSharingImpl8(s.impl(), offset, length));
535 }
536
537 inline JSString* jsSubstring(VM* vm, const String& s, unsigned offset, unsigned length)
538 {
539     ASSERT(offset <= static_cast<unsigned>(s.length()));
540     ASSERT(length <= static_cast<unsigned>(s.length()));
541     ASSERT(offset + length <= static_cast<unsigned>(s.length()));
542     if (!length)
543         return vm->smallStrings.emptyString();
544     if (length == 1) {
545         UChar c = s.characterAt(offset);
546         if (c <= maxSingleCharacterString)
547             return vm->smallStrings.singleCharacterString(c);
548     }
549     return JSString::createHasOtherOwner(*vm, StringImpl::createSubstringSharingImpl(s.impl(), offset, length));
550 }
551
552 inline JSString* jsOwnedString(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::createHasOtherOwner(*vm, s.impl());
563 }
564
565 inline JSRopeString* jsStringBuilder(VM* vm)
566 {
567     return JSRopeString::createNull(*vm);
568 }
569
570 inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->vm()); }
571 inline JSString* jsString(ExecState* exec, const String& s) { return jsString(&exec->vm(), s); }
572 inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->vm(), c); }
573 inline JSString* jsSubstring8(ExecState* exec, const String& s, unsigned offset, unsigned length) { return jsSubstring8(&exec->vm(), s, offset, length); }
574 inline JSString* jsSubstring(ExecState* exec, const String& s, unsigned offset, unsigned length) { return jsSubstring(&exec->vm(), s, offset, length); }
575 inline JSString* jsNontrivialString(ExecState* exec, const String& s) { return jsNontrivialString(&exec->vm(), s); }
576 inline JSString* jsNontrivialString(ExecState* exec, String&& s) { return jsNontrivialString(&exec->vm(), WTF::move(s)); }
577 inline JSString* jsOwnedString(ExecState* exec, const String& s) { return jsOwnedString(&exec->vm(), s); }
578
579 JS_EXPORT_PRIVATE JSString* jsStringWithCacheSlowCase(VM&, StringImpl&);
580
581 ALWAYS_INLINE JSString* jsStringWithCache(ExecState* exec, const String& s)
582 {
583     VM& vm = exec->vm();
584     StringImpl* stringImpl = s.impl();
585     if (!stringImpl || !stringImpl->length())
586         return jsEmptyString(&vm);
587
588     if (stringImpl->length() == 1) {
589         UChar singleCharacter = (*stringImpl)[0u];
590         if (singleCharacter <= maxSingleCharacterString)
591             return vm.smallStrings.singleCharacterString(static_cast<unsigned char>(singleCharacter));
592     }
593
594     if (JSString* lastCachedString = vm.lastCachedString.get()) {
595         if (lastCachedString->tryGetValueImpl() == stringImpl)
596             return lastCachedString;
597     }
598
599     return jsStringWithCacheSlowCase(vm, *stringImpl);
600 }
601
602 ALWAYS_INLINE JSString* jsStringWithCache(ExecState* exec, const AtomicString& s)
603 {
604     return jsStringWithCache(exec, s.string());
605 }
606
607 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
608 {
609     if (propertyName == exec->propertyNames().length) {
610         slot.setValue(this, DontEnum | DontDelete | ReadOnly, jsNumber(m_length));
611         return true;
612     }
613
614     Optional<uint32_t> index = parseIndex(propertyName);
615     if (index && index.value() < m_length) {
616         slot.setValue(this, DontDelete | ReadOnly, getIndex(exec, index.value()));
617         return true;
618     }
619
620     return false;
621 }
622
623 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
624 {
625     if (propertyName < m_length) {
626         slot.setValue(this, DontDelete | ReadOnly, getIndex(exec, propertyName));
627         return true;
628     }
629
630     return false;
631 }
632
633 inline bool isJSString(JSValue v) { return v.isCell() && v.asCell()->type() == StringType; }
634
635 // --- JSValue inlines ----------------------------
636
637 inline bool JSValue::toBoolean(ExecState* exec) const
638 {
639     if (isInt32())
640         return asInt32();
641     if (isDouble())
642         return asDouble() > 0.0 || asDouble() < 0.0; // false for NaN
643     if (isCell())
644         return asCell()->toBoolean(exec);
645     return isTrue(); // false, null, and undefined all convert to false.
646 }
647
648 inline JSString* JSValue::toString(ExecState* exec) const
649 {
650     if (isString())
651         return jsCast<JSString*>(asCell());
652     return toStringSlowCase(exec);
653 }
654
655 inline String JSValue::toWTFString(ExecState* exec) const
656 {
657     if (isString())
658         return static_cast<JSString*>(asCell())->value(exec);
659     return toWTFStringSlowCase(exec);
660 }
661
662 ALWAYS_INLINE String inlineJSValueNotStringtoString(const JSValue& value, ExecState* exec)
663 {
664     VM& vm = exec->vm();
665     if (value.isInt32())
666         return vm.numericStrings.add(value.asInt32());
667     if (value.isDouble())
668         return vm.numericStrings.add(value.asDouble());
669     if (value.isTrue())
670         return vm.propertyNames->trueKeyword.string();
671     if (value.isFalse())
672         return vm.propertyNames->falseKeyword.string();
673     if (value.isNull())
674         return vm.propertyNames->nullKeyword.string();
675     if (value.isUndefined())
676         return vm.propertyNames->undefinedKeyword.string();
677     return value.toString(exec)->value(exec);
678 }
679
680 ALWAYS_INLINE String JSValue::toWTFStringInline(ExecState* exec) const
681 {
682     if (isString())
683         return static_cast<JSString*>(asCell())->value(exec);
684
685     return inlineJSValueNotStringtoString(*this, exec);
686 }
687
688 ALWAYS_INLINE StringView JSRopeString::view(ExecState* exec) const
689 {
690     if (isSubstring()) {
691         if (is8Bit())
692             return StringView(substringBase()->m_value.characters8() + substringOffset(), m_length);
693         return StringView(substringBase()->m_value.characters16() + substringOffset(), m_length);
694     }
695     resolveRope(exec);
696     return StringView(m_value);
697 }
698
699 ALWAYS_INLINE StringView JSString::view(ExecState* exec) const
700 {
701     if (isRope())
702         return static_cast<const JSRopeString*>(this)->view(exec);
703     return StringView(m_value);
704 }
705
706 } // namespace JSC
707
708 #endif // JSString_h