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