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 "PropertyDescriptor.h"
30 #include "PropertySlot.h"
32 #include "Structure.h"
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
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);
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;
69 friend class SpecializedThunkJIT;
70 friend struct ThunkHelpers;
74 RopeBuilder(unsigned fiberCount)
76 , m_rope(RopeImpl::tryCreateUninitialized(fiberCount))
80 bool isOutOfMemory() { return !m_rope; }
82 void append(RopeImpl::Fiber& fiber)
85 m_rope->initializeFiber(m_index, fiber);
87 void append(const UString& string)
90 m_rope->initializeFiber(m_index, string.impl());
92 void append(JSString* jsString)
94 if (jsString->isRope()) {
95 for (unsigned i = 0; i < jsString->m_fiberCount; ++i)
96 append(jsString->m_other.m_fibers[i]);
98 append(jsString->string());
101 PassRefPtr<RopeImpl> release()
103 ASSERT(m_index == m_rope->fiberCount());
104 return m_rope.release();
107 unsigned length() { return m_rope->length(); }
111 RefPtr<RopeImpl> m_rope;
118 RopeIterator(RopeImpl::Fiber* fibers, size_t fiberCount)
121 m_workQueue.append(WorkItem(fibers, fiberCount));
125 RopeIterator& operator++()
127 WorkItem& item = m_workQueue.last();
128 ASSERT(!RopeImpl::isRope(item.fibers[item.i]));
129 if (++item.i == item.fiberCount)
130 m_workQueue.removeLast();
135 StringImpl* operator*()
137 WorkItem& item = m_workQueue.last();
138 RopeImpl::Fiber fiber = item.fibers[item.i];
139 ASSERT(!RopeImpl::isRope(fiber));
140 return static_cast<StringImpl*>(fiber);
143 bool operator!=(const RopeIterator& other) const
145 return m_workQueue != other.m_workQueue;
150 WorkItem(RopeImpl::Fiber* fibers, size_t fiberCount)
152 , fiberCount(fiberCount)
157 bool operator!=(const WorkItem& other) const
159 return fibers != other.fibers || fiberCount != other.fiberCount || i != other.i;
162 RopeImpl::Fiber* fibers;
169 if (m_workQueue.isEmpty())
173 WorkItem& item = m_workQueue.last();
174 RopeImpl::Fiber fiber = item.fibers[item.i];
175 if (!RopeImpl::isRope(fiber))
177 RopeImpl* rope = static_cast<RopeImpl*>(fiber);
178 if (++item.i == item.fiberCount)
179 m_workQueue.removeLast();
180 m_workQueue.append(WorkItem(rope->fibers(), rope->fiberCount()));
184 Vector<WorkItem, 16> m_workQueue;
187 ALWAYS_INLINE JSString(JSGlobalData* globalData, const UString& value)
188 : JSCell(*globalData, globalData->stringStructure.get())
189 , m_length(value.length())
193 ASSERT(!m_value.isNull());
194 Heap::heap(this)->reportExtraMemoryCost(value.impl()->cost());
197 enum HasOtherOwnerType { HasOtherOwner };
198 JSString(JSGlobalData* globalData, const UString& value, HasOtherOwnerType)
199 : JSCell(*globalData, globalData->stringStructure.get())
200 , m_length(value.length())
204 ASSERT(!m_value.isNull());
206 JSString(JSGlobalData* globalData, PassRefPtr<StringImpl> value, HasOtherOwnerType)
207 : JSCell(*globalData, globalData->stringStructure.get())
208 , m_length(value->length())
212 ASSERT(!m_value.isNull());
214 JSString(JSGlobalData* globalData, PassRefPtr<RopeImpl> rope)
215 : JSCell(*globalData, globalData->stringStructure.get())
216 , m_length(rope->length())
219 m_other.m_fibers[0] = rope.leakRef();
221 // This constructor constructs a new string by concatenating s1 & s2.
222 // This should only be called with fiberCount <= 3.
223 JSString(JSGlobalData* globalData, unsigned fiberCount, JSString* s1, JSString* s2)
224 : JSCell(*globalData, globalData->stringStructure.get())
225 , m_length(s1->length() + s2->length())
226 , m_fiberCount(fiberCount)
228 ASSERT(fiberCount <= s_maxInternalRopeLength);
230 appendStringInConstruct(index, s1);
231 appendStringInConstruct(index, s2);
232 ASSERT(fiberCount == index);
234 // This constructor constructs a new string by concatenating s1 & s2.
235 // This should only be called with fiberCount <= 3.
236 JSString(JSGlobalData* globalData, unsigned fiberCount, JSString* s1, const UString& u2)
237 : JSCell(*globalData, globalData->stringStructure.get())
238 , m_length(s1->length() + u2.length())
239 , m_fiberCount(fiberCount)
241 ASSERT(fiberCount <= s_maxInternalRopeLength);
243 appendStringInConstruct(index, s1);
244 appendStringInConstruct(index, u2);
245 ASSERT(fiberCount == index);
247 // This constructor constructs a new string by concatenating s1 & s2.
248 // This should only be called with fiberCount <= 3.
249 JSString(JSGlobalData* globalData, unsigned fiberCount, const UString& u1, JSString* s2)
250 : JSCell(*globalData, globalData->stringStructure.get())
251 , m_length(u1.length() + s2->length())
252 , m_fiberCount(fiberCount)
254 ASSERT(fiberCount <= s_maxInternalRopeLength);
256 appendStringInConstruct(index, u1);
257 appendStringInConstruct(index, s2);
258 ASSERT(fiberCount == index);
260 // This constructor constructs a new string by concatenating v1, v2 & v3.
261 // This should only be called with fiberCount <= 3 ... which since every
262 // value must require a fiberCount of at least one implies that the length
263 // for each value must be exactly 1!
264 JSString(ExecState* exec, JSValue v1, JSValue v2, JSValue v3)
265 : JSCell(exec->globalData(), exec->globalData().stringStructure.get())
267 , m_fiberCount(s_maxInternalRopeLength)
270 appendValueInConstructAndIncrementLength(exec, index, v1);
271 appendValueInConstructAndIncrementLength(exec, index, v2);
272 appendValueInConstructAndIncrementLength(exec, index, v3);
273 ASSERT(index == s_maxInternalRopeLength);
276 // This constructor constructs a new string by concatenating u1 & u2.
277 JSString(JSGlobalData* globalData, const UString& u1, const UString& u2)
278 : JSCell(*globalData, globalData->stringStructure.get())
279 , m_length(u1.length() + u2.length())
283 appendStringInConstruct(index, u1);
284 appendStringInConstruct(index, u2);
285 ASSERT(index <= s_maxInternalRopeLength);
288 // This constructor constructs a new string by concatenating u1, u2 & u3.
289 JSString(JSGlobalData* globalData, const UString& u1, const UString& u2, const UString& u3)
290 : JSCell(*globalData, globalData->stringStructure.get())
291 , m_length(u1.length() + u2.length() + u3.length())
292 , m_fiberCount(s_maxInternalRopeLength)
295 appendStringInConstruct(index, u1);
296 appendStringInConstruct(index, u2);
297 appendStringInConstruct(index, u3);
298 ASSERT(index <= s_maxInternalRopeLength);
301 JSString(JSGlobalData* globalData, const UString& value, JSStringFinalizerCallback finalizer, void* context)
302 : JSCell(*globalData, globalData->stringStructure.get())
303 , m_length(value.length())
307 ASSERT(!m_value.isNull());
308 // nasty hack because we can't union non-POD types
309 m_other.m_finalizerCallback = finalizer;
310 m_other.m_finalizerContext = context;
311 Heap::heap(this)->reportExtraMemoryCost(value.impl()->cost());
316 ASSERT(vptr() == JSGlobalData::jsStringVPtr);
318 if (m_other.m_finalizerCallback)
319 m_other.m_finalizerCallback(this, m_other.m_finalizerContext);
323 RopeImpl::deref(m_other.m_fibers[i]);
324 while (++i < m_fiberCount);
328 const UString& value(ExecState* exec) const
334 const UString& tryGetValue() const
340 unsigned length() { return m_length; }
342 bool getStringPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
343 bool getStringPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
344 bool getStringPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor&);
346 bool canGetIndex(unsigned i) { return i < m_length; }
347 JSString* getIndex(ExecState*, unsigned);
348 JSString* getIndexSlowCase(ExecState*, unsigned);
350 JSValue replaceCharacter(ExecState*, UChar, const UString& replacement);
352 static Structure* createStructure(JSGlobalData& globalData, JSValue proto) { return Structure::create(globalData, proto, TypeInfo(StringType, OverridesGetOwnPropertySlot | NeedsThisConversion), AnonymousSlotCount, 0); }
355 JSString(VPtrStealingHackType)
356 : JSCell(VPtrStealingHack)
361 void resolveRope(ExecState*) const;
362 JSString* substringFromRope(ExecState*, unsigned offset, unsigned length);
364 void appendStringInConstruct(unsigned& index, const UString& string)
366 StringImpl* impl = string.impl();
368 m_other.m_fibers[index++] = impl;
371 void appendStringInConstruct(unsigned& index, JSString* jsString)
373 if (jsString->isRope()) {
374 for (unsigned i = 0; i < jsString->m_fiberCount; ++i) {
375 RopeImpl::Fiber fiber = jsString->m_other.m_fibers[i];
377 m_other.m_fibers[index++] = fiber;
380 appendStringInConstruct(index, jsString->string());
383 void appendValueInConstructAndIncrementLength(ExecState* exec, unsigned& index, JSValue v)
386 ASSERT(v.asCell()->isString());
387 JSString* s = static_cast<JSString*>(v.asCell());
388 ASSERT(s->fiberCount() == 1);
389 appendStringInConstruct(index, s);
390 m_length += s->length();
392 UString u(v.toString(exec));
393 StringImpl* impl = u.impl();
395 m_other.m_fibers[index++] = impl;
396 m_length += u.length();
400 virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const;
401 virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue& value);
402 virtual bool toBoolean(ExecState*) const;
403 virtual double toNumber(ExecState*) const;
404 virtual JSObject* toObject(ExecState*, JSGlobalObject*) const;
405 virtual UString toString(ExecState*) const;
407 virtual JSObject* toThisObject(ExecState*) const;
409 // Actually getPropertySlot, not getOwnPropertySlot (see JSCell).
410 virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
411 virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
412 virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&);
414 static const unsigned s_maxInternalRopeLength = 3;
416 // A string is represented either by a UString or a RopeImpl.
418 mutable UString m_value;
419 mutable unsigned m_fiberCount;
420 // This structure exists to support a temporary workaround for a GC issue.
421 struct JSStringFinalizerStruct {
422 JSStringFinalizerStruct() : m_finalizerCallback(0) {}
424 mutable FixedArray<RopeImpl::Fiber, s_maxInternalRopeLength> m_fibers;
426 JSStringFinalizerCallback m_finalizerCallback;
427 void* m_finalizerContext;
432 bool isRope() const { return m_fiberCount; }
433 UString& string() { ASSERT(!isRope()); return m_value; }
434 unsigned fiberCount() { return m_fiberCount ? m_fiberCount : 1; }
436 friend JSValue jsString(ExecState* exec, JSString* s1, JSString* s2);
437 friend JSValue jsString(ExecState* exec, const UString& u1, JSString* s2);
438 friend JSValue jsString(ExecState* exec, JSString* s1, const UString& u2);
439 friend JSValue jsString(ExecState* exec, Register* strings, unsigned count);
440 friend JSValue jsString(ExecState* exec, JSValue thisValue);
441 friend JSString* jsStringWithFinalizer(ExecState*, const UString&, JSStringFinalizerCallback callback, void* context);
442 friend JSString* jsSubstring(ExecState* exec, JSString* s, unsigned offset, unsigned length);
445 JSString* asString(JSValue);
447 // When an object is created from a different DLL, MSVC changes vptr to a "local" one right after invoking a constructor,
448 // see <http://groups.google.com/group/microsoft.public.vc.language/msg/55cdcefeaf770212>.
449 // This breaks isJSString(), and we don't need that hack anyway, so we change vptr back to primary one.
450 // The below function must be called by any inline function that invokes a JSString constructor.
451 #if COMPILER(MSVC) && !defined(BUILDING_JavaScriptCore)
452 inline JSString* fixupVPtr(JSGlobalData* globalData, JSString* string) { string->setVPtr(globalData->jsStringVPtr); return string; }
454 inline JSString* fixupVPtr(JSGlobalData*, JSString* string) { return string; }
457 inline JSString* asString(JSValue value)
459 ASSERT(value.asCell()->isString());
460 return static_cast<JSString*>(value.asCell());
463 inline JSString* jsEmptyString(JSGlobalData* globalData)
465 return globalData->smallStrings.emptyString(globalData);
468 inline JSString* jsSingleCharacterString(JSGlobalData* globalData, UChar c)
470 if (c <= maxSingleCharacterString)
471 return globalData->smallStrings.singleCharacterString(globalData, c);
472 return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(&c, 1)));
475 inline JSString* jsSingleCharacterSubstring(ExecState* exec, const UString& s, unsigned offset)
477 JSGlobalData* globalData = &exec->globalData();
478 ASSERT(offset < static_cast<unsigned>(s.length()));
479 UChar c = s.characters()[offset];
480 if (c <= maxSingleCharacterString)
481 return globalData->smallStrings.singleCharacterString(globalData, c);
482 return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(StringImpl::create(s.impl(), offset, 1))));
485 inline JSString* jsNontrivialString(JSGlobalData* globalData, const char* s)
490 return fixupVPtr(globalData, new (globalData) JSString(globalData, s));
493 inline JSString* jsNontrivialString(JSGlobalData* globalData, const UString& s)
495 ASSERT(s.length() > 1);
496 return fixupVPtr(globalData, new (globalData) JSString(globalData, s));
499 inline JSString* JSString::getIndex(ExecState* exec, unsigned i)
501 ASSERT(canGetIndex(i));
503 return getIndexSlowCase(exec, i);
504 ASSERT(i < m_value.length());
505 return jsSingleCharacterSubstring(exec, m_value, i);
508 inline JSString* jsString(JSGlobalData* globalData, const UString& s)
510 int size = s.length();
512 return globalData->smallStrings.emptyString(globalData);
514 UChar c = s.characters()[0];
515 if (c <= maxSingleCharacterString)
516 return globalData->smallStrings.singleCharacterString(globalData, c);
518 return fixupVPtr(globalData, new (globalData) JSString(globalData, s));
521 inline JSString* jsStringWithFinalizer(ExecState* exec, const UString& s, JSStringFinalizerCallback callback, void* context)
523 ASSERT(s.length() && (s.length() > 1 || s.characters()[0] > maxSingleCharacterString));
524 JSGlobalData* globalData = &exec->globalData();
525 return fixupVPtr(globalData, new (globalData) JSString(globalData, s, callback, context));
528 inline JSString* jsSubstring(ExecState* exec, JSString* s, unsigned offset, unsigned length)
530 ASSERT(offset <= static_cast<unsigned>(s->length()));
531 ASSERT(length <= static_cast<unsigned>(s->length()));
532 ASSERT(offset + length <= static_cast<unsigned>(s->length()));
533 JSGlobalData* globalData = &exec->globalData();
535 return globalData->smallStrings.emptyString(globalData);
537 return s->substringFromRope(exec, offset, length);
538 return jsSubstring(globalData, s->m_value, offset, length);
541 inline JSString* jsSubstring(JSGlobalData* globalData, const UString& s, unsigned offset, unsigned length)
543 ASSERT(offset <= static_cast<unsigned>(s.length()));
544 ASSERT(length <= static_cast<unsigned>(s.length()));
545 ASSERT(offset + length <= static_cast<unsigned>(s.length()));
547 return globalData->smallStrings.emptyString(globalData);
549 UChar c = s.characters()[offset];
550 if (c <= maxSingleCharacterString)
551 return globalData->smallStrings.singleCharacterString(globalData, c);
553 return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(StringImpl::create(s.impl(), offset, length)), JSString::HasOtherOwner));
556 inline JSString* jsOwnedString(JSGlobalData* globalData, const UString& s)
558 int size = s.length();
560 return globalData->smallStrings.emptyString(globalData);
562 UChar c = s.characters()[0];
563 if (c <= maxSingleCharacterString)
564 return globalData->smallStrings.singleCharacterString(globalData, c);
566 return fixupVPtr(globalData, new (globalData) JSString(globalData, s, JSString::HasOtherOwner));
569 inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->globalData()); }
570 inline JSString* jsString(ExecState* exec, const UString& s) { return jsString(&exec->globalData(), s); }
571 inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->globalData(), c); }
572 inline JSString* jsSubstring(ExecState* exec, const UString& s, unsigned offset, unsigned length) { return jsSubstring(&exec->globalData(), s, offset, length); }
573 inline JSString* jsNontrivialString(ExecState* exec, const UString& s) { return jsNontrivialString(&exec->globalData(), s); }
574 inline JSString* jsNontrivialString(ExecState* exec, const char* s) { return jsNontrivialString(&exec->globalData(), s); }
575 inline JSString* jsOwnedString(ExecState* exec, const UString& s) { return jsOwnedString(&exec->globalData(), s); }
577 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
579 if (propertyName == exec->propertyNames().length) {
580 slot.setValue(jsNumber(m_length));
585 unsigned i = propertyName.toUInt32(isStrictUInt32);
586 if (isStrictUInt32 && i < m_length) {
587 slot.setValue(getIndex(exec, i));
594 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
596 if (propertyName < m_length) {
597 slot.setValue(getIndex(exec, propertyName));
604 inline bool isJSString(JSGlobalData* globalData, JSValue v) { return v.isCell() && v.asCell()->vptr() == globalData->jsStringVPtr; }
606 // --- JSValue inlines ----------------------------
608 inline UString JSValue::toString(ExecState* exec) const
611 return static_cast<JSString*>(asCell())->value(exec);
613 return exec->globalData().numericStrings.add(asInt32());
615 return exec->globalData().numericStrings.add(asDouble());
625 return asCell()->toString(exec);
628 inline UString JSValue::toPrimitiveString(ExecState* exec) const
632 return exec->globalData().numericStrings.add(asInt32());
634 return exec->globalData().numericStrings.add(asDouble());
644 return asCell()->toPrimitive(exec, NoPreference).toString(exec);