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.
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.
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.
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.
26 #include "CallFrame.h"
27 #include "CommonIdentifiers.h"
28 #include "Identifier.h"
29 #include "JSNumberCell.h"
30 #include "PropertyDescriptor.h"
31 #include "PropertySlot.h"
37 JSString
* jsEmptyString(JSGlobalData
*);
38 JSString
* jsEmptyString(ExecState
*);
39 JSString
* jsString(JSGlobalData
*, const UString
&); // returns empty string if passed null string
40 JSString
* jsString(ExecState
*, const UString
&); // returns empty string if passed null string
42 JSString
* jsSingleCharacterString(JSGlobalData
*, UChar
);
43 JSString
* jsSingleCharacterString(ExecState
*, UChar
);
44 JSString
* jsSingleCharacterSubstring(JSGlobalData
*, const UString
&, unsigned offset
);
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
);
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*);
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
&);
62 typedef void (*JSStringFinalizerCallback
)(JSString
*, void* context
);
63 JSString
* jsStringWithFinalizer(ExecState
*, const UString
&, JSStringFinalizerCallback callback
, void* context
);
65 class JS_EXPORTCLASS JSString
: public JSCell
{
68 friend class JSGlobalData
;
70 // A Rope is a string composed of a set of substrings.
71 class Rope
: public RefCounted
<Rope
> {
73 // A Rope is composed from a set of smaller strings called Fibers.
74 // Each Fiber in a rope is either UString::Rep or another Rope.
77 Fiber() : m_value(0) {}
78 Fiber(UString::Rep
* string
) : m_value(reinterpret_cast<intptr_t>(string
)) {}
79 Fiber(Rope
* rope
) : m_value(reinterpret_cast<intptr_t>(rope
) | 1) {}
81 Fiber(void* nonFiber
) : m_value(reinterpret_cast<intptr_t>(nonFiber
)) {}
100 unsigned refAndGetLength()
103 UString::Rep
* rep
= string();
104 return rep
->ref()->size();
108 return r
->stringLength();
112 bool isRope() { return m_value
& 1; }
113 Rope
* rope() { return reinterpret_cast<Rope
*>(m_value
& ~1); }
114 bool isString() { return !isRope(); }
115 UString::Rep
* string() { return reinterpret_cast<UString::Rep
*>(m_value
); }
117 void* nonFiber() { return reinterpret_cast<void*>(m_value
); }
122 // Creates a Rope comprising of 'ropeLength' Fibers.
123 // The Rope is constructed in an uninitialized state - initialize must be called for each Fiber in the Rope.
124 static PassRefPtr
<Rope
> createOrNull(unsigned ropeLength
)
127 if (tryFastMalloc(sizeof(Rope
) + (ropeLength
- 1) * sizeof(Fiber
)).getValue(allocation
))
128 return adoptRef(new (allocation
) Rope(ropeLength
));
133 void destructNonRecursive();
135 void append(unsigned &index
, Fiber
& fiber
)
137 m_fibers
[index
++] = fiber
;
138 m_stringLength
+= fiber
.refAndGetLength();
140 void append(unsigned &index
, const UString
& string
)
142 UString::Rep
* rep
= string
.rep();
143 m_fibers
[index
++] = Fiber(rep
);
144 m_stringLength
+= rep
->ref()->size();
146 void append(unsigned& index
, JSString
* jsString
)
148 if (jsString
->isRope()) {
149 for (unsigned i
= 0; i
< jsString
->m_ropeLength
; ++i
)
150 append(index
, jsString
->m_fibers
[i
]);
152 append(index
, jsString
->string());
155 unsigned ropeLength() { return m_ropeLength
; }
156 unsigned stringLength() { return m_stringLength
; }
157 Fiber
& fibers(unsigned index
) { return m_fibers
[index
]; }
160 Rope(unsigned ropeLength
) : m_ropeLength(ropeLength
), m_stringLength(0) {}
161 void* operator new(size_t, void* inPlace
) { return inPlace
; }
163 unsigned m_ropeLength
;
164 unsigned m_stringLength
;
168 ALWAYS_INLINE
JSString(JSGlobalData
* globalData
, const UString
& value
)
169 : JSCell(globalData
->stringStructure
.get())
170 , m_stringLength(value
.size())
174 Heap::heap(this)->reportExtraMemoryCost(value
.cost());
177 enum HasOtherOwnerType
{ HasOtherOwner
};
178 JSString(JSGlobalData
* globalData
, const UString
& value
, HasOtherOwnerType
)
179 : JSCell(globalData
->stringStructure
.get())
180 , m_stringLength(value
.size())
185 JSString(JSGlobalData
* globalData
, PassRefPtr
<UString::Rep
> value
, HasOtherOwnerType
)
186 : JSCell(globalData
->stringStructure
.get())
187 , m_stringLength(value
->size())
192 JSString(JSGlobalData
* globalData
, PassRefPtr
<JSString::Rope
> rope
)
193 : JSCell(globalData
->stringStructure
.get())
194 , m_stringLength(rope
->stringLength())
197 m_fibers
[0] = rope
.releaseRef();
199 // This constructor constructs a new string by concatenating s1 & s2.
200 // This should only be called with ropeLength <= 3.
201 JSString(JSGlobalData
* globalData
, unsigned ropeLength
, JSString
* s1
, JSString
* s2
)
202 : JSCell(globalData
->stringStructure
.get())
203 , m_stringLength(s1
->length() + s2
->length())
204 , m_ropeLength(ropeLength
)
206 ASSERT(ropeLength
<= s_maxInternalRopeLength
);
208 appendStringInConstruct(index
, s1
);
209 appendStringInConstruct(index
, s2
);
210 ASSERT(ropeLength
== index
);
212 // This constructor constructs a new string by concatenating s1 & s2.
213 // This should only be called with ropeLength <= 3.
214 JSString(JSGlobalData
* globalData
, unsigned ropeLength
, JSString
* s1
, const UString
& u2
)
215 : JSCell(globalData
->stringStructure
.get())
216 , m_stringLength(s1
->length() + u2
.size())
217 , m_ropeLength(ropeLength
)
219 ASSERT(ropeLength
<= s_maxInternalRopeLength
);
221 appendStringInConstruct(index
, s1
);
222 appendStringInConstruct(index
, u2
);
223 ASSERT(ropeLength
== index
);
225 // This constructor constructs a new string by concatenating s1 & s2.
226 // This should only be called with ropeLength <= 3.
227 JSString(JSGlobalData
* globalData
, unsigned ropeLength
, const UString
& u1
, JSString
* s2
)
228 : JSCell(globalData
->stringStructure
.get())
229 , m_stringLength(u1
.size() + s2
->length())
230 , m_ropeLength(ropeLength
)
232 ASSERT(ropeLength
<= s_maxInternalRopeLength
);
234 appendStringInConstruct(index
, u1
);
235 appendStringInConstruct(index
, s2
);
236 ASSERT(ropeLength
== index
);
238 // This constructor constructs a new string by concatenating v1, v2 & v3.
239 // This should only be called with ropeLength <= 3 ... which since every
240 // value must require a ropeLength of at least one implies that the length
241 // for each value must be exactly 1!
242 JSString(ExecState
* exec
, JSValue v1
, JSValue v2
, JSValue v3
)
243 : JSCell(exec
->globalData().stringStructure
.get())
245 , m_ropeLength(s_maxInternalRopeLength
)
248 appendValueInConstructAndIncrementLength(exec
, index
, v1
);
249 appendValueInConstructAndIncrementLength(exec
, index
, v2
);
250 appendValueInConstructAndIncrementLength(exec
, index
, v3
);
251 ASSERT(index
== s_maxInternalRopeLength
);
254 JSString(JSGlobalData
* globalData
, const UString
& value
, JSStringFinalizerCallback finalizer
, void* context
)
255 : JSCell(globalData
->stringStructure
.get())
256 , m_stringLength(value
.size())
260 // nasty hack because we can't union non-POD types
261 m_fibers
[0] = reinterpret_cast<void*>(reinterpret_cast<ptrdiff_t>(finalizer
));
262 m_fibers
[1] = context
;
263 Heap::heap(this)->reportExtraMemoryCost(value
.cost());
268 ASSERT(vptr() == JSGlobalData::jsStringVPtr
);
269 for (unsigned i
= 0; i
< m_ropeLength
; ++i
)
272 if (!m_ropeLength
&& m_fibers
[0].nonFiber()) {
273 JSStringFinalizerCallback finalizer
= reinterpret_cast<JSStringFinalizerCallback
>(m_fibers
[0].nonFiber());
274 finalizer(this, m_fibers
[1].nonFiber());
278 const UString
& value(ExecState
* exec
) const
284 const UString
tryGetValue() const
290 unsigned length() { return m_stringLength
; }
292 bool getStringPropertySlot(ExecState
*, const Identifier
& propertyName
, PropertySlot
&);
293 bool getStringPropertySlot(ExecState
*, unsigned propertyName
, PropertySlot
&);
294 bool getStringPropertyDescriptor(ExecState
*, const Identifier
& propertyName
, PropertyDescriptor
&);
296 bool canGetIndex(unsigned i
) { return i
< m_stringLength
; }
297 JSString
* getIndex(ExecState
*, unsigned);
299 static PassRefPtr
<Structure
> createStructure(JSValue proto
) { return Structure::create(proto
, TypeInfo(StringType
, OverridesGetOwnPropertySlot
| NeedsThisConversion
), AnonymousSlotCount
); }
302 enum VPtrStealingHackType
{ VPtrStealingHack
};
303 JSString(VPtrStealingHackType
)
309 void resolveRope(ExecState
*) const;
311 void appendStringInConstruct(unsigned& index
, const UString
& string
)
313 m_fibers
[index
++] = Rope::Fiber(string
.rep()->ref());
316 void appendStringInConstruct(unsigned& index
, JSString
* jsString
)
318 if (jsString
->isRope()) {
319 for (unsigned i
= 0; i
< jsString
->m_ropeLength
; ++i
)
320 m_fibers
[index
++] = jsString
->m_fibers
[i
].ref();
322 appendStringInConstruct(index
, jsString
->string());
325 void appendValueInConstructAndIncrementLength(ExecState
* exec
, unsigned& index
, JSValue v
)
328 ASSERT(asCell(v
)->isString());
329 JSString
* s
= static_cast<JSString
*>(asCell(v
));
330 ASSERT(s
->ropeLength() == 1);
331 appendStringInConstruct(index
, s
);
332 m_stringLength
+= s
->length();
334 UString
u(v
.toString(exec
));
335 m_fibers
[index
++] = Rope::Fiber(u
.rep()->ref());
336 m_stringLength
+= u
.size();
340 virtual JSValue
toPrimitive(ExecState
*, PreferredPrimitiveType
) const;
341 virtual bool getPrimitiveNumber(ExecState
*, double& number
, JSValue
& value
);
342 virtual bool toBoolean(ExecState
*) const;
343 virtual double toNumber(ExecState
*) const;
344 virtual JSObject
* toObject(ExecState
*) const;
345 virtual UString
toString(ExecState
*) const;
347 virtual JSObject
* toThisObject(ExecState
*) const;
348 virtual UString
toThisString(ExecState
*) const;
349 virtual JSString
* toThisJSString(ExecState
*);
351 // Actually getPropertySlot, not getOwnPropertySlot (see JSCell).
352 virtual bool getOwnPropertySlot(ExecState
*, const Identifier
& propertyName
, PropertySlot
&);
353 virtual bool getOwnPropertySlot(ExecState
*, unsigned propertyName
, PropertySlot
&);
354 virtual bool getOwnPropertyDescriptor(ExecState
*, const Identifier
&, PropertyDescriptor
&);
356 static const unsigned s_maxInternalRopeLength
= 3;
358 // A string is represented either by a UString or a Rope.
359 unsigned m_stringLength
;
360 mutable UString m_value
;
361 mutable unsigned m_ropeLength
;
362 mutable Rope::Fiber m_fibers
[s_maxInternalRopeLength
];
364 bool isRope() const { return m_ropeLength
; }
365 UString
& string() { ASSERT(!isRope()); return m_value
; }
366 unsigned ropeLength() { return m_ropeLength
? m_ropeLength
: 1; }
368 friend JSValue
jsString(ExecState
* exec
, JSString
* s1
, JSString
* s2
);
369 friend JSValue
jsString(ExecState
* exec
, const UString
& u1
, JSString
* s2
);
370 friend JSValue
jsString(ExecState
* exec
, JSString
* s1
, const UString
& u2
);
371 friend JSValue
jsString(ExecState
* exec
, Register
* strings
, unsigned count
);
372 friend JSValue
jsString(ExecState
* exec
, JSValue thisValue
, const ArgList
& args
);
373 friend JSString
* jsStringWithFinalizer(ExecState
*, const UString
&, JSStringFinalizerCallback callback
, void* context
);
376 JSString
* asString(JSValue
);
378 // When an object is created from a different DLL, MSVC changes vptr to a "local" one right after invoking a constructor,
379 // see <http://groups.google.com/group/microsoft.public.vc.language/msg/55cdcefeaf770212>.
380 // This breaks isJSString(), and we don't need that hack anyway, so we change vptr back to primary one.
381 // The below function must be called by any inline function that invokes a JSString constructor.
382 #if COMPILER(MSVC) && !defined(BUILDING_JavaScriptCore)
383 inline JSString
* fixupVPtr(JSGlobalData
* globalData
, JSString
* string
) { string
->setVPtr(globalData
->jsStringVPtr
); return string
; }
385 inline JSString
* fixupVPtr(JSGlobalData
*, JSString
* string
) { return string
; }
388 inline JSString
* asString(JSValue value
)
390 ASSERT(asCell(value
)->isString());
391 return static_cast<JSString
*>(asCell(value
));
394 inline JSString
* jsEmptyString(JSGlobalData
* globalData
)
396 return globalData
->smallStrings
.emptyString(globalData
);
399 inline JSString
* jsSingleCharacterString(JSGlobalData
* globalData
, UChar c
)
402 return globalData
->smallStrings
.singleCharacterString(globalData
, c
);
403 return fixupVPtr(globalData
, new (globalData
) JSString(globalData
, UString(&c
, 1)));
406 inline JSString
* jsSingleCharacterSubstring(JSGlobalData
* globalData
, const UString
& s
, unsigned offset
)
408 ASSERT(offset
< static_cast<unsigned>(s
.size()));
409 UChar c
= s
.data()[offset
];
411 return globalData
->smallStrings
.singleCharacterString(globalData
, c
);
412 return fixupVPtr(globalData
, new (globalData
) JSString(globalData
, UString(UString::Rep::create(s
.rep(), offset
, 1))));
415 inline JSString
* jsNontrivialString(JSGlobalData
* globalData
, const char* s
)
420 return fixupVPtr(globalData
, new (globalData
) JSString(globalData
, s
));
423 inline JSString
* jsNontrivialString(JSGlobalData
* globalData
, const UString
& s
)
425 ASSERT(s
.size() > 1);
426 return fixupVPtr(globalData
, new (globalData
) JSString(globalData
, s
));
429 inline JSString
* JSString::getIndex(ExecState
* exec
, unsigned i
)
431 ASSERT(canGetIndex(i
));
432 return jsSingleCharacterSubstring(&exec
->globalData(), value(exec
), i
);
435 inline JSString
* jsString(JSGlobalData
* globalData
, const UString
& s
)
439 return globalData
->smallStrings
.emptyString(globalData
);
441 UChar c
= s
.data()[0];
443 return globalData
->smallStrings
.singleCharacterString(globalData
, c
);
445 return fixupVPtr(globalData
, new (globalData
) JSString(globalData
, s
));
448 inline JSString
* jsStringWithFinalizer(ExecState
* exec
, const UString
& s
, JSStringFinalizerCallback callback
, void* context
)
450 ASSERT(s
.size() && (s
.size() > 1 || s
.data()[0] > 0xFF));
451 JSGlobalData
* globalData
= &exec
->globalData();
452 return fixupVPtr(globalData
, new (globalData
) JSString(globalData
, s
, callback
, context
));
455 inline JSString
* jsSubstring(JSGlobalData
* globalData
, const UString
& s
, unsigned offset
, unsigned length
)
457 ASSERT(offset
<= static_cast<unsigned>(s
.size()));
458 ASSERT(length
<= static_cast<unsigned>(s
.size()));
459 ASSERT(offset
+ length
<= static_cast<unsigned>(s
.size()));
461 return globalData
->smallStrings
.emptyString(globalData
);
463 UChar c
= s
.data()[offset
];
465 return globalData
->smallStrings
.singleCharacterString(globalData
, c
);
467 return fixupVPtr(globalData
, new (globalData
) JSString(globalData
, UString(UString::Rep::create(s
.rep(), offset
, length
)), JSString::HasOtherOwner
));
470 inline JSString
* jsOwnedString(JSGlobalData
* globalData
, const UString
& s
)
474 return globalData
->smallStrings
.emptyString(globalData
);
476 UChar c
= s
.data()[0];
478 return globalData
->smallStrings
.singleCharacterString(globalData
, c
);
480 return fixupVPtr(globalData
, new (globalData
) JSString(globalData
, s
, JSString::HasOtherOwner
));
483 inline JSString
* jsEmptyString(ExecState
* exec
) { return jsEmptyString(&exec
->globalData()); }
484 inline JSString
* jsString(ExecState
* exec
, const UString
& s
) { return jsString(&exec
->globalData(), s
); }
485 inline JSString
* jsSingleCharacterString(ExecState
* exec
, UChar c
) { return jsSingleCharacterString(&exec
->globalData(), c
); }
486 inline JSString
* jsSingleCharacterSubstring(ExecState
* exec
, const UString
& s
, unsigned offset
) { return jsSingleCharacterSubstring(&exec
->globalData(), s
, offset
); }
487 inline JSString
* jsSubstring(ExecState
* exec
, const UString
& s
, unsigned offset
, unsigned length
) { return jsSubstring(&exec
->globalData(), s
, offset
, length
); }
488 inline JSString
* jsNontrivialString(ExecState
* exec
, const UString
& s
) { return jsNontrivialString(&exec
->globalData(), s
); }
489 inline JSString
* jsNontrivialString(ExecState
* exec
, const char* s
) { return jsNontrivialString(&exec
->globalData(), s
); }
490 inline JSString
* jsOwnedString(ExecState
* exec
, const UString
& s
) { return jsOwnedString(&exec
->globalData(), s
); }
492 ALWAYS_INLINE
bool JSString::getStringPropertySlot(ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
494 if (propertyName
== exec
->propertyNames().length
) {
495 slot
.setValue(jsNumber(exec
, m_stringLength
));
500 unsigned i
= propertyName
.toStrictUInt32(&isStrictUInt32
);
501 if (isStrictUInt32
&& i
< m_stringLength
) {
502 slot
.setValue(jsSingleCharacterSubstring(exec
, value(exec
), i
));
509 ALWAYS_INLINE
bool JSString::getStringPropertySlot(ExecState
* exec
, unsigned propertyName
, PropertySlot
& slot
)
511 if (propertyName
< m_stringLength
) {
512 slot
.setValue(jsSingleCharacterSubstring(exec
, value(exec
), propertyName
));
519 inline bool isJSString(JSGlobalData
* globalData
, JSValue v
) { return v
.isCell() && v
.asCell()->vptr() == globalData
->jsStringVPtr
; }
521 // --- JSValue inlines ----------------------------
523 inline JSString
* JSValue::toThisJSString(ExecState
* exec
)
525 return isCell() ? asCell()->toThisJSString(exec
) : jsString(exec
, toString(exec
));
528 inline UString
JSValue::toString(ExecState
* exec
) const
531 return static_cast<JSString
*>(asCell())->value(exec
);
533 return exec
->globalData().numericStrings
.add(asInt32());
535 return exec
->globalData().numericStrings
.add(asDouble());
545 return asCell()->toString(exec
);
548 inline UString
JSValue::toPrimitiveString(ExecState
* exec
) const
551 return static_cast<JSString
*>(asCell())->value(exec
);
553 return exec
->globalData().numericStrings
.add(asInt32());
555 return exec
->globalData().numericStrings
.add(asDouble());
565 return asCell()->toPrimitive(exec
, NoPreference
).toString(exec
);