tizen beta release
[framework/web/webkit-efl.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
36     JSString* jsEmptyString(JSGlobalData*);
37     JSString* jsEmptyString(ExecState*);
38     JSString* jsString(JSGlobalData*, const UString&); // returns empty string if passed null string
39     JSString* jsString(ExecState*, const UString&); // returns empty string if passed null string
40
41     JSString* jsSingleCharacterString(JSGlobalData*, UChar);
42     JSString* jsSingleCharacterString(ExecState*, UChar);
43     JSString* jsSingleCharacterSubstring(ExecState*, const UString&, unsigned offset);
44     JSString* jsSubstring(JSGlobalData*, const UString&, unsigned offset, unsigned length);
45     JSString* jsSubstring(ExecState*, const UString&, unsigned offset, unsigned length);
46
47     // Non-trivial strings are two or more characters long.
48     // These functions are faster than just calling jsString.
49     JSString* jsNontrivialString(JSGlobalData*, const UString&);
50     JSString* jsNontrivialString(ExecState*, const UString&);
51     JSString* jsNontrivialString(JSGlobalData*, const char*);
52     JSString* jsNontrivialString(ExecState*, const char*);
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 UString
57     JSString* jsOwnedString(JSGlobalData*, const UString&); 
58     JSString* jsOwnedString(ExecState*, const UString&); 
59
60     JSString* jsStringBuilder(JSGlobalData*);
61
62     class JS_EXPORTCLASS JSString : public JSCell {
63     public:
64         friend class JIT;
65         friend class JSGlobalData;
66         friend class SpecializedThunkJIT;
67         friend struct ThunkHelpers;
68         friend JSString* jsStringBuilder(JSGlobalData*);
69
70         typedef JSCell Base;
71
72         class RopeBuilder {
73         public:
74             RopeBuilder(JSGlobalData& globalData)
75                 : m_globalData(globalData)
76                 , m_jsString(jsStringBuilder(&globalData))
77                 , m_index(0)
78             {
79             }
80
81             void append(JSString* jsString)
82             {
83                 if (m_index == JSString::s_maxInternalRopeLength)
84                     expand();
85                 m_jsString->m_fibers[m_index++].set(m_globalData, m_jsString, jsString);
86                 m_jsString->m_length += jsString->m_length;
87                 m_jsString->m_is8Bit = m_jsString->m_is8Bit && jsString->m_is8Bit;
88             }
89
90             JSString* release()
91             {
92                 JSString* tmp = m_jsString;
93                 m_jsString = 0;
94                 return tmp;
95             }
96
97             unsigned length() { return m_jsString->m_length; }
98
99         private:
100             void expand();
101
102             JSGlobalData& m_globalData;
103             JSString* m_jsString;
104             size_t m_index;
105         };
106
107     private:
108         JSString(JSGlobalData& globalData, PassRefPtr<StringImpl> value)
109             : JSCell(globalData, globalData.stringStructure.get())
110             , m_value(value)
111         {
112         }
113
114         JSString(JSGlobalData& globalData)
115             : JSCell(globalData, globalData.stringStructure.get())
116         {
117         }
118
119         void finishCreation(JSGlobalData& globalData)
120         {
121             Base::finishCreation(globalData);
122             m_length = 0;
123             m_is8Bit = true;
124         }
125
126         void finishCreation(JSGlobalData& globalData, size_t length)
127         {
128             ASSERT(!m_value.isNull());
129             Base::finishCreation(globalData);
130             m_length = length;
131             m_is8Bit = m_value.impl()->is8Bit();
132         }
133
134         void finishCreation(JSGlobalData& globalData, size_t length, size_t cost)
135         {
136             ASSERT(!m_value.isNull());
137             Base::finishCreation(globalData);
138             m_length = length;
139             m_is8Bit = m_value.impl()->is8Bit();
140             Heap::heap(this)->reportExtraMemoryCost(cost);
141         }
142
143         void finishCreation(JSGlobalData& globalData, JSString* s1, JSString* s2)
144         {
145             Base::finishCreation(globalData);
146             m_length = s1->length() + s2->length();
147             m_is8Bit = (s1->is8Bit() && s2->is8Bit());
148             m_fibers[0].set(globalData, this, s1);
149             m_fibers[1].set(globalData, this, s2);
150         }
151
152         void finishCreation(JSGlobalData& globalData, JSString* s1, JSString* s2, JSString* s3)
153         {
154             Base::finishCreation(globalData);
155             m_length = s1->length() + s2->length() + s3->length();
156             m_is8Bit = (s1->is8Bit() && s2->is8Bit() &&  s3->is8Bit());
157             m_fibers[0].set(globalData, this, s1);
158             m_fibers[1].set(globalData, this, s2);
159             m_fibers[2].set(globalData, this, s3);
160         }
161
162         static JSString* createNull(JSGlobalData& globalData)
163         {
164             JSString* newString = new (allocateCell<JSString>(globalData.heap)) JSString(globalData);
165             newString->finishCreation(globalData);
166             return newString;
167         }
168
169     public:
170         static JSString* create(JSGlobalData& globalData, PassRefPtr<StringImpl> value)
171         {
172             ASSERT(value);
173             size_t length = value->length();
174             size_t cost = value->cost();
175             JSString* newString = new (allocateCell<JSString>(globalData.heap)) JSString(globalData, value);
176             newString->finishCreation(globalData, length, cost);
177             return newString;
178         }
179         static JSString* create(JSGlobalData& globalData, JSString* s1, JSString* s2)
180         {
181             JSString* newString = new (allocateCell<JSString>(globalData.heap)) JSString(globalData);
182             newString->finishCreation(globalData, s1, s2);
183             return newString;
184         }
185         static JSString* create(JSGlobalData& globalData, JSString* s1, JSString* s2, JSString* s3)
186         {
187             JSString* newString = new (allocateCell<JSString>(globalData.heap)) JSString(globalData);
188             newString->finishCreation(globalData, s1, s2, s3);
189             return newString;
190         }
191         static JSString* createHasOtherOwner(JSGlobalData& globalData, PassRefPtr<StringImpl> value)
192         {
193             ASSERT(value);
194             size_t length = value->length();
195             JSString* newString = new (allocateCell<JSString>(globalData.heap)) JSString(globalData, value);
196             newString->finishCreation(globalData, length);
197             return newString;
198         }
199
200         virtual ~JSString();
201
202         const UString& value(ExecState* exec) const
203         {
204             if (isRope())
205                 resolveRope(exec);
206             return m_value;
207         }
208         const UString& tryGetValue() const
209         {
210             if (isRope())
211                 resolveRope(0);
212             return m_value;
213         }
214         unsigned length() { return m_length; }
215
216         JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const;
217         bool toBoolean(ExecState*) const;
218         bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const;
219         JSObject* toObject(ExecState*, JSGlobalObject*) const;
220         UString toString(ExecState*) const;
221         double toNumber(ExecState*) const;
222         
223         bool getStringPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
224         bool getStringPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
225         bool getStringPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor&);
226
227         bool canGetIndex(unsigned i) { return i < m_length; }
228         JSString* getIndex(ExecState*, unsigned);
229         JSString* getIndexSlowCase(ExecState*, unsigned);
230
231         JSValue replaceCharacter(ExecState*, UChar, const UString& replacement);
232
233         static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto)
234         {
235             return Structure::create(globalData, globalObject, proto, TypeInfo(StringType, OverridesGetOwnPropertySlot), &s_info);
236         }
237
238         static size_t offsetOfLength() { return OBJECT_OFFSETOF(JSString, m_length); }
239         static size_t offsetOfValue() { return OBJECT_OFFSETOF(JSString, m_value); }
240
241         static const ClassInfo s_info;
242
243         static void visitChildren(JSCell*, SlotVisitor&);
244
245     private:
246         JSString(VPtrStealingHackType) 
247             : JSCell(VPtrStealingHack)
248         {
249         }
250
251         void resolveRope(ExecState*) const;
252         void resolveRopeSlowCase8(LChar*) const;
253         void resolveRopeSlowCase(UChar*) const;
254         void outOfMemory(ExecState*) const;
255
256         static JSObject* toThisObject(JSCell*, ExecState*);
257
258         // Actually getPropertySlot, not getOwnPropertySlot (see JSCell).
259         static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier& propertyName, PropertySlot&);
260         static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&);
261
262         static const unsigned s_maxInternalRopeLength = 3;
263
264         // A string is represented either by a UString or a rope of fibers.
265         bool m_is8Bit : 1;
266         unsigned m_length;
267         mutable UString m_value;
268         mutable FixedArray<WriteBarrier<JSString>, s_maxInternalRopeLength> m_fibers;
269
270         bool isRope() const { return m_value.isNull(); }
271         bool is8Bit() const { return m_is8Bit; }
272         UString& string() { ASSERT(!isRope()); return m_value; }
273
274         friend JSValue jsString(ExecState*, JSString*, JSString*);
275         friend JSValue jsString(ExecState*, Register*, unsigned count);
276         friend JSValue jsStringFromArguments(ExecState*, JSValue thisValue);
277         friend JSString* jsSubstring(ExecState*, JSString*, unsigned offset, unsigned length);
278     };
279
280     JSString* asString(JSValue);
281
282     // When an object is created from a different DLL, MSVC changes vptr to a "local" one right after invoking a constructor,
283     // see <http://groups.google.com/group/microsoft.public.vc.language/msg/55cdcefeaf770212>.
284     // This breaks isJSString(), and we don't need that hack anyway, so we change vptr back to primary one.
285     // The below function must be called by any inline function that invokes a JSString constructor.
286 #if COMPILER(MSVC) && !defined(BUILDING_JavaScriptCore)
287     inline JSString* fixupVPtr(JSGlobalData* globalData, JSString* string) { string->setVPtr(globalData->jsStringVPtr); return string; }
288 #else
289     inline JSString* fixupVPtr(JSGlobalData*, JSString* string) { return string; }
290 #endif
291
292     inline JSString* asString(JSValue value)
293     {
294         ASSERT(value.asCell()->isString());
295         return static_cast<JSString*>(value.asCell());
296     }
297
298     inline JSString* jsEmptyString(JSGlobalData* globalData)
299     {
300         return globalData->smallStrings.emptyString(globalData);
301     }
302
303     inline JSString* jsSingleCharacterString(JSGlobalData* globalData, UChar c)
304     {
305         if (c <= maxSingleCharacterString)
306             return globalData->smallStrings.singleCharacterString(globalData, c);
307         return fixupVPtr(globalData, JSString::create(*globalData, UString(&c, 1).impl()));
308     }
309
310     inline JSString* jsSingleCharacterSubstring(ExecState* exec, const UString& s, unsigned offset)
311     {
312         JSGlobalData* globalData = &exec->globalData();
313         ASSERT(offset < static_cast<unsigned>(s.length()));
314         UChar c = s[offset];
315         if (c <= maxSingleCharacterString)
316             return globalData->smallStrings.singleCharacterString(globalData, c);
317         return fixupVPtr(globalData, JSString::create(*globalData, StringImpl::create(s.impl(), offset, 1)));
318     }
319
320     inline JSString* jsNontrivialString(JSGlobalData* globalData, const char* s)
321     {
322         ASSERT(s);
323         ASSERT(s[0]);
324         ASSERT(s[1]);
325         return fixupVPtr(globalData, JSString::create(*globalData, UString(s).impl()));
326     }
327
328     inline JSString* jsNontrivialString(JSGlobalData* globalData, const UString& s)
329     {
330         ASSERT(s.length() > 1);
331         return fixupVPtr(globalData, JSString::create(*globalData, s.impl()));
332     }
333
334     inline JSString* JSString::getIndex(ExecState* exec, unsigned i)
335     {
336         ASSERT(canGetIndex(i));
337         if (isRope())
338             return getIndexSlowCase(exec, i);
339         ASSERT(i < m_value.length());
340         return jsSingleCharacterSubstring(exec, m_value, i);
341     }
342
343     inline JSString* jsString(JSGlobalData* globalData, const UString& s)
344     {
345         int size = s.length();
346         if (!size)
347             return globalData->smallStrings.emptyString(globalData);
348         if (size == 1) {
349             UChar c = s[0];
350             if (c <= maxSingleCharacterString)
351                 return globalData->smallStrings.singleCharacterString(globalData, c);
352         }
353         return fixupVPtr(globalData, JSString::create(*globalData, s.impl()));
354     }
355
356     inline JSString* jsSubstring(ExecState* exec, JSString* s, unsigned offset, unsigned length)
357     {
358         ASSERT(offset <= static_cast<unsigned>(s->length()));
359         ASSERT(length <= static_cast<unsigned>(s->length()));
360         ASSERT(offset + length <= static_cast<unsigned>(s->length()));
361         JSGlobalData* globalData = &exec->globalData();
362         if (!length)
363             return globalData->smallStrings.emptyString(globalData);
364         return jsSubstring(globalData, s->value(exec), offset, length);
365     }
366
367     inline JSString* jsSubstring8(JSGlobalData* globalData, const UString& s, unsigned offset, unsigned length)
368     {
369         ASSERT(offset <= static_cast<unsigned>(s.length()));
370         ASSERT(length <= static_cast<unsigned>(s.length()));
371         ASSERT(offset + length <= static_cast<unsigned>(s.length()));
372         if (!length)
373             return globalData->smallStrings.emptyString(globalData);
374         if (length == 1) {
375             UChar c = s[offset];
376             if (c <= maxSingleCharacterString)
377                 return globalData->smallStrings.singleCharacterString(globalData, c);
378         }
379         return fixupVPtr(globalData, JSString::createHasOtherOwner(*globalData, StringImpl::create8(s.impl(), offset, length)));
380     }
381
382     inline JSString* jsSubstring(JSGlobalData* globalData, const UString& s, unsigned offset, unsigned length)
383     {
384         ASSERT(offset <= static_cast<unsigned>(s.length()));
385         ASSERT(length <= static_cast<unsigned>(s.length()));
386         ASSERT(offset + length <= static_cast<unsigned>(s.length()));
387         if (!length)
388             return globalData->smallStrings.emptyString(globalData);
389         if (length == 1) {
390             UChar c = s[offset];
391             if (c <= maxSingleCharacterString)
392                 return globalData->smallStrings.singleCharacterString(globalData, c);
393         }
394         return fixupVPtr(globalData, JSString::createHasOtherOwner(*globalData, StringImpl::create(s.impl(), offset, length)));
395     }
396
397     inline JSString* jsOwnedString(JSGlobalData* globalData, const UString& s)
398     {
399         int size = s.length();
400         if (!size)
401             return globalData->smallStrings.emptyString(globalData);
402         if (size == 1) {
403             UChar c = s[0];
404             if (c <= maxSingleCharacterString)
405                 return globalData->smallStrings.singleCharacterString(globalData, c);
406         }
407         return fixupVPtr(globalData, JSString::createHasOtherOwner(*globalData, s.impl()));
408     }
409
410     inline JSString* jsStringBuilder(JSGlobalData* globalData)
411     {
412         return fixupVPtr(globalData, JSString::createNull(*globalData));
413     }
414
415     inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->globalData()); }
416     inline JSString* jsString(ExecState* exec, const UString& s) { return jsString(&exec->globalData(), s); }
417     inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->globalData(), c); }
418     inline JSString* jsSubstring8(ExecState* exec, const UString& s, unsigned offset, unsigned length) { return jsSubstring8(&exec->globalData(), s, offset, length); }
419     inline JSString* jsSubstring(ExecState* exec, const UString& s, unsigned offset, unsigned length) { return jsSubstring(&exec->globalData(), s, offset, length); }
420     inline JSString* jsNontrivialString(ExecState* exec, const UString& s) { return jsNontrivialString(&exec->globalData(), s); }
421     inline JSString* jsNontrivialString(ExecState* exec, const char* s) { return jsNontrivialString(&exec->globalData(), s); }
422     inline JSString* jsOwnedString(ExecState* exec, const UString& s) { return jsOwnedString(&exec->globalData(), s); } 
423
424     ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
425     {
426         if (propertyName == exec->propertyNames().length) {
427             slot.setValue(jsNumber(m_length));
428             return true;
429         }
430
431         bool isStrictUInt32;
432         unsigned i = propertyName.toUInt32(isStrictUInt32);
433         if (isStrictUInt32 && i < m_length) {
434             slot.setValue(getIndex(exec, i));
435             return true;
436         }
437
438         return false;
439     }
440         
441     ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
442     {
443         if (propertyName < m_length) {
444             slot.setValue(getIndex(exec, propertyName));
445             return true;
446         }
447
448         return false;
449     }
450
451     inline bool isJSString(JSGlobalData* globalData, JSValue v) { return v.isCell() && v.asCell()->vptr() == globalData->jsStringVPtr; }
452
453     inline bool JSCell::toBoolean(ExecState* exec) const
454     {
455         if (isString()) 
456             return static_cast<const JSString*>(this)->toBoolean(exec);
457         return !structure()->typeInfo().masqueradesAsUndefined();
458     }
459
460     // --- JSValue inlines ----------------------------
461     
462     inline bool JSValue::toBoolean(ExecState* exec) const
463     {
464         if (isInt32())
465             return asInt32();
466         if (isDouble())
467             return asDouble() > 0.0 || asDouble() < 0.0; // false for NaN
468         if (isCell())
469             return asCell()->toBoolean(exec);
470         return isTrue(); // false, null, and undefined all convert to false.
471     }
472
473     inline UString JSValue::toString(ExecState* exec) const
474     {
475         if (isString())
476             return static_cast<JSString*>(asCell())->value(exec);
477         if (isInt32())
478             return exec->globalData().numericStrings.add(asInt32());
479         if (isDouble())
480             return exec->globalData().numericStrings.add(asDouble());
481         if (isTrue())
482             return "true";
483         if (isFalse())
484             return "false";
485         if (isNull())
486             return "null";
487         if (isUndefined())
488             return "undefined";
489         ASSERT(isCell());
490         return asCell()->toString(exec);
491     }
492
493 } // namespace JSC
494
495 #endif // JSString_h