]> git.saurik.com Git - apple/javascriptcore.git/blame - runtime/JSString.h
JavaScriptCore-1218.34.tar.gz
[apple/javascriptcore.git] / runtime / JSString.h
CommitLineData
9dae56ea
A
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
9dae56ea 25#include "CallFrame.h"
ba379fdc 26#include "CommonIdentifiers.h"
9dae56ea 27#include "Identifier.h"
f9bf01c6 28#include "PropertyDescriptor.h"
9dae56ea 29#include "PropertySlot.h"
14957cd0 30#include "Structure.h"
9dae56ea
A
31
32namespace JSC {
33
93a37866
A
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 }
9dae56ea 85
93a37866
A
86 JSString(VM& vm)
87 : JSCell(vm, vm.stringStructure.get())
88 , m_flags(0)
89 {
90 }
f9bf01c6 91
93a37866
A
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 }
4e4e5a6f 100
93a37866
A
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 }
4e4e5a6f 110
93a37866
A
111protected:
112 void finishCreation(VM& vm)
113 {
114 Base::finishCreation(vm);
115 m_length = 0;
116 setIs8Bit(true);
117 vm.m_newStringsSinceLastHashCons++;
118 }
6fe7ccc8 119
93a37866
A
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 }
6fe7ccc8 138
93a37866
A
139 const String& value(ExecState*) const;
140 const String& tryGetValue() const;
141 unsigned length() { return m_length; }
9dae56ea 142
93a37866
A
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;
6fe7ccc8 148
93a37866
A
149 bool getStringPropertySlot(ExecState*, PropertyName, PropertySlot&);
150 bool getStringPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
151 bool getStringPropertyDescriptor(ExecState*, PropertyName, PropertyDescriptor&);
9dae56ea 152
93a37866
A
153 bool canGetIndex(unsigned i) { return i < m_length; }
154 JSString* getIndex(ExecState*, unsigned);
9dae56ea 155
93a37866
A
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 }
6fe7ccc8 160
93a37866
A
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); }
6fe7ccc8 164
93a37866 165 static JS_EXPORTDATA const ClassInfo s_info;
6fe7ccc8 166
93a37866 167 static void visitChildren(JSCell*, SlotVisitor&);
6fe7ccc8 168
93a37866
A
169 enum {
170 HashConsLock = 1u << 2,
171 IsHashConsSingleton = 1u << 1,
172 Is8Bit = 1u
173 };
6fe7ccc8 174
93a37866
A
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;
6fe7ccc8 199
93a37866
A
200private:
201 friend class LLIntOffsetsExtractor;
14957cd0 202
93a37866 203 static JSObject* toThisObject(JSCell*, ExecState*);
6fe7ccc8 204
93a37866
A
205 // Actually getPropertySlot, not getOwnPropertySlot (see JSCell).
206 static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&);
207 static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&);
6fe7ccc8 208
93a37866 209 String& string() { ASSERT(!isRope()); return m_value; }
6fe7ccc8 210
93a37866
A
211 friend JSValue jsString(ExecState*, JSString*, JSString*);
212 friend JSString* jsSubstring(ExecState*, JSString*, unsigned offset, unsigned length);
213};
6fe7ccc8 214
93a37866
A
215class JSRopeString : public JSString {
216 friend class JSString;
9dae56ea 217
93a37866 218 friend JSRopeString* jsStringBuilder(VM*);
6fe7ccc8 219
93a37866
A
220 class RopeBuilder {
221 public:
222 RopeBuilder(VM& vm)
223 : m_vm(vm)
224 , m_jsString(jsStringBuilder(&vm))
6fe7ccc8 225 , m_index(0)
f9bf01c6
A
226 {
227 }
228
93a37866 229 void append(JSString* jsString)
6fe7ccc8 230 {
93a37866
A
231 if (m_index == JSRopeString::s_maxInternalRopeLength)
232 expand();
233 m_jsString->append(m_vm, m_index++, jsString);
6fe7ccc8 234 }
f9bf01c6 235
93a37866 236 JSRopeString* release()
f9bf01c6 237 {
93a37866
A
238 JSRopeString* tmp = m_jsString;
239 m_jsString = 0;
240 return tmp;
f9bf01c6
A
241 }
242
93a37866 243 unsigned length() { return m_jsString->m_length; }
9dae56ea 244
6fe7ccc8 245 private:
93a37866
A
246 void expand();
247
248 VM& m_vm;
249 JSRopeString* m_jsString;
250 size_t m_index;
9dae56ea 251 };
93a37866
A
252
253private:
254 JSRopeString(VM& vm)
255 : JSString(vm)
9dae56ea 256 {
9dae56ea
A
257 }
258
93a37866 259 void finishCreation(VM& vm, JSString* s1, JSString* s2)
9dae56ea 260 {
93a37866
A
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);
9dae56ea 266 }
93a37866
A
267
268 void finishCreation(VM& vm, JSString* s1, JSString* s2, JSString* s3)
9dae56ea 269 {
93a37866
A
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);
9dae56ea
A
276 }
277
93a37866 278 void finishCreation(VM& vm)
9dae56ea 279 {
93a37866 280 JSString::finishCreation(vm);
9dae56ea
A
281 }
282
93a37866 283 void append(VM& vm, size_t index, JSString* jsString)
9dae56ea 284 {
93a37866
A
285 m_fibers[index].set(vm, this, jsString);
286 m_length += jsString->m_length;
287 setIs8Bit(is8Bit() && jsString->is8Bit());
9dae56ea
A
288 }
289
93a37866 290 static JSRopeString* createNull(VM& vm)
9dae56ea 291 {
93a37866
A
292 JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
293 newString->finishCreation(vm);
294 return newString;
6fe7ccc8
A
295 }
296
93a37866
A
297public:
298 static JSString* create(VM& vm, JSString* s1, JSString* s2)
6fe7ccc8 299 {
93a37866
A
300 JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
301 newString->finishCreation(vm, s1, s2);
302 return newString;
6fe7ccc8 303 }
93a37866 304 static JSString* create(VM& vm, JSString* s1, JSString* s2, JSString* s3)
6fe7ccc8 305 {
93a37866
A
306 JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
307 newString->finishCreation(vm, s1, s2, s3);
308 return newString;
9dae56ea
A
309 }
310
93a37866
A
311 void visitFibers(SlotVisitor&);
312
313 static ptrdiff_t offsetOfFibers() { return OBJECT_OFFSETOF(JSRopeString, m_fibers); }
f9bf01c6 314
93a37866
A
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);
f9bf01c6 399 }
93a37866
A
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);
6fe7ccc8 425 }
93a37866
A
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);
b80e6193 440 }
93a37866
A
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);
f9bf01c6 453 }
93a37866
A
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;
6fe7ccc8
A
475 }
476
93a37866
A
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;
9dae56ea
A
482 }
483
93a37866
A
484 return false;
485}
9dae56ea 486
93a37866
A
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;
9dae56ea
A
492 }
493
93a37866
A
494 return false;
495}
6fe7ccc8 496
93a37866 497inline bool isJSString(JSValue v) { return v.isCell() && v.asCell()->classInfo() == &JSString::s_info; }
ba379fdc 498
93a37866 499// --- JSValue inlines ----------------------------
6fe7ccc8 500
93a37866
A
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}
f9bf01c6 551
9dae56ea
A
552} // namespace JSC
553
554#endif // JSString_h