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