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 class JS_EXPORTCLASS JSString
: public JSCell
{
65 friend class JSGlobalData
;
66 friend class SpecializedThunkJIT
;
67 friend struct ThunkHelpers
;
71 RopeBuilder(unsigned fiberCount
)
73 , m_rope(RopeImpl::tryCreateUninitialized(fiberCount
))
77 bool isOutOfMemory() { return !m_rope
; }
79 void append(RopeImpl::Fiber
& fiber
)
82 m_rope
->initializeFiber(m_index
, fiber
);
84 void append(const UString
& string
)
87 m_rope
->initializeFiber(m_index
, string
.impl());
89 void append(JSString
* jsString
)
91 if (jsString
->isRope()) {
92 for (unsigned i
= 0; i
< jsString
->m_fiberCount
; ++i
)
93 append(jsString
->m_fibers
[i
]);
95 append(jsString
->string());
98 PassRefPtr
<RopeImpl
> release()
100 ASSERT(m_index
== m_rope
->fiberCount());
101 return m_rope
.release();
104 unsigned length() { return m_rope
->length(); }
108 RefPtr
<RopeImpl
> m_rope
;
115 RopeIterator(RopeImpl::Fiber
* fibers
, size_t fiberCount
)
118 m_workQueue
.append(WorkItem(fibers
, fiberCount
));
122 RopeIterator
& operator++()
124 WorkItem
& item
= m_workQueue
.last();
125 ASSERT(!RopeImpl::isRope(item
.fibers
[item
.i
]));
126 if (++item
.i
== item
.fiberCount
)
127 m_workQueue
.removeLast();
132 StringImpl
* operator*()
134 WorkItem
& item
= m_workQueue
.last();
135 RopeImpl::Fiber fiber
= item
.fibers
[item
.i
];
136 ASSERT(!RopeImpl::isRope(fiber
));
137 return static_cast<StringImpl
*>(fiber
);
140 bool operator!=(const RopeIterator
& other
) const
142 return m_workQueue
!= other
.m_workQueue
;
147 WorkItem(RopeImpl::Fiber
* fibers
, size_t fiberCount
)
149 , fiberCount(fiberCount
)
154 bool operator!=(const WorkItem
& other
) const
156 return fibers
!= other
.fibers
|| fiberCount
!= other
.fiberCount
|| i
!= other
.i
;
159 RopeImpl::Fiber
* fibers
;
166 if (m_workQueue
.isEmpty())
170 WorkItem
& item
= m_workQueue
.last();
171 RopeImpl::Fiber fiber
= item
.fibers
[item
.i
];
172 if (!RopeImpl::isRope(fiber
))
174 RopeImpl
* rope
= static_cast<RopeImpl
*>(fiber
);
175 if (++item
.i
== item
.fiberCount
)
176 m_workQueue
.removeLast();
177 m_workQueue
.append(WorkItem(rope
->fibers(), rope
->fiberCount()));
181 Vector
<WorkItem
, 16> m_workQueue
;
184 ALWAYS_INLINE
JSString(JSGlobalData
* globalData
, const UString
& value
)
185 : JSCell(*globalData
, globalData
->stringStructure
.get())
186 , m_length(value
.length())
190 ASSERT(!m_value
.isNull());
191 Heap::heap(this)->reportExtraMemoryCost(value
.impl()->cost());
194 enum HasOtherOwnerType
{ HasOtherOwner
};
195 JSString(JSGlobalData
* globalData
, const UString
& value
, HasOtherOwnerType
)
196 : JSCell(*globalData
, globalData
->stringStructure
.get())
197 , m_length(value
.length())
201 ASSERT(!m_value
.isNull());
203 JSString(JSGlobalData
* globalData
, PassRefPtr
<StringImpl
> value
, HasOtherOwnerType
)
204 : JSCell(*globalData
, globalData
->stringStructure
.get())
205 , m_length(value
->length())
209 ASSERT(!m_value
.isNull());
211 JSString(JSGlobalData
* globalData
, PassRefPtr
<RopeImpl
> rope
)
212 : JSCell(*globalData
, globalData
->stringStructure
.get())
213 , m_length(rope
->length())
216 m_fibers
[0] = rope
.leakRef();
218 // This constructor constructs a new string by concatenating s1 & s2.
219 // This should only be called with fiberCount <= 3.
220 JSString(JSGlobalData
* globalData
, unsigned fiberCount
, JSString
* s1
, JSString
* s2
)
221 : JSCell(*globalData
, globalData
->stringStructure
.get())
222 , m_length(s1
->length() + s2
->length())
223 , m_fiberCount(fiberCount
)
225 ASSERT(fiberCount
<= s_maxInternalRopeLength
);
227 appendStringInConstruct(index
, s1
);
228 appendStringInConstruct(index
, s2
);
229 ASSERT(fiberCount
== index
);
231 // This constructor constructs a new string by concatenating s1 & s2.
232 // This should only be called with fiberCount <= 3.
233 JSString(JSGlobalData
* globalData
, unsigned fiberCount
, JSString
* s1
, const UString
& u2
)
234 : JSCell(*globalData
, globalData
->stringStructure
.get())
235 , m_length(s1
->length() + u2
.length())
236 , m_fiberCount(fiberCount
)
238 ASSERT(fiberCount
<= s_maxInternalRopeLength
);
240 appendStringInConstruct(index
, s1
);
241 appendStringInConstruct(index
, u2
);
242 ASSERT(fiberCount
== index
);
244 // This constructor constructs a new string by concatenating s1 & s2.
245 // This should only be called with fiberCount <= 3.
246 JSString(JSGlobalData
* globalData
, unsigned fiberCount
, const UString
& u1
, JSString
* s2
)
247 : JSCell(*globalData
, globalData
->stringStructure
.get())
248 , m_length(u1
.length() + s2
->length())
249 , m_fiberCount(fiberCount
)
251 ASSERT(fiberCount
<= s_maxInternalRopeLength
);
253 appendStringInConstruct(index
, u1
);
254 appendStringInConstruct(index
, s2
);
255 ASSERT(fiberCount
== index
);
257 // This constructor constructs a new string by concatenating v1, v2 & v3.
258 // This should only be called with fiberCount <= 3 ... which since every
259 // value must require a fiberCount of at least one implies that the length
260 // for each value must be exactly 1!
261 JSString(ExecState
* exec
, JSValue v1
, JSValue v2
, JSValue v3
)
262 : JSCell(exec
->globalData(), exec
->globalData().stringStructure
.get())
264 , m_fiberCount(s_maxInternalRopeLength
)
267 appendValueInConstructAndIncrementLength(exec
, index
, v1
);
268 appendValueInConstructAndIncrementLength(exec
, index
, v2
);
269 appendValueInConstructAndIncrementLength(exec
, index
, v3
);
270 ASSERT(index
== s_maxInternalRopeLength
);
273 // This constructor constructs a new string by concatenating u1 & u2.
274 JSString(JSGlobalData
* globalData
, const UString
& u1
, const UString
& u2
)
275 : JSCell(*globalData
, globalData
->stringStructure
.get())
276 , m_length(u1
.length() + u2
.length())
280 appendStringInConstruct(index
, u1
);
281 appendStringInConstruct(index
, u2
);
282 ASSERT(index
<= s_maxInternalRopeLength
);
285 // This constructor constructs a new string by concatenating u1, u2 & u3.
286 JSString(JSGlobalData
* globalData
, const UString
& u1
, const UString
& u2
, const UString
& u3
)
287 : JSCell(*globalData
, globalData
->stringStructure
.get())
288 , m_length(u1
.length() + u2
.length() + u3
.length())
289 , m_fiberCount(s_maxInternalRopeLength
)
292 appendStringInConstruct(index
, u1
);
293 appendStringInConstruct(index
, u2
);
294 appendStringInConstruct(index
, u3
);
295 ASSERT(index
<= s_maxInternalRopeLength
);
300 ASSERT(vptr() == JSGlobalData::jsStringVPtr
);
301 for (unsigned i
= 0; i
< m_fiberCount
; ++i
)
302 RopeImpl::deref(m_fibers
[i
]);
305 const UString
& value(ExecState
* exec
) const
311 const UString
& tryGetValue() const
317 unsigned length() { return m_length
; }
319 bool getStringPropertySlot(ExecState
*, const Identifier
& propertyName
, PropertySlot
&);
320 bool getStringPropertySlot(ExecState
*, unsigned propertyName
, PropertySlot
&);
321 bool getStringPropertyDescriptor(ExecState
*, const Identifier
& propertyName
, PropertyDescriptor
&);
323 bool canGetIndex(unsigned i
) { return i
< m_length
; }
324 JSString
* getIndex(ExecState
*, unsigned);
325 JSString
* getIndexSlowCase(ExecState
*, unsigned);
327 JSValue
replaceCharacter(ExecState
*, UChar
, const UString
& replacement
);
329 static Structure
* createStructure(JSGlobalData
& globalData
, JSValue proto
)
331 return Structure::create(globalData
, proto
, TypeInfo(StringType
, OverridesGetOwnPropertySlot
| NeedsThisConversion
), AnonymousSlotCount
, &s_info
);
334 static const ClassInfo s_info
;
337 JSString(VPtrStealingHackType
)
338 : JSCell(VPtrStealingHack
)
343 void resolveRope(ExecState
*) const;
344 void resolveRopeSlowCase(ExecState
*, UChar
*) const;
345 void outOfMemory(ExecState
*) const;
346 JSString
* substringFromRope(ExecState
*, unsigned offset
, unsigned length
);
348 void appendStringInConstruct(unsigned& index
, const UString
& string
)
350 StringImpl
* impl
= string
.impl();
352 m_fibers
[index
++] = impl
;
355 void appendStringInConstruct(unsigned& index
, JSString
* jsString
)
357 if (jsString
->isRope()) {
358 for (unsigned i
= 0; i
< jsString
->m_fiberCount
; ++i
) {
359 RopeImpl::Fiber fiber
= jsString
->m_fibers
[i
];
361 m_fibers
[index
++] = fiber
;
364 appendStringInConstruct(index
, jsString
->string());
367 void appendValueInConstructAndIncrementLength(ExecState
* exec
, unsigned& index
, JSValue v
)
370 ASSERT(v
.asCell()->isString());
371 JSString
* s
= static_cast<JSString
*>(v
.asCell());
372 ASSERT(s
->fiberCount() == 1);
373 appendStringInConstruct(index
, s
);
374 m_length
+= s
->length();
376 UString
u(v
.toString(exec
));
377 StringImpl
* impl
= u
.impl();
379 m_fibers
[index
++] = impl
;
380 m_length
+= u
.length();
384 virtual JSValue
toPrimitive(ExecState
*, PreferredPrimitiveType
) const;
385 virtual bool getPrimitiveNumber(ExecState
*, double& number
, JSValue
& value
);
386 virtual bool toBoolean(ExecState
*) const;
387 virtual double toNumber(ExecState
*) const;
388 virtual JSObject
* toObject(ExecState
*, JSGlobalObject
*) const;
389 virtual UString
toString(ExecState
*) const;
391 virtual JSObject
* toThisObject(ExecState
*) const;
393 // Actually getPropertySlot, not getOwnPropertySlot (see JSCell).
394 virtual bool getOwnPropertySlot(ExecState
*, const Identifier
& propertyName
, PropertySlot
&);
395 virtual bool getOwnPropertySlot(ExecState
*, unsigned propertyName
, PropertySlot
&);
396 virtual bool getOwnPropertyDescriptor(ExecState
*, const Identifier
&, PropertyDescriptor
&);
398 static const unsigned s_maxInternalRopeLength
= 3;
400 // A string is represented either by a UString or a RopeImpl.
402 mutable UString m_value
;
403 mutable unsigned m_fiberCount
;
404 mutable FixedArray
<RopeImpl::Fiber
, s_maxInternalRopeLength
> m_fibers
;
406 bool isRope() const { return m_fiberCount
; }
407 UString
& string() { ASSERT(!isRope()); return m_value
; }
408 unsigned fiberCount() { return m_fiberCount
? m_fiberCount
: 1; }
410 friend JSValue
jsString(ExecState
* exec
, JSString
* s1
, JSString
* s2
);
411 friend JSValue
jsString(ExecState
* exec
, const UString
& u1
, JSString
* s2
);
412 friend JSValue
jsString(ExecState
* exec
, JSString
* s1
, const UString
& u2
);
413 friend JSValue
jsString(ExecState
* exec
, Register
* strings
, unsigned count
);
414 friend JSValue
jsString(ExecState
* exec
, JSValue thisValue
);
415 friend JSString
* jsSubstring(ExecState
* exec
, JSString
* s
, unsigned offset
, unsigned length
);
418 JSString
* asString(JSValue
);
420 // When an object is created from a different DLL, MSVC changes vptr to a "local" one right after invoking a constructor,
421 // see <http://groups.google.com/group/microsoft.public.vc.language/msg/55cdcefeaf770212>.
422 // This breaks isJSString(), and we don't need that hack anyway, so we change vptr back to primary one.
423 // The below function must be called by any inline function that invokes a JSString constructor.
424 #if COMPILER(MSVC) && !defined(BUILDING_JavaScriptCore)
425 inline JSString
* fixupVPtr(JSGlobalData
* globalData
, JSString
* string
) { string
->setVPtr(globalData
->jsStringVPtr
); return string
; }
427 inline JSString
* fixupVPtr(JSGlobalData
*, JSString
* string
) { return string
; }
430 inline JSString
* asString(JSValue value
)
432 ASSERT(value
.asCell()->isString());
433 return static_cast<JSString
*>(value
.asCell());
436 inline JSString
* jsEmptyString(JSGlobalData
* globalData
)
438 return globalData
->smallStrings
.emptyString(globalData
);
441 inline JSString
* jsSingleCharacterString(JSGlobalData
* globalData
, UChar c
)
443 if (c
<= maxSingleCharacterString
)
444 return globalData
->smallStrings
.singleCharacterString(globalData
, c
);
445 return fixupVPtr(globalData
, new (globalData
) JSString(globalData
, UString(&c
, 1)));
448 inline JSString
* jsSingleCharacterSubstring(ExecState
* exec
, const UString
& s
, unsigned offset
)
450 JSGlobalData
* globalData
= &exec
->globalData();
451 ASSERT(offset
< static_cast<unsigned>(s
.length()));
452 UChar c
= s
.characters()[offset
];
453 if (c
<= maxSingleCharacterString
)
454 return globalData
->smallStrings
.singleCharacterString(globalData
, c
);
455 return fixupVPtr(globalData
, new (globalData
) JSString(globalData
, UString(StringImpl::create(s
.impl(), offset
, 1))));
458 inline JSString
* jsNontrivialString(JSGlobalData
* globalData
, const char* s
)
463 return fixupVPtr(globalData
, new (globalData
) JSString(globalData
, s
));
466 inline JSString
* jsNontrivialString(JSGlobalData
* globalData
, const UString
& s
)
468 ASSERT(s
.length() > 1);
469 return fixupVPtr(globalData
, new (globalData
) JSString(globalData
, s
));
472 inline JSString
* JSString::getIndex(ExecState
* exec
, unsigned i
)
474 ASSERT(canGetIndex(i
));
476 return getIndexSlowCase(exec
, i
);
477 ASSERT(i
< m_value
.length());
478 return jsSingleCharacterSubstring(exec
, m_value
, i
);
481 inline JSString
* jsString(JSGlobalData
* globalData
, const UString
& s
)
483 int size
= s
.length();
485 return globalData
->smallStrings
.emptyString(globalData
);
487 UChar c
= s
.characters()[0];
488 if (c
<= maxSingleCharacterString
)
489 return globalData
->smallStrings
.singleCharacterString(globalData
, c
);
491 return fixupVPtr(globalData
, new (globalData
) JSString(globalData
, s
));
494 inline JSString
* jsSubstring(ExecState
* exec
, JSString
* s
, unsigned offset
, unsigned length
)
496 ASSERT(offset
<= static_cast<unsigned>(s
->length()));
497 ASSERT(length
<= static_cast<unsigned>(s
->length()));
498 ASSERT(offset
+ length
<= static_cast<unsigned>(s
->length()));
499 JSGlobalData
* globalData
= &exec
->globalData();
501 return globalData
->smallStrings
.emptyString(globalData
);
503 return s
->substringFromRope(exec
, offset
, length
);
504 return jsSubstring(globalData
, s
->m_value
, offset
, length
);
507 inline JSString
* jsSubstring(JSGlobalData
* globalData
, const UString
& s
, unsigned offset
, unsigned length
)
509 ASSERT(offset
<= static_cast<unsigned>(s
.length()));
510 ASSERT(length
<= static_cast<unsigned>(s
.length()));
511 ASSERT(offset
+ length
<= static_cast<unsigned>(s
.length()));
513 return globalData
->smallStrings
.emptyString(globalData
);
515 UChar c
= s
.characters()[offset
];
516 if (c
<= maxSingleCharacterString
)
517 return globalData
->smallStrings
.singleCharacterString(globalData
, c
);
519 return fixupVPtr(globalData
, new (globalData
) JSString(globalData
, UString(StringImpl::create(s
.impl(), offset
, length
)), JSString::HasOtherOwner
));
522 inline JSString
* jsOwnedString(JSGlobalData
* globalData
, const UString
& s
)
524 int size
= s
.length();
526 return globalData
->smallStrings
.emptyString(globalData
);
528 UChar c
= s
.characters()[0];
529 if (c
<= maxSingleCharacterString
)
530 return globalData
->smallStrings
.singleCharacterString(globalData
, c
);
532 return fixupVPtr(globalData
, new (globalData
) JSString(globalData
, s
, JSString::HasOtherOwner
));
535 inline JSString
* jsEmptyString(ExecState
* exec
) { return jsEmptyString(&exec
->globalData()); }
536 inline JSString
* jsString(ExecState
* exec
, const UString
& s
) { return jsString(&exec
->globalData(), s
); }
537 inline JSString
* jsSingleCharacterString(ExecState
* exec
, UChar c
) { return jsSingleCharacterString(&exec
->globalData(), c
); }
538 inline JSString
* jsSubstring(ExecState
* exec
, const UString
& s
, unsigned offset
, unsigned length
) { return jsSubstring(&exec
->globalData(), s
, offset
, length
); }
539 inline JSString
* jsNontrivialString(ExecState
* exec
, const UString
& s
) { return jsNontrivialString(&exec
->globalData(), s
); }
540 inline JSString
* jsNontrivialString(ExecState
* exec
, const char* s
) { return jsNontrivialString(&exec
->globalData(), s
); }
541 inline JSString
* jsOwnedString(ExecState
* exec
, const UString
& s
) { return jsOwnedString(&exec
->globalData(), s
); }
543 ALWAYS_INLINE
bool JSString::getStringPropertySlot(ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
545 if (propertyName
== exec
->propertyNames().length
) {
546 slot
.setValue(jsNumber(m_length
));
551 unsigned i
= propertyName
.toUInt32(isStrictUInt32
);
552 if (isStrictUInt32
&& i
< m_length
) {
553 slot
.setValue(getIndex(exec
, i
));
560 ALWAYS_INLINE
bool JSString::getStringPropertySlot(ExecState
* exec
, unsigned propertyName
, PropertySlot
& slot
)
562 if (propertyName
< m_length
) {
563 slot
.setValue(getIndex(exec
, propertyName
));
570 inline bool isJSString(JSGlobalData
* globalData
, JSValue v
) { return v
.isCell() && v
.asCell()->vptr() == globalData
->jsStringVPtr
; }
572 // --- JSValue inlines ----------------------------
574 inline UString
JSValue::toString(ExecState
* exec
) const
577 return static_cast<JSString
*>(asCell())->value(exec
);
579 return exec
->globalData().numericStrings
.add(asInt32());
581 return exec
->globalData().numericStrings
.add(asDouble());
591 return asCell()->toString(exec
);
594 inline UString
JSValue::toPrimitiveString(ExecState
* exec
) const
598 return exec
->globalData().numericStrings
.add(asInt32());
600 return exec
->globalData().numericStrings
.add(asDouble());
610 return asCell()->toPrimitive(exec
, NoPreference
).toString(exec
);