Fast path for jsStringWithCache() when asked for the same string repeatedly.
[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
34 namespace JSC {
35
36     class JSString;
37     class JSRopeString;
38     class LLIntOffsetsExtractor;
39
40     JSString* jsEmptyString(VM*);
41     JSString* jsEmptyString(ExecState*);
42     JSString* jsString(VM*, const String&); // returns empty string if passed null string
43     JSString* jsString(ExecState*, const String&); // returns empty string if passed null string
44
45     JSString* jsSingleCharacterString(VM*, UChar);
46     JSString* jsSingleCharacterString(ExecState*, UChar);
47     JSString* jsSingleCharacterSubstring(ExecState*, const String&, unsigned offset);
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
56     // Should be used for strings that are owned by an object that will
57     // likely outlive the JSValue this makes, such as the parse tree or a
58     // DOM object that contains a String
59     JSString* jsOwnedString(VM*, const String&);
60     JSString* jsOwnedString(ExecState*, const String&);
61
62     JSRopeString* jsStringBuilder(VM*);
63
64     class JSString : public JSCell {
65     public:
66         friend class JIT;
67         friend class VM;
68         friend class SpecializedThunkJIT;
69         friend class JSRopeString;
70         friend class MarkStack;
71         friend class SlotVisitor;
72         friend struct ThunkHelpers;
73
74         typedef JSCell Base;
75
76         static const bool needsDestruction = true;
77         static const bool hasImmortalStructure = true;
78         static void destroy(JSCell*);
79
80     private:
81         JSString(VM& vm, PassRefPtr<StringImpl> value)
82             : JSCell(vm, vm.stringStructure.get())
83             , m_flags(0)
84             , m_value(value)
85         {
86         }
87
88         JSString(VM& vm)
89             : JSCell(vm, vm.stringStructure.get())
90             , m_flags(0)
91         {
92         }
93
94         void finishCreation(VM& vm, size_t length)
95         {
96             ASSERT(!m_value.isNull());
97             Base::finishCreation(vm);
98             m_length = length;
99             setIs8Bit(m_value.impl()->is8Bit());
100             vm.m_newStringsSinceLastHashCons++;
101         }
102
103         void finishCreation(VM& vm, size_t length, size_t cost)
104         {
105             ASSERT(!m_value.isNull());
106             Base::finishCreation(vm);
107             m_length = length;
108             setIs8Bit(m_value.impl()->is8Bit());
109             Heap::heap(this)->reportExtraMemoryCost(cost);
110             vm.m_newStringsSinceLastHashCons++;
111         }
112
113     protected:
114         void finishCreation(VM& vm)
115         {
116             Base::finishCreation(vm);
117             m_length = 0;
118             setIs8Bit(true);
119             vm.m_newStringsSinceLastHashCons++;
120         }
121             
122     public:
123         static JSString* create(VM& vm, PassRefPtr<StringImpl> value)
124         {
125             ASSERT(value);
126             int32_t length = value->length();
127             RELEASE_ASSERT(length >= 0);
128             size_t cost = value->cost();
129             JSString* newString = new (NotNull, allocateCell<JSString>(vm.heap)) JSString(vm, value);
130             newString->finishCreation(vm, length, cost);
131             return newString;
132         }
133         static JSString* createHasOtherOwner(VM& vm, PassRefPtr<StringImpl> value)
134         {
135             ASSERT(value);
136             size_t length = value->length();
137             JSString* newString = new (NotNull, allocateCell<JSString>(vm.heap)) JSString(vm, value);
138             newString->finishCreation(vm, length);
139             return newString;
140         }
141
142         Identifier toIdentifier(ExecState*) const;
143         AtomicString toAtomicString(ExecState*) const;
144         AtomicStringImpl* toExistingAtomicString(ExecState*) const;
145         const String& value(ExecState*) const;
146         const String& tryGetValue() const;
147         const StringImpl* tryGetValueImpl() const;
148         unsigned length() const { return m_length; }
149
150         JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const;
151         JS_EXPORT_PRIVATE bool toBoolean() const;
152         bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const;
153         JSObject* toObject(ExecState*, JSGlobalObject*) const;
154         double toNumber(ExecState*) const;
155             
156         bool getStringPropertySlot(ExecState*, PropertyName, PropertySlot&);
157         bool getStringPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
158         bool getStringPropertyDescriptor(ExecState*, PropertyName, PropertyDescriptor&);
159
160         bool canGetIndex(unsigned i) { return i < m_length; }
161         JSString* getIndex(ExecState*, unsigned);
162
163         static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto)
164         {
165             return Structure::create(vm, globalObject, proto, TypeInfo(StringType, StructureFlags), info());
166         }
167
168         static size_t offsetOfLength() { return OBJECT_OFFSETOF(JSString, m_length); }
169         static size_t offsetOfFlags() { return OBJECT_OFFSETOF(JSString, m_flags); }
170         static size_t offsetOfValue() { return OBJECT_OFFSETOF(JSString, m_value); }
171
172         DECLARE_EXPORT_INFO;
173
174         static void dumpToStream(const JSCell*, PrintStream&);
175         static void visitChildren(JSCell*, SlotVisitor&);
176
177         enum {
178             HashConsLock = 1u << 2,
179             IsHashConsSingleton = 1u << 1,
180             Is8Bit = 1u
181         };
182
183     protected:
184         static const unsigned StructureFlags = OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | StructureIsImmortal;
185
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)
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         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 : public JSString {
222         friend class JSString;
223
224         friend JSRopeString* jsStringBuilder(VM*);
225
226         class RopeBuilder {
227         public:
228             RopeBuilder(VM& vm)
229                 : m_vm(vm)
230                 , m_jsString(jsStringBuilder(&vm))
231                 , m_index(0)
232             {
233             }
234
235             bool append(JSString* jsString)
236             {
237                 if (m_index == JSRopeString::s_maxInternalRopeLength)
238                     expand();
239                 if (static_cast<int32_t>(m_jsString->length() + jsString->length()) < 0) {
240                     m_jsString = nullptr;
241                     return false;
242                 }
243                 m_jsString->append(m_vm, m_index++, jsString);
244                 return true;
245             }
246
247             JSRopeString* release()
248             {
249                 RELEASE_ASSERT(m_jsString);
250                 JSRopeString* tmp = m_jsString;
251                 m_jsString = 0;
252                 return tmp;
253             }
254
255             unsigned length() const { return m_jsString->m_length; }
256
257         private:
258             void expand();
259                 
260             VM& m_vm;
261             JSRopeString* m_jsString;
262             size_t m_index;
263         };
264             
265     private:
266         JSRopeString(VM& vm)
267             : JSString(vm)
268         {
269         }
270
271         void finishCreation(VM& vm, JSString* s1, JSString* s2)
272         {
273             Base::finishCreation(vm);
274             m_length = s1->length() + s2->length();
275             setIs8Bit(s1->is8Bit() && s2->is8Bit());
276             m_fibers[0].set(vm, this, s1);
277             m_fibers[1].set(vm, this, s2);
278         }
279             
280         void finishCreation(VM& vm, JSString* s1, JSString* s2, JSString* s3)
281         {
282             Base::finishCreation(vm);
283             m_length = s1->length() + s2->length() + s3->length();
284             setIs8Bit(s1->is8Bit() && s2->is8Bit() &&  s3->is8Bit());
285             m_fibers[0].set(vm, this, s1);
286             m_fibers[1].set(vm, this, s2);
287             m_fibers[2].set(vm, this, s3);
288         }
289
290         void finishCreation(VM& vm)
291         {
292             JSString::finishCreation(vm);
293         }
294
295         void append(VM& vm, size_t index, JSString* jsString)
296         {
297             m_fibers[index].set(vm, this, jsString);
298             m_length += jsString->m_length;
299             RELEASE_ASSERT(static_cast<int32_t>(m_length) >= 0);
300             setIs8Bit(is8Bit() && jsString->is8Bit());
301         }
302
303         static JSRopeString* createNull(VM& vm)
304         {
305             JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
306             newString->finishCreation(vm);
307             return newString;
308         }
309
310     public:
311         static JSString* create(VM& vm, JSString* s1, JSString* s2)
312         {
313             JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
314             newString->finishCreation(vm, s1, s2);
315             return newString;
316         }
317         static JSString* create(VM& vm, JSString* s1, JSString* s2, JSString* s3)
318         {
319             JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
320             newString->finishCreation(vm, s1, s2, s3);
321             return newString;
322         }
323
324         void visitFibers(SlotVisitor&);
325             
326         static ptrdiff_t offsetOfFibers() { return OBJECT_OFFSETOF(JSRopeString, m_fibers); }
327
328         static const unsigned s_maxInternalRopeLength = 3;
329             
330     private:
331         friend JSValue jsStringFromRegisterArray(ExecState*, Register*, unsigned);
332         friend JSValue jsStringFromArguments(ExecState*, JSValue);
333
334         JS_EXPORT_PRIVATE void resolveRope(ExecState*) const;
335         JS_EXPORT_PRIVATE void resolveRopeToAtomicString(ExecState*) const;
336         JS_EXPORT_PRIVATE AtomicStringImpl* resolveRopeToExistingAtomicString(ExecState*) const;
337         void resolveRopeSlowCase8(LChar*) const;
338         void resolveRopeSlowCase(UChar*) const;
339         void outOfMemory(ExecState*) const;
340         void resolveRopeInternal8(LChar*) const;
341         void resolveRopeInternal16(UChar*) const;
342         void clearFibers() const;
343             
344         JS_EXPORT_PRIVATE JSString* getIndexSlowCase(ExecState*, unsigned);
345
346         mutable std::array<WriteBarrier<JSString>, s_maxInternalRopeLength> m_fibers;
347     };
348
349
350     inline const StringImpl* JSString::tryGetValueImpl() const
351     {
352         return m_value.impl();
353     }
354
355     JSString* asString(JSValue);
356
357     inline JSString* asString(JSValue value)
358     {
359         ASSERT(value.asCell()->isString());
360         return jsCast<JSString*>(value.asCell());
361     }
362
363     inline JSString* jsEmptyString(VM* vm)
364     {
365         return vm->smallStrings.emptyString();
366     }
367
368     ALWAYS_INLINE JSString* jsSingleCharacterString(VM* vm, UChar c)
369     {
370         if (c <= maxSingleCharacterString)
371             return vm->smallStrings.singleCharacterString(c);
372         return JSString::create(*vm, String(&c, 1).impl());
373     }
374
375     ALWAYS_INLINE JSString* jsSingleCharacterSubstring(ExecState* exec, const String& s, unsigned offset)
376     {
377         VM* vm = &exec->vm();
378         ASSERT(offset < static_cast<unsigned>(s.length()));
379         UChar c = s.characterAt(offset);
380         if (c <= maxSingleCharacterString)
381             return vm->smallStrings.singleCharacterString(c);
382         return JSString::create(*vm, StringImpl::createSubstringSharingImpl(s.impl(), offset, 1));
383     }
384
385     inline JSString* jsNontrivialString(VM* vm, const String& s)
386     {
387         ASSERT(s.length() > 1);
388         return JSString::create(*vm, s.impl());
389     }
390
391     ALWAYS_INLINE Identifier JSString::toIdentifier(ExecState* exec) const
392     {
393         return Identifier(exec, toAtomicString(exec));
394     }
395
396     ALWAYS_INLINE AtomicString JSString::toAtomicString(ExecState* exec) const
397     {
398         if (isRope())
399             static_cast<const JSRopeString*>(this)->resolveRopeToAtomicString(exec);
400         return AtomicString(m_value);
401     }
402
403     ALWAYS_INLINE AtomicStringImpl* JSString::toExistingAtomicString(ExecState* exec) const
404     {
405         if (isRope())
406             return static_cast<const JSRopeString*>(this)->resolveRopeToExistingAtomicString(exec);
407         if (m_value.impl()->isAtomic())
408             return static_cast<AtomicStringImpl*>(m_value.impl());
409         if (AtomicStringImpl* existingAtomicString = AtomicString::find(m_value.impl())) {
410             m_value = *existingAtomicString;
411             return existingAtomicString;
412         }
413         return nullptr;
414     }
415
416     inline const String& JSString::value(ExecState* exec) const
417     {
418         if (isRope())
419             static_cast<const JSRopeString*>(this)->resolveRope(exec);
420         return m_value;
421     }
422
423     inline const String& JSString::tryGetValue() const
424     {
425         if (isRope())
426             static_cast<const JSRopeString*>(this)->resolveRope(0);
427         return m_value;
428     }
429
430     inline JSString* JSString::getIndex(ExecState* exec, unsigned i)
431     {
432         ASSERT(canGetIndex(i));
433         if (isRope())
434             return static_cast<JSRopeString*>(this)->getIndexSlowCase(exec, i);
435         ASSERT(i < m_value.length());
436         return jsSingleCharacterSubstring(exec, m_value, i);
437     }
438
439     inline JSString* jsString(VM* vm, const String& s)
440     {
441         int size = s.length();
442         if (!size)
443             return vm->smallStrings.emptyString();
444         if (size == 1) {
445             UChar c = s.characterAt(0);
446             if (c <= maxSingleCharacterString)
447                 return vm->smallStrings.singleCharacterString(c);
448         }
449         return JSString::create(*vm, s.impl());
450     }
451
452     inline JSString* jsSubstring(ExecState* exec, JSString* s, unsigned offset, unsigned length)
453     {
454         ASSERT(offset <= static_cast<unsigned>(s->length()));
455         ASSERT(length <= static_cast<unsigned>(s->length()));
456         ASSERT(offset + length <= static_cast<unsigned>(s->length()));
457         VM* vm = &exec->vm();
458         if (!length)
459             return vm->smallStrings.emptyString();
460         return jsSubstring(vm, s->value(exec), offset, length);
461     }
462
463     inline JSString* jsSubstring8(VM* vm, const String& s, unsigned offset, unsigned length)
464     {
465         ASSERT(offset <= static_cast<unsigned>(s.length()));
466         ASSERT(length <= static_cast<unsigned>(s.length()));
467         ASSERT(offset + length <= static_cast<unsigned>(s.length()));
468         if (!length)
469             return vm->smallStrings.emptyString();
470         if (length == 1) {
471             UChar c = s.characterAt(offset);
472             if (c <= maxSingleCharacterString)
473                 return vm->smallStrings.singleCharacterString(c);
474         }
475         return JSString::createHasOtherOwner(*vm, StringImpl::createSubstringSharingImpl8(s.impl(), offset, length));
476     }
477
478     inline JSString* jsSubstring(VM* vm, const String& s, unsigned offset, unsigned length)
479     {
480         ASSERT(offset <= static_cast<unsigned>(s.length()));
481         ASSERT(length <= static_cast<unsigned>(s.length()));
482         ASSERT(offset + length <= static_cast<unsigned>(s.length()));
483         if (!length)
484             return vm->smallStrings.emptyString();
485         if (length == 1) {
486             UChar c = s.characterAt(offset);
487             if (c <= maxSingleCharacterString)
488                 return vm->smallStrings.singleCharacterString(c);
489         }
490         return JSString::createHasOtherOwner(*vm, StringImpl::createSubstringSharingImpl(s.impl(), offset, length));
491     }
492
493     inline JSString* jsOwnedString(VM* vm, const String& s)
494     {
495         int size = s.length();
496         if (!size)
497             return vm->smallStrings.emptyString();
498         if (size == 1) {
499             UChar c = s.characterAt(0);
500             if (c <= maxSingleCharacterString)
501                 return vm->smallStrings.singleCharacterString(c);
502         }
503         return JSString::createHasOtherOwner(*vm, s.impl());
504     }
505
506     inline JSRopeString* jsStringBuilder(VM* vm)
507     {
508         return JSRopeString::createNull(*vm);
509     }
510
511     inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->vm()); }
512     inline JSString* jsString(ExecState* exec, const String& s) { return jsString(&exec->vm(), s); }
513     inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->vm(), c); }
514     inline JSString* jsSubstring8(ExecState* exec, const String& s, unsigned offset, unsigned length) { return jsSubstring8(&exec->vm(), s, offset, length); }
515     inline JSString* jsSubstring(ExecState* exec, const String& s, unsigned offset, unsigned length) { return jsSubstring(&exec->vm(), s, offset, length); }
516     inline JSString* jsNontrivialString(ExecState* exec, const String& s) { return jsNontrivialString(&exec->vm(), s); }
517     inline JSString* jsOwnedString(ExecState* exec, const String& s) { return jsOwnedString(&exec->vm(), s); }
518
519     JS_EXPORT_PRIVATE JSString* jsStringWithCacheSlowCase(VM&, StringImpl&);
520
521     ALWAYS_INLINE JSString* jsStringWithCache(ExecState* exec, const String& s)
522     {
523         VM& vm = exec->vm();
524         StringImpl* stringImpl = s.impl();
525         if (!stringImpl || !stringImpl->length())
526             return jsEmptyString(&vm);
527
528         if (stringImpl->length() == 1) {
529             UChar singleCharacter = (*stringImpl)[0u];
530             if (singleCharacter <= maxSingleCharacterString)
531                 return vm.smallStrings.singleCharacterString(static_cast<unsigned char>(singleCharacter));
532         }
533
534         if (JSString* lastCachedString = vm.lastCachedString.get()) {
535             if (lastCachedString->tryGetValueImpl() == stringImpl)
536                 return lastCachedString;
537         }
538
539         return jsStringWithCacheSlowCase(vm, *stringImpl);
540     }
541
542     ALWAYS_INLINE JSString* jsStringWithCache(ExecState* exec, const AtomicString& s)
543     {
544         return jsStringWithCache(exec, s.string());
545     }
546
547     ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
548     {
549         if (propertyName == exec->propertyNames().length) {
550             slot.setValue(this, DontEnum | DontDelete | ReadOnly, jsNumber(m_length));
551             return true;
552         }
553
554         unsigned i = propertyName.asIndex();
555         if (i < m_length) {
556             ASSERT(i != PropertyName::NotAnIndex); // No need for an explicit check, the above test would always fail!
557             slot.setValue(this, DontDelete | ReadOnly, getIndex(exec, i));
558             return true;
559         }
560
561         return false;
562     }
563             
564     ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
565     {
566         if (propertyName < m_length) {
567             slot.setValue(this, DontDelete | ReadOnly, getIndex(exec, propertyName));
568             return true;
569         }
570
571         return false;
572     }
573
574     inline bool isJSString(JSValue v) { return v.isCell() && v.asCell()->type() == StringType; }
575
576     // --- JSValue inlines ----------------------------
577         
578     inline bool JSValue::toBoolean(ExecState* exec) const
579     {
580         if (isInt32())
581             return asInt32();
582         if (isDouble())
583             return asDouble() > 0.0 || asDouble() < 0.0; // false for NaN
584         if (isCell())
585             return asCell()->toBoolean(exec);
586         return isTrue(); // false, null, and undefined all convert to false.
587     }
588
589     inline JSString* JSValue::toString(ExecState* exec) const
590     {
591         if (isString())
592             return jsCast<JSString*>(asCell());
593         return toStringSlowCase(exec);
594     }
595
596     inline String JSValue::toWTFString(ExecState* exec) const
597     {
598         if (isString())
599             return static_cast<JSString*>(asCell())->value(exec);
600         return toWTFStringSlowCase(exec);
601     }
602
603     ALWAYS_INLINE String inlineJSValueNotStringtoString(const JSValue& value, ExecState* exec)
604     {
605         VM& vm = exec->vm();
606         if (value.isInt32())
607             return vm.numericStrings.add(value.asInt32());
608         if (value.isDouble())
609             return vm.numericStrings.add(value.asDouble());
610         if (value.isTrue())
611             return vm.propertyNames->trueKeyword.string();
612         if (value.isFalse())
613             return vm.propertyNames->falseKeyword.string();
614         if (value.isNull())
615             return vm.propertyNames->nullKeyword.string();
616         if (value.isUndefined())
617             return vm.propertyNames->undefinedKeyword.string();
618         return value.toString(exec)->value(exec);
619     }
620
621     ALWAYS_INLINE String JSValue::toWTFStringInline(ExecState* exec) const
622     {
623         if (isString())
624             return static_cast<JSString*>(asCell())->value(exec);
625
626         return inlineJSValueNotStringtoString(*this, exec);
627     }
628
629 } // namespace JSC
630
631 #endif // JSString_h