Roll out r108309, r108323, and r108326
[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
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 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         static void destroy(JSCell*);
73
74         class RopeBuilder {
75         public:
76             RopeBuilder(JSGlobalData& globalData)
77                 : m_globalData(globalData)
78                 , m_jsString(jsStringBuilder(&globalData))
79                 , m_index(0)
80             {
81             }
82
83             void append(JSString* jsString)
84             {
85                 if (m_index == JSString::s_maxInternalRopeLength)
86                     expand();
87                 m_jsString->m_fibers[m_index++].set(m_globalData, m_jsString, jsString);
88                 m_jsString->m_length += jsString->m_length;
89                 m_jsString->m_is8Bit = m_jsString->m_is8Bit && jsString->m_is8Bit;
90             }
91
92             JSString* release()
93             {
94                 JSString* tmp = m_jsString;
95                 m_jsString = 0;
96                 return tmp;
97             }
98
99             unsigned length() { return m_jsString->m_length; }
100
101         private:
102             void expand();
103
104             JSGlobalData& m_globalData;
105             JSString* m_jsString;
106             size_t m_index;
107         };
108
109     private:
110         JSString(JSGlobalData& globalData, PassRefPtr<StringImpl> value)
111             : JSCell(globalData, globalData.stringStructure.get())
112             , m_value(value)
113         {
114         }
115
116         JSString(JSGlobalData& globalData)
117             : JSCell(globalData, globalData.stringStructure.get())
118         {
119         }
120
121         void finishCreation(JSGlobalData& globalData)
122         {
123             Base::finishCreation(globalData);
124             m_length = 0;
125             m_is8Bit = true;
126         }
127
128         void finishCreation(JSGlobalData& globalData, size_t length)
129         {
130             ASSERT(!m_value.isNull());
131             Base::finishCreation(globalData);
132             m_length = length;
133             m_is8Bit = m_value.impl()->is8Bit();
134         }
135
136         void finishCreation(JSGlobalData& globalData, size_t length, size_t cost)
137         {
138             ASSERT(!m_value.isNull());
139             Base::finishCreation(globalData);
140             m_length = length;
141             m_is8Bit = m_value.impl()->is8Bit();
142             Heap::heap(this)->reportExtraMemoryCost(cost);
143         }
144
145         void finishCreation(JSGlobalData& globalData, JSString* s1, JSString* s2)
146         {
147             Base::finishCreation(globalData);
148             m_length = s1->length() + s2->length();
149             m_is8Bit = (s1->is8Bit() && s2->is8Bit());
150             m_fibers[0].set(globalData, this, s1);
151             m_fibers[1].set(globalData, this, s2);
152         }
153
154         void finishCreation(JSGlobalData& globalData, JSString* s1, JSString* s2, JSString* s3)
155         {
156             Base::finishCreation(globalData);
157             m_length = s1->length() + s2->length() + s3->length();
158             m_is8Bit = (s1->is8Bit() && s2->is8Bit() &&  s3->is8Bit());
159             m_fibers[0].set(globalData, this, s1);
160             m_fibers[1].set(globalData, this, s2);
161             m_fibers[2].set(globalData, this, s3);
162         }
163
164         static JSString* createNull(JSGlobalData& globalData)
165         {
166             JSString* newString = new (NotNull, allocateCell<JSString>(globalData.heap)) JSString(globalData);
167             newString->finishCreation(globalData);
168             return newString;
169         }
170
171     public:
172         static JSString* create(JSGlobalData& globalData, PassRefPtr<StringImpl> value)
173         {
174             ASSERT(value);
175             size_t length = value->length();
176             size_t cost = value->cost();
177             JSString* newString = new (NotNull, allocateCell<JSString>(globalData.heap)) JSString(globalData, value);
178             newString->finishCreation(globalData, length, cost);
179             return newString;
180         }
181         static JSString* create(JSGlobalData& globalData, JSString* s1, JSString* s2)
182         {
183             JSString* newString = new (NotNull, allocateCell<JSString>(globalData.heap)) JSString(globalData);
184             newString->finishCreation(globalData, s1, s2);
185             return newString;
186         }
187         static JSString* create(JSGlobalData& globalData, JSString* s1, JSString* s2, JSString* s3)
188         {
189             JSString* newString = new (NotNull, allocateCell<JSString>(globalData.heap)) JSString(globalData);
190             newString->finishCreation(globalData, s1, s2, s3);
191             return newString;
192         }
193         static JSString* createHasOtherOwner(JSGlobalData& globalData, PassRefPtr<StringImpl> value)
194         {
195             ASSERT(value);
196             size_t length = value->length();
197             JSString* newString = new (NotNull, allocateCell<JSString>(globalData.heap)) JSString(globalData, value);
198             newString->finishCreation(globalData, length);
199             return newString;
200         }
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         JS_EXPORT_PRIVATE bool toBoolean(ExecState*) const;
218         bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const;
219         JSObject* toObject(ExecState*, JSGlobalObject*) const;
220         double toNumber(ExecState*) const;
221         
222         bool getStringPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
223         bool getStringPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
224         bool getStringPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor&);
225
226         bool canGetIndex(unsigned i) { return i < m_length; }
227         JSString* getIndex(ExecState*, unsigned);
228         JSString* getIndexSlowCase(ExecState*, unsigned);
229
230         static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto)
231         {
232             return Structure::create(globalData, globalObject, proto, TypeInfo(StringType, OverridesGetOwnPropertySlot), &s_info);
233         }
234
235         static size_t offsetOfLength() { return OBJECT_OFFSETOF(JSString, m_length); }
236         static size_t offsetOfValue() { return OBJECT_OFFSETOF(JSString, m_value); }
237
238         static JS_EXPORTDATA const ClassInfo s_info;
239
240         static void visitChildren(JSCell*, SlotVisitor&);
241
242     private:
243         JS_EXPORT_PRIVATE void resolveRope(ExecState*) const;
244         void resolveRopeSlowCase8(LChar*) const;
245         void resolveRopeSlowCase(UChar*) const;
246         void outOfMemory(ExecState*) const;
247
248         static JSObject* toThisObject(JSCell*, ExecState*);
249
250         // Actually getPropertySlot, not getOwnPropertySlot (see JSCell).
251         static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier& propertyName, PropertySlot&);
252         static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&);
253
254         static const unsigned s_maxInternalRopeLength = 3;
255
256         // A string is represented either by a UString or a rope of fibers.
257         bool m_is8Bit : 1;
258         unsigned m_length;
259         mutable UString m_value;
260         mutable FixedArray<WriteBarrier<JSString>, s_maxInternalRopeLength> m_fibers;
261
262         bool isRope() const { return m_value.isNull(); }
263         bool is8Bit() const { return m_is8Bit; }
264         UString& string() { ASSERT(!isRope()); return m_value; }
265
266         friend JSValue jsString(ExecState*, JSString*, JSString*);
267         friend JSValue jsString(ExecState*, Register*, unsigned count);
268         friend JSValue jsStringFromArguments(ExecState*, JSValue thisValue);
269         friend JSString* jsSubstring(ExecState*, JSString*, unsigned offset, unsigned length);
270     };
271
272     JSString* asString(JSValue);
273
274     inline JSString* asString(JSValue value)
275     {
276         ASSERT(value.asCell()->isString());
277         return static_cast<JSString*>(value.asCell());
278     }
279
280     inline JSString* jsEmptyString(JSGlobalData* globalData)
281     {
282         return globalData->smallStrings.emptyString(globalData);
283     }
284
285     inline JSString* jsSingleCharacterString(JSGlobalData* globalData, UChar c)
286     {
287         if (c <= maxSingleCharacterString)
288             return globalData->smallStrings.singleCharacterString(globalData, c);
289         return JSString::create(*globalData, UString(&c, 1).impl());
290     }
291
292     inline JSString* jsSingleCharacterSubstring(ExecState* exec, const UString& s, unsigned offset)
293     {
294         JSGlobalData* globalData = &exec->globalData();
295         ASSERT(offset < static_cast<unsigned>(s.length()));
296         UChar c = s[offset];
297         if (c <= maxSingleCharacterString)
298             return globalData->smallStrings.singleCharacterString(globalData, c);
299         return JSString::create(*globalData, StringImpl::create(s.impl(), offset, 1));
300     }
301
302     inline JSString* jsNontrivialString(JSGlobalData* globalData, const char* s)
303     {
304         ASSERT(s);
305         ASSERT(s[0]);
306         ASSERT(s[1]);
307         return JSString::create(*globalData, UString(s).impl());
308     }
309
310     inline JSString* jsNontrivialString(JSGlobalData* globalData, const UString& s)
311     {
312         ASSERT(s.length() > 1);
313         return JSString::create(*globalData, s.impl());
314     }
315
316     inline JSString* JSString::getIndex(ExecState* exec, unsigned i)
317     {
318         ASSERT(canGetIndex(i));
319         if (isRope())
320             return getIndexSlowCase(exec, i);
321         ASSERT(i < m_value.length());
322         return jsSingleCharacterSubstring(exec, m_value, i);
323     }
324
325     inline JSString* jsString(JSGlobalData* globalData, const UString& s)
326     {
327         int size = s.length();
328         if (!size)
329             return globalData->smallStrings.emptyString(globalData);
330         if (size == 1) {
331             UChar c = s[0];
332             if (c <= maxSingleCharacterString)
333                 return globalData->smallStrings.singleCharacterString(globalData, c);
334         }
335         return JSString::create(*globalData, s.impl());
336     }
337
338     inline JSString* jsSubstring(ExecState* exec, JSString* s, unsigned offset, unsigned length)
339     {
340         ASSERT(offset <= static_cast<unsigned>(s->length()));
341         ASSERT(length <= static_cast<unsigned>(s->length()));
342         ASSERT(offset + length <= static_cast<unsigned>(s->length()));
343         JSGlobalData* globalData = &exec->globalData();
344         if (!length)
345             return globalData->smallStrings.emptyString(globalData);
346         return jsSubstring(globalData, s->value(exec), offset, length);
347     }
348
349     inline JSString* jsSubstring8(JSGlobalData* globalData, const UString& s, unsigned offset, unsigned length)
350     {
351         ASSERT(offset <= static_cast<unsigned>(s.length()));
352         ASSERT(length <= static_cast<unsigned>(s.length()));
353         ASSERT(offset + length <= static_cast<unsigned>(s.length()));
354         if (!length)
355             return globalData->smallStrings.emptyString(globalData);
356         if (length == 1) {
357             UChar c = s[offset];
358             if (c <= maxSingleCharacterString)
359                 return globalData->smallStrings.singleCharacterString(globalData, c);
360         }
361         return JSString::createHasOtherOwner(*globalData, StringImpl::create8(s.impl(), offset, length));
362     }
363
364     inline JSString* jsSubstring(JSGlobalData* globalData, const UString& s, unsigned offset, unsigned length)
365     {
366         ASSERT(offset <= static_cast<unsigned>(s.length()));
367         ASSERT(length <= static_cast<unsigned>(s.length()));
368         ASSERT(offset + length <= static_cast<unsigned>(s.length()));
369         if (!length)
370             return globalData->smallStrings.emptyString(globalData);
371         if (length == 1) {
372             UChar c = s[offset];
373             if (c <= maxSingleCharacterString)
374                 return globalData->smallStrings.singleCharacterString(globalData, c);
375         }
376         return JSString::createHasOtherOwner(*globalData, StringImpl::create(s.impl(), offset, length));
377     }
378
379     inline JSString* jsOwnedString(JSGlobalData* globalData, const UString& s)
380     {
381         int size = s.length();
382         if (!size)
383             return globalData->smallStrings.emptyString(globalData);
384         if (size == 1) {
385             UChar c = s[0];
386             if (c <= maxSingleCharacterString)
387                 return globalData->smallStrings.singleCharacterString(globalData, c);
388         }
389         return JSString::createHasOtherOwner(*globalData, s.impl());
390     }
391
392     inline JSString* jsStringBuilder(JSGlobalData* globalData)
393     {
394         return JSString::createNull(*globalData);
395     }
396
397     inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->globalData()); }
398     inline JSString* jsString(ExecState* exec, const UString& s) { return jsString(&exec->globalData(), s); }
399     inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->globalData(), c); }
400     inline JSString* jsSubstring8(ExecState* exec, const UString& s, unsigned offset, unsigned length) { return jsSubstring8(&exec->globalData(), s, offset, length); }
401     inline JSString* jsSubstring(ExecState* exec, const UString& s, unsigned offset, unsigned length) { return jsSubstring(&exec->globalData(), s, offset, length); }
402     inline JSString* jsNontrivialString(ExecState* exec, const UString& s) { return jsNontrivialString(&exec->globalData(), s); }
403     inline JSString* jsNontrivialString(ExecState* exec, const char* s) { return jsNontrivialString(&exec->globalData(), s); }
404     inline JSString* jsOwnedString(ExecState* exec, const UString& s) { return jsOwnedString(&exec->globalData(), s); } 
405
406     ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
407     {
408         if (propertyName == exec->propertyNames().length) {
409             slot.setValue(jsNumber(m_length));
410             return true;
411         }
412
413         bool isStrictUInt32;
414         unsigned i = propertyName.toUInt32(isStrictUInt32);
415         if (isStrictUInt32 && i < m_length) {
416             slot.setValue(getIndex(exec, i));
417             return true;
418         }
419
420         return false;
421     }
422         
423     ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
424     {
425         if (propertyName < m_length) {
426             slot.setValue(getIndex(exec, propertyName));
427             return true;
428         }
429
430         return false;
431     }
432
433     inline bool isJSString(JSValue v) { return v.isCell() && v.asCell()->classInfo() == &JSString::s_info; }
434
435     inline bool JSCell::toBoolean(ExecState* exec) const
436     {
437         if (isString()) 
438             return static_cast<const JSString*>(this)->toBoolean(exec);
439         return !structure()->typeInfo().masqueradesAsUndefined();
440     }
441
442     // --- JSValue inlines ----------------------------
443     
444     inline bool JSValue::toBoolean(ExecState* exec) const
445     {
446         if (isInt32())
447             return asInt32();
448         if (isDouble())
449             return asDouble() > 0.0 || asDouble() < 0.0; // false for NaN
450         if (isCell())
451             return asCell()->toBoolean(exec);
452         return isTrue(); // false, null, and undefined all convert to false.
453     }
454
455     inline JSString* JSValue::toString(ExecState* exec) const
456     {
457         if (isString())
458             return static_cast<JSString*>(asCell());
459         return toStringSlowCase(exec);
460     }
461
462 } // namespace JSC
463
464 #endif // JSString_h