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