DFG implementation of op_strcat should inline rope allocations
[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 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 #include "CallFrame.h"
26 #include "CommonIdentifiers.h"
27 #include "Identifier.h"
28 #include "PropertyDescriptor.h"
29 #include "PropertySlot.h"
30 #include "Structure.h"
31
32 namespace JSC {
33
34     class JSString;
35     class JSRopeString;
36     class LLIntOffsetsExtractor;
37
38     JSString* jsEmptyString(JSGlobalData*);
39     JSString* jsEmptyString(ExecState*);
40     JSString* jsString(JSGlobalData*, const String&); // returns empty string if passed null string
41     JSString* jsString(ExecState*, const String&); // returns empty string if passed null string
42
43     JSString* jsSingleCharacterString(JSGlobalData*, UChar);
44     JSString* jsSingleCharacterString(ExecState*, UChar);
45     JSString* jsSingleCharacterSubstring(ExecState*, const String&, unsigned offset);
46     JSString* jsSubstring(JSGlobalData*, const String&, unsigned offset, unsigned length);
47     JSString* jsSubstring(ExecState*, const String&, unsigned offset, unsigned length);
48
49     // Non-trivial strings are two or more characters long.
50     // These functions are faster than just calling jsString.
51     JSString* jsNontrivialString(JSGlobalData*, const String&);
52     JSString* jsNontrivialString(ExecState*, const String&);
53
54     // Should be used for strings that are owned by an object that will
55     // likely outlive the JSValue this makes, such as the parse tree or a
56     // DOM object that contains a String
57     JSString* jsOwnedString(JSGlobalData*, const String&);
58     JSString* jsOwnedString(ExecState*, const String&);
59
60     JSRopeString* jsStringBuilder(JSGlobalData*);
61
62     class JSString : public JSCell {
63     public:
64         friend class JIT;
65         friend class JSGlobalData;
66         friend class SpecializedThunkJIT;
67         friend class JSRopeString;
68         friend class MarkStack;
69         friend class SlotVisitor;
70         friend struct ThunkHelpers;
71
72         typedef JSCell Base;
73
74         static const bool needsDestruction = true;
75         static const bool hasImmortalStructure = true;
76         static void destroy(JSCell*);
77
78     private:
79         JSString(JSGlobalData& globalData, PassRefPtr<StringImpl> value)
80             : JSCell(globalData, globalData.stringStructure.get())
81             , m_flags(0)
82             , m_value(value)
83         {
84         }
85
86         JSString(JSGlobalData& globalData)
87             : JSCell(globalData, globalData.stringStructure.get())
88             , m_flags(0)
89         {
90         }
91
92         void finishCreation(JSGlobalData& globalData, size_t length)
93         {
94             ASSERT(!m_value.isNull());
95             Base::finishCreation(globalData);
96             m_length = length;
97             setIs8Bit(m_value.impl()->is8Bit());
98             globalData.m_newStringsSinceLastHashConst++;
99         }
100
101         void finishCreation(JSGlobalData& globalData, size_t length, size_t cost)
102         {
103             ASSERT(!m_value.isNull());
104             Base::finishCreation(globalData);
105             m_length = length;
106             setIs8Bit(m_value.impl()->is8Bit());
107             Heap::heap(this)->reportExtraMemoryCost(cost);
108             globalData.m_newStringsSinceLastHashConst++;
109         }
110
111     protected:
112         void finishCreation(JSGlobalData& globalData)
113         {
114             Base::finishCreation(globalData);
115             m_length = 0;
116             setIs8Bit(true);
117             globalData.m_newStringsSinceLastHashConst++;
118         }
119         
120     public:
121         static JSString* create(JSGlobalData& globalData, PassRefPtr<StringImpl> value)
122         {
123             ASSERT(value);
124             size_t length = value->length();
125             size_t cost = value->cost();
126             JSString* newString = new (NotNull, allocateCell<JSString>(globalData.heap)) JSString(globalData, value);
127             newString->finishCreation(globalData, length, cost);
128             return newString;
129         }
130         static JSString* createHasOtherOwner(JSGlobalData& globalData, PassRefPtr<StringImpl> value)
131         {
132             ASSERT(value);
133             size_t length = value->length();
134             JSString* newString = new (NotNull, allocateCell<JSString>(globalData.heap)) JSString(globalData, value);
135             newString->finishCreation(globalData, length);
136             return newString;
137         }
138
139         const String& value(ExecState*) const;
140         const String& tryGetValue() const;
141         unsigned length() { return m_length; }
142
143         JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const;
144         JS_EXPORT_PRIVATE bool toBoolean() const;
145         bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const;
146         JSObject* toObject(ExecState*, JSGlobalObject*) const;
147         double toNumber(ExecState*) const;
148         
149         bool getStringPropertySlot(ExecState*, PropertyName, PropertySlot&);
150         bool getStringPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
151         bool getStringPropertyDescriptor(ExecState*, PropertyName, PropertyDescriptor&);
152
153         bool canGetIndex(unsigned i) { return i < m_length; }
154         JSString* getIndex(ExecState*, unsigned);
155
156         static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto)
157         {
158             return Structure::create(globalData, globalObject, proto, TypeInfo(StringType, OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero), &s_info);
159         }
160
161         static size_t offsetOfLength() { return OBJECT_OFFSETOF(JSString, m_length); }
162         static size_t offsetOfFlags() { return OBJECT_OFFSETOF(JSString, m_flags); }
163         static size_t offsetOfValue() { return OBJECT_OFFSETOF(JSString, m_value); }
164
165         static JS_EXPORTDATA const ClassInfo s_info;
166
167         static void visitChildren(JSCell*, SlotVisitor&);
168
169         enum {
170             HashConstLock = 1u << 2,
171             IsHashConstSingleton = 1u << 1,
172             Is8Bit = 1u
173         };
174
175     protected:
176         friend class JSValue;
177         
178         bool isRope() const { return m_value.isNull(); }
179         bool is8Bit() const { return m_flags & Is8Bit; }
180         void setIs8Bit(bool flag)
181         {
182             if (flag)
183                 m_flags |= Is8Bit;
184             else
185                 m_flags &= ~Is8Bit;
186         }
187         bool shouldTryHashConst();
188         bool isHashConstSingleton() const { return m_flags & IsHashConstSingleton; }
189         void clearHashConstSingleton() { m_flags &= ~IsHashConstSingleton; }
190         void setHashConstSingleton() { m_flags |= IsHashConstSingleton; }
191         bool tryHashConstLock();
192         void releaseHashConstLock();
193
194         unsigned m_flags;
195         
196         // A string is represented either by a String or a rope of fibers.
197         unsigned m_length;
198         mutable String m_value;
199
200     private:
201         friend class LLIntOffsetsExtractor;
202         
203         static JSObject* toThisObject(JSCell*, ExecState*);
204
205         // Actually getPropertySlot, not getOwnPropertySlot (see JSCell).
206         static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&);
207         static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&);
208
209         String& string() { ASSERT(!isRope()); return m_value; }
210
211         friend JSValue jsString(ExecState*, JSString*, JSString*);
212         friend JSString* jsSubstring(ExecState*, JSString*, unsigned offset, unsigned length);
213     };
214
215     class JSRopeString : public JSString {
216         friend class JSString;
217
218         friend JSRopeString* jsStringBuilder(JSGlobalData*);
219
220         class RopeBuilder {
221         public:
222             RopeBuilder(JSGlobalData& globalData)
223             : m_globalData(globalData)
224             , m_jsString(jsStringBuilder(&globalData))
225             , m_index(0)
226             {
227             }
228
229             void append(JSString* jsString)
230             {
231                 if (m_index == JSRopeString::s_maxInternalRopeLength)
232                     expand();
233                 m_jsString->append(m_globalData, m_index++, jsString);
234             }
235
236             JSRopeString* release()
237             {
238                 JSRopeString* tmp = m_jsString;
239                 m_jsString = 0;
240                 return tmp;
241             }
242
243             unsigned length() { return m_jsString->m_length; }
244
245         private:
246             void expand();
247             
248             JSGlobalData& m_globalData;
249             JSRopeString* m_jsString;
250             size_t m_index;
251         };
252         
253     private:
254         JSRopeString(JSGlobalData& globalData)
255             : JSString(globalData)
256         {
257         }
258
259         void finishCreation(JSGlobalData& globalData, JSString* s1, JSString* s2)
260         {
261             Base::finishCreation(globalData);
262             m_length = s1->length() + s2->length();
263             setIs8Bit(s1->is8Bit() && s2->is8Bit());
264             m_fibers[0].set(globalData, this, s1);
265             m_fibers[1].set(globalData, this, s2);
266         }
267         
268         void finishCreation(JSGlobalData& globalData, JSString* s1, JSString* s2, JSString* s3)
269         {
270             Base::finishCreation(globalData);
271             m_length = s1->length() + s2->length() + s3->length();
272             setIs8Bit(s1->is8Bit() && s2->is8Bit() &&  s3->is8Bit());
273             m_fibers[0].set(globalData, this, s1);
274             m_fibers[1].set(globalData, this, s2);
275             m_fibers[2].set(globalData, this, s3);
276         }
277
278         void finishCreation(JSGlobalData& globalData)
279         {
280             JSString::finishCreation(globalData);
281         }
282
283         void append(JSGlobalData& globalData, size_t index, JSString* jsString)
284         {
285             m_fibers[index].set(globalData, this, jsString);
286             m_length += jsString->m_length;
287             setIs8Bit(is8Bit() && jsString->is8Bit());
288         }
289
290         static JSRopeString* createNull(JSGlobalData& globalData)
291         {
292             JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(globalData.heap)) JSRopeString(globalData);
293             newString->finishCreation(globalData);
294             return newString;
295         }
296
297     public:
298         static JSString* create(JSGlobalData& globalData, JSString* s1, JSString* s2)
299         {
300             JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(globalData.heap)) JSRopeString(globalData);
301             newString->finishCreation(globalData, s1, s2);
302             return newString;
303         }
304         static JSString* create(JSGlobalData& globalData, JSString* s1, JSString* s2, JSString* s3)
305         {
306             JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(globalData.heap)) JSRopeString(globalData);
307             newString->finishCreation(globalData, s1, s2, s3);
308             return newString;
309         }
310
311         void visitFibers(SlotVisitor&);
312         
313         static ptrdiff_t offsetOfFibers() { return OBJECT_OFFSETOF(JSRopeString, m_fibers); }
314
315         static const unsigned s_maxInternalRopeLength = 3;
316         
317     private:
318         friend JSValue jsString(ExecState*, Register*, unsigned);
319         friend JSValue jsStringFromArguments(ExecState*, JSValue);
320
321         JS_EXPORT_PRIVATE void resolveRope(ExecState*) const;
322         void resolveRopeSlowCase8(LChar*) const;
323         void resolveRopeSlowCase(UChar*) const;
324         void outOfMemory(ExecState*) const;
325         
326         JSString* getIndexSlowCase(ExecState*, unsigned);
327
328         mutable FixedArray<WriteBarrier<JSString>, s_maxInternalRopeLength> m_fibers;
329     };
330
331     JSString* asString(JSValue);
332
333     inline JSString* asString(JSValue value)
334     {
335         ASSERT(value.asCell()->isString());
336         return jsCast<JSString*>(value.asCell());
337     }
338
339     inline JSString* jsEmptyString(JSGlobalData* globalData)
340     {
341         return globalData->smallStrings.emptyString();
342     }
343
344     ALWAYS_INLINE JSString* jsSingleCharacterString(JSGlobalData* globalData, UChar c)
345     {
346         if (c <= maxSingleCharacterString)
347             return globalData->smallStrings.singleCharacterString(globalData, c);
348         return JSString::create(*globalData, String(&c, 1).impl());
349     }
350
351     ALWAYS_INLINE JSString* jsSingleCharacterSubstring(ExecState* exec, const String& s, unsigned offset)
352     {
353         JSGlobalData* globalData = &exec->globalData();
354         ASSERT(offset < static_cast<unsigned>(s.length()));
355         UChar c = s.characterAt(offset);
356         if (c <= maxSingleCharacterString)
357             return globalData->smallStrings.singleCharacterString(globalData, c);
358         return JSString::create(*globalData, StringImpl::create(s.impl(), offset, 1));
359     }
360
361     inline JSString* jsNontrivialString(JSGlobalData* globalData, const String& s)
362     {
363         ASSERT(s.length() > 1);
364         return JSString::create(*globalData, s.impl());
365     }
366
367     inline const String& JSString::value(ExecState* exec) const
368     {
369         if (isRope())
370             static_cast<const JSRopeString*>(this)->resolveRope(exec);
371         return m_value;
372     }
373
374     inline const String& JSString::tryGetValue() const
375     {
376         if (isRope())
377             static_cast<const JSRopeString*>(this)->resolveRope(0);
378         return m_value;
379     }
380
381     inline JSString* JSString::getIndex(ExecState* exec, unsigned i)
382     {
383         ASSERT(canGetIndex(i));
384         if (isRope())
385             return static_cast<JSRopeString*>(this)->getIndexSlowCase(exec, i);
386         ASSERT(i < m_value.length());
387         return jsSingleCharacterSubstring(exec, m_value, i);
388     }
389
390     inline JSString* jsString(JSGlobalData* globalData, const String& s)
391     {
392         int size = s.length();
393         if (!size)
394             return globalData->smallStrings.emptyString();
395         if (size == 1) {
396             UChar c = s.characterAt(0);
397             if (c <= maxSingleCharacterString)
398                 return globalData->smallStrings.singleCharacterString(globalData, c);
399         }
400         return JSString::create(*globalData, s.impl());
401     }
402
403     inline JSString* jsSubstring(ExecState* exec, JSString* s, unsigned offset, unsigned length)
404     {
405         ASSERT(offset <= static_cast<unsigned>(s->length()));
406         ASSERT(length <= static_cast<unsigned>(s->length()));
407         ASSERT(offset + length <= static_cast<unsigned>(s->length()));
408         JSGlobalData* globalData = &exec->globalData();
409         if (!length)
410             return globalData->smallStrings.emptyString();
411         return jsSubstring(globalData, s->value(exec), offset, length);
412     }
413
414     inline JSString* jsSubstring8(JSGlobalData* globalData, const String& s, unsigned offset, unsigned length)
415     {
416         ASSERT(offset <= static_cast<unsigned>(s.length()));
417         ASSERT(length <= static_cast<unsigned>(s.length()));
418         ASSERT(offset + length <= static_cast<unsigned>(s.length()));
419         if (!length)
420             return globalData->smallStrings.emptyString();
421         if (length == 1) {
422             UChar c = s.characterAt(offset);
423             if (c <= maxSingleCharacterString)
424                 return globalData->smallStrings.singleCharacterString(globalData, c);
425         }
426         return JSString::createHasOtherOwner(*globalData, StringImpl::create8(s.impl(), offset, length));
427     }
428
429     inline JSString* jsSubstring(JSGlobalData* globalData, const String& s, unsigned offset, unsigned length)
430     {
431         ASSERT(offset <= static_cast<unsigned>(s.length()));
432         ASSERT(length <= static_cast<unsigned>(s.length()));
433         ASSERT(offset + length <= static_cast<unsigned>(s.length()));
434         if (!length)
435             return globalData->smallStrings.emptyString();
436         if (length == 1) {
437             UChar c = s.characterAt(offset);
438             if (c <= maxSingleCharacterString)
439                 return globalData->smallStrings.singleCharacterString(globalData, c);
440         }
441         return JSString::createHasOtherOwner(*globalData, StringImpl::create(s.impl(), offset, length));
442     }
443
444     inline JSString* jsOwnedString(JSGlobalData* globalData, const String& s)
445     {
446         int size = s.length();
447         if (!size)
448             return globalData->smallStrings.emptyString();
449         if (size == 1) {
450             UChar c = s.characterAt(0);
451             if (c <= maxSingleCharacterString)
452                 return globalData->smallStrings.singleCharacterString(globalData, c);
453         }
454         return JSString::createHasOtherOwner(*globalData, s.impl());
455     }
456
457     inline JSRopeString* jsStringBuilder(JSGlobalData* globalData)
458     {
459         return JSRopeString::createNull(*globalData);
460     }
461
462     inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->globalData()); }
463     inline JSString* jsString(ExecState* exec, const String& s) { return jsString(&exec->globalData(), s); }
464     inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->globalData(), c); }
465     inline JSString* jsSubstring8(ExecState* exec, const String& s, unsigned offset, unsigned length) { return jsSubstring8(&exec->globalData(), s, offset, length); }
466     inline JSString* jsSubstring(ExecState* exec, const String& s, unsigned offset, unsigned length) { return jsSubstring(&exec->globalData(), s, offset, length); }
467     inline JSString* jsNontrivialString(ExecState* exec, const String& s) { return jsNontrivialString(&exec->globalData(), s); }
468     inline JSString* jsOwnedString(ExecState* exec, const String& s) { return jsOwnedString(&exec->globalData(), s); }
469
470     ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
471     {
472         if (propertyName == exec->propertyNames().length) {
473             slot.setValue(jsNumber(m_length));
474             return true;
475         }
476
477         unsigned i = propertyName.asIndex();
478         if (i < m_length) {
479             ASSERT(i != PropertyName::NotAnIndex); // No need for an explicit check, the above test would always fail!
480             slot.setValue(getIndex(exec, i));
481             return true;
482         }
483
484         return false;
485     }
486         
487     ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
488     {
489         if (propertyName < m_length) {
490             slot.setValue(getIndex(exec, propertyName));
491             return true;
492         }
493
494         return false;
495     }
496
497     inline bool isJSString(JSValue v) { return v.isCell() && v.asCell()->classInfo() == &JSString::s_info; }
498
499     // --- JSValue inlines ----------------------------
500     
501     inline bool JSValue::toBoolean(ExecState* exec) const
502     {
503         if (isInt32())
504             return asInt32();
505         if (isDouble())
506             return asDouble() > 0.0 || asDouble() < 0.0; // false for NaN
507         if (isCell())
508             return asCell()->toBoolean(exec);
509         return isTrue(); // false, null, and undefined all convert to false.
510     }
511
512     inline JSString* JSValue::toString(ExecState* exec) const
513     {
514         if (isString())
515             return jsCast<JSString*>(asCell());
516         return toStringSlowCase(exec);
517     }
518
519     inline String JSValue::toWTFString(ExecState* exec) const
520     {
521         if (isString())
522             return static_cast<JSString*>(asCell())->value(exec);
523         return toWTFStringSlowCase(exec);
524     }
525
526     ALWAYS_INLINE String inlineJSValueNotStringtoString(const JSValue& value, ExecState* exec)
527     {
528         JSGlobalData& globalData = exec->globalData();
529         if (value.isInt32())
530             return globalData.numericStrings.add(value.asInt32());
531         if (value.isDouble())
532             return globalData.numericStrings.add(value.asDouble());
533         if (value.isTrue())
534             return globalData.propertyNames->trueKeyword.string();
535         if (value.isFalse())
536             return globalData.propertyNames->falseKeyword.string();
537         if (value.isNull())
538             return globalData.propertyNames->nullKeyword.string();
539         if (value.isUndefined())
540             return globalData.propertyNames->undefinedKeyword.string();
541         return value.toString(exec)->value(exec);
542     }
543
544     ALWAYS_INLINE String JSValue::toWTFStringInline(ExecState* exec) const
545     {
546         if (isString())
547             return static_cast<JSString*>(asCell())->value(exec);
548
549         return inlineJSValueNotStringtoString(*this, exec);
550     }
551
552 } // namespace JSC
553
554 #endif // JSString_h