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