]> git.saurik.com Git - apple/javascriptcore.git/blame_incremental - runtime/JSString.h
JavaScriptCore-1218.34.tar.gz
[apple/javascriptcore.git] / runtime / JSString.h
... / ...
CommitLineData
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
32namespace JSC {
33
34class JSString;
35class JSRopeString;
36class LLIntOffsetsExtractor;
37
38JSString* jsEmptyString(VM*);
39JSString* jsEmptyString(ExecState*);
40JSString* jsString(VM*, const String&); // returns empty string if passed null string
41JSString* jsString(ExecState*, const String&); // returns empty string if passed null string
42
43JSString* jsSingleCharacterString(VM*, UChar);
44JSString* jsSingleCharacterString(ExecState*, UChar);
45JSString* jsSingleCharacterSubstring(ExecState*, const String&, unsigned offset);
46JSString* jsSubstring(VM*, const String&, unsigned offset, unsigned length);
47JSString* 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.
51JSString* jsNontrivialString(VM*, const String&);
52JSString* 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
57JSString* jsOwnedString(VM*, const String&);
58JSString* jsOwnedString(ExecState*, const String&);
59
60JSRopeString* jsStringBuilder(VM*);
61
62class JSString : public JSCell {
63public:
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
78private:
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
111protected:
112 void finishCreation(VM& vm)
113 {
114 Base::finishCreation(vm);
115 m_length = 0;
116 setIs8Bit(true);
117 vm.m_newStringsSinceLastHashCons++;
118 }
119
120public:
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 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(VM& vm, JSGlobalObject* globalObject, JSValue proto)
157 {
158 return Structure::create(vm, 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 HashConsLock = 1u << 2,
171 IsHashConsSingleton = 1u << 1,
172 Is8Bit = 1u
173 };
174
175protected:
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 shouldTryHashCons();
188 bool isHashConsSingleton() const { return m_flags & IsHashConsSingleton; }
189 void clearHashConsSingleton() { m_flags &= ~IsHashConsSingleton; }
190 void setHashConsSingleton() { m_flags |= IsHashConsSingleton; }
191 bool tryHashConsLock();
192 void releaseHashConsLock();
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
200private:
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
215class JSRopeString : public JSString {
216 friend class JSString;
217
218 friend JSRopeString* jsStringBuilder(VM*);
219
220 class RopeBuilder {
221 public:
222 RopeBuilder(VM& vm)
223 : m_vm(vm)
224 , m_jsString(jsStringBuilder(&vm))
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_vm, 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 VM& m_vm;
249 JSRopeString* m_jsString;
250 size_t m_index;
251 };
252
253private:
254 JSRopeString(VM& vm)
255 : JSString(vm)
256 {
257 }
258
259 void finishCreation(VM& vm, JSString* s1, JSString* s2)
260 {
261 Base::finishCreation(vm);
262 m_length = s1->length() + s2->length();
263 setIs8Bit(s1->is8Bit() && s2->is8Bit());
264 m_fibers[0].set(vm, this, s1);
265 m_fibers[1].set(vm, this, s2);
266 }
267
268 void finishCreation(VM& vm, JSString* s1, JSString* s2, JSString* s3)
269 {
270 Base::finishCreation(vm);
271 m_length = s1->length() + s2->length() + s3->length();
272 setIs8Bit(s1->is8Bit() && s2->is8Bit() && s3->is8Bit());
273 m_fibers[0].set(vm, this, s1);
274 m_fibers[1].set(vm, this, s2);
275 m_fibers[2].set(vm, this, s3);
276 }
277
278 void finishCreation(VM& vm)
279 {
280 JSString::finishCreation(vm);
281 }
282
283 void append(VM& vm, size_t index, JSString* jsString)
284 {
285 m_fibers[index].set(vm, this, jsString);
286 m_length += jsString->m_length;
287 setIs8Bit(is8Bit() && jsString->is8Bit());
288 }
289
290 static JSRopeString* createNull(VM& vm)
291 {
292 JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
293 newString->finishCreation(vm);
294 return newString;
295 }
296
297public:
298 static JSString* create(VM& vm, JSString* s1, JSString* s2)
299 {
300 JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
301 newString->finishCreation(vm, s1, s2);
302 return newString;
303 }
304 static JSString* create(VM& vm, JSString* s1, JSString* s2, JSString* s3)
305 {
306 JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
307 newString->finishCreation(vm, 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
317private:
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
331JSString* asString(JSValue);
332
333inline JSString* asString(JSValue value)
334{
335 ASSERT(value.asCell()->isString());
336 return jsCast<JSString*>(value.asCell());
337}
338
339inline JSString* jsEmptyString(VM* vm)
340{
341 return vm->smallStrings.emptyString();
342}
343
344ALWAYS_INLINE JSString* jsSingleCharacterString(VM* vm, UChar c)
345{
346 if (c <= maxSingleCharacterString)
347 return vm->smallStrings.singleCharacterString(vm, c);
348 return JSString::create(*vm, String(&c, 1).impl());
349}
350
351ALWAYS_INLINE JSString* jsSingleCharacterSubstring(ExecState* exec, const String& s, unsigned offset)
352{
353 VM* vm = &exec->vm();
354 ASSERT(offset < static_cast<unsigned>(s.length()));
355 UChar c = s.characterAt(offset);
356 if (c <= maxSingleCharacterString)
357 return vm->smallStrings.singleCharacterString(vm, c);
358 return JSString::create(*vm, StringImpl::create(s.impl(), offset, 1));
359}
360
361inline JSString* jsNontrivialString(VM* vm, const String& s)
362{
363 ASSERT(s.length() > 1);
364 return JSString::create(*vm, s.impl());
365}
366
367inline 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
374inline const String& JSString::tryGetValue() const
375{
376 if (isRope())
377 static_cast<const JSRopeString*>(this)->resolveRope(0);
378 return m_value;
379}
380
381inline 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
390inline JSString* jsString(VM* vm, const String& s)
391{
392 int size = s.length();
393 if (!size)
394 return vm->smallStrings.emptyString();
395 if (size == 1) {
396 UChar c = s.characterAt(0);
397 if (c <= maxSingleCharacterString)
398 return vm->smallStrings.singleCharacterString(vm, c);
399 }
400 return JSString::create(*vm, s.impl());
401}
402
403inline 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 VM* vm = &exec->vm();
409 if (!length)
410 return vm->smallStrings.emptyString();
411 return jsSubstring(vm, s->value(exec), offset, length);
412}
413
414inline JSString* jsSubstring8(VM* vm, 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 vm->smallStrings.emptyString();
421 if (length == 1) {
422 UChar c = s.characterAt(offset);
423 if (c <= maxSingleCharacterString)
424 return vm->smallStrings.singleCharacterString(vm, c);
425 }
426 return JSString::createHasOtherOwner(*vm, StringImpl::create8(s.impl(), offset, length));
427}
428
429inline JSString* jsSubstring(VM* vm, 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 vm->smallStrings.emptyString();
436 if (length == 1) {
437 UChar c = s.characterAt(offset);
438 if (c <= maxSingleCharacterString)
439 return vm->smallStrings.singleCharacterString(vm, c);
440 }
441 return JSString::createHasOtherOwner(*vm, StringImpl::create(s.impl(), offset, length));
442}
443
444inline JSString* jsOwnedString(VM* vm, const String& s)
445{
446 int size = s.length();
447 if (!size)
448 return vm->smallStrings.emptyString();
449 if (size == 1) {
450 UChar c = s.characterAt(0);
451 if (c <= maxSingleCharacterString)
452 return vm->smallStrings.singleCharacterString(vm, c);
453 }
454 return JSString::createHasOtherOwner(*vm, s.impl());
455}
456
457inline JSRopeString* jsStringBuilder(VM* vm)
458{
459 return JSRopeString::createNull(*vm);
460}
461
462inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->vm()); }
463inline JSString* jsString(ExecState* exec, const String& s) { return jsString(&exec->vm(), s); }
464inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->vm(), c); }
465inline JSString* jsSubstring8(ExecState* exec, const String& s, unsigned offset, unsigned length) { return jsSubstring8(&exec->vm(), s, offset, length); }
466inline JSString* jsSubstring(ExecState* exec, const String& s, unsigned offset, unsigned length) { return jsSubstring(&exec->vm(), s, offset, length); }
467inline JSString* jsNontrivialString(ExecState* exec, const String& s) { return jsNontrivialString(&exec->vm(), s); }
468inline JSString* jsOwnedString(ExecState* exec, const String& s) { return jsOwnedString(&exec->vm(), s); }
469
470ALWAYS_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
487ALWAYS_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
497inline bool isJSString(JSValue v) { return v.isCell() && v.asCell()->classInfo() == &JSString::s_info; }
498
499// --- JSValue inlines ----------------------------
500
501inline 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
512inline JSString* JSValue::toString(ExecState* exec) const
513{
514 if (isString())
515 return jsCast<JSString*>(asCell());
516 return toStringSlowCase(exec);
517}
518
519inline String JSValue::toWTFString(ExecState* exec) const
520{
521 if (isString())
522 return static_cast<JSString*>(asCell())->value(exec);
523 return toWTFStringSlowCase(exec);
524}
525
526ALWAYS_INLINE String inlineJSValueNotStringtoString(const JSValue& value, ExecState* exec)
527{
528 VM& vm = exec->vm();
529 if (value.isInt32())
530 return vm.numericStrings.add(value.asInt32());
531 if (value.isDouble())
532 return vm.numericStrings.add(value.asDouble());
533 if (value.isTrue())
534 return vm.propertyNames->trueKeyword.string();
535 if (value.isFalse())
536 return vm.propertyNames->falseKeyword.string();
537 if (value.isNull())
538 return vm.propertyNames->nullKeyword.string();
539 if (value.isUndefined())
540 return vm.propertyNames->undefinedKeyword.string();
541 return value.toString(exec)->value(exec);
542}
543
544ALWAYS_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