source: webkit/trunk/JavaScriptCore/runtime/JSString.h@ 57019

Last change on this file since 57019 was 57019, checked in by [email protected], 15 years ago

Inlined toThisString and toThisJSString to avoid virtual function call overhead
https://bugs.webkit.org/show_bug.cgi?id=37039

Reviewed by Oliver Hunt.

Maybe a 1% speedup on iBench JS.

  • runtime/JSCell.cpp:
  • runtime/JSCell.h:
  • runtime/JSNumberCell.cpp:
  • runtime/JSNumberCell.h:
  • runtime/JSString.cpp:
  • runtime/JSString.h:
  • runtime/JSValue.h:
  • runtime/JSZombie.h:

(JSC::JSZombie::toThisObject): Nixed the old virtual-type implementation.

  • runtime/JSObject.h:

(JSC::JSValue::toThisString):
(JSC::JSValue::toThisJSString): Added the inlined implementation.

  • Property svn:eol-style set to native
File size: 21.2 KB
Line 
1/*
2 * Copyright (C) 1999-2001 Harri Porten ([email protected])
3 * Copyright (C) 2001 Peter Kelly ([email protected])
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
26#include "CallFrame.h"
27#include "CommonIdentifiers.h"
28#include "Identifier.h"
29#include "JSNumberCell.h"
30#include "PropertyDescriptor.h"
31#include "PropertySlot.h"
32
33namespace JSC {
34
35 class JSString;
36
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
41
42 JSString* jsSingleCharacterString(JSGlobalData*, UChar);
43 JSString* jsSingleCharacterString(ExecState*, UChar);
44 JSString* jsSingleCharacterSubstring(ExecState*, const UString&, unsigned offset);
45 JSString* jsSubstring(JSGlobalData*, const UString&, unsigned offset, unsigned length);
46 JSString* jsSubstring(ExecState*, const UString&, unsigned offset, unsigned length);
47
48 // Non-trivial strings are two or more characters long.
49 // These functions are faster than just calling jsString.
50 JSString* jsNontrivialString(JSGlobalData*, const UString&);
51 JSString* jsNontrivialString(ExecState*, const UString&);
52 JSString* jsNontrivialString(JSGlobalData*, const char*);
53 JSString* jsNontrivialString(ExecState*, const char*);
54
55 // Should be used for strings that are owned by an object that will
56 // likely outlive the JSValue this makes, such as the parse tree or a
57 // DOM object that contains a UString
58 JSString* jsOwnedString(JSGlobalData*, const UString&);
59 JSString* jsOwnedString(ExecState*, const UString&);
60
61 typedef void (*JSStringFinalizerCallback)(JSString*, void* context);
62 JSString* jsStringWithFinalizer(ExecState*, const UString&, JSStringFinalizerCallback callback, void* context);
63
64 class JS_EXPORTCLASS JSString : public JSCell {
65 public:
66 friend class JIT;
67 friend class JSGlobalData;
68
69 typedef URopeImpl Rope;
70
71 class RopeBuilder {
72 public:
73 RopeBuilder(unsigned fiberCount)
74 : m_index(0)
75 , m_rope(Rope::tryCreateUninitialized(fiberCount))
76 {
77 }
78
79 bool isOutOfMemory() { return !m_rope; }
80
81 void append(Rope::Fiber& fiber)
82 {
83 ASSERT(m_rope);
84 m_rope->initializeFiber(m_index, fiber);
85 }
86 void append(const UString& string)
87 {
88 ASSERT(m_rope);
89 m_rope->initializeFiber(m_index, string.rep());
90 }
91 void append(JSString* jsString)
92 {
93 if (jsString->isRope()) {
94 for (unsigned i = 0; i < jsString->m_fiberCount; ++i)
95 append(jsString->m_other.m_fibers[i]);
96 } else
97 append(jsString->string());
98 }
99
100 PassRefPtr<Rope> release()
101 {
102 ASSERT(m_index == m_rope->fiberCount());
103 return m_rope.release();
104 }
105
106 unsigned length() { return m_rope->length(); }
107
108 private:
109 unsigned m_index;
110 RefPtr<Rope> m_rope;
111 };
112
113 ALWAYS_INLINE JSString(JSGlobalData* globalData, const UString& value)
114 : JSCell(globalData->stringStructure.get())
115 , m_length(value.size())
116 , m_value(value)
117 , m_fiberCount(0)
118 {
119 Heap::heap(this)->reportExtraMemoryCost(value.cost());
120 }
121
122 enum HasOtherOwnerType { HasOtherOwner };
123 JSString(JSGlobalData* globalData, const UString& value, HasOtherOwnerType)
124 : JSCell(globalData->stringStructure.get())
125 , m_length(value.size())
126 , m_value(value)
127 , m_fiberCount(0)
128 {
129 }
130 JSString(JSGlobalData* globalData, PassRefPtr<UString::Rep> value, HasOtherOwnerType)
131 : JSCell(globalData->stringStructure.get())
132 , m_length(value->length())
133 , m_value(value)
134 , m_fiberCount(0)
135 {
136 }
137 JSString(JSGlobalData* globalData, PassRefPtr<Rope> rope)
138 : JSCell(globalData->stringStructure.get())
139 , m_length(rope->length())
140 , m_fiberCount(1)
141 {
142 m_other.m_fibers[0] = rope.releaseRef();
143 }
144 // This constructor constructs a new string by concatenating s1 & s2.
145 // This should only be called with fiberCount <= 3.
146 JSString(JSGlobalData* globalData, unsigned fiberCount, JSString* s1, JSString* s2)
147 : JSCell(globalData->stringStructure.get())
148 , m_length(s1->length() + s2->length())
149 , m_fiberCount(fiberCount)
150 {
151 ASSERT(fiberCount <= s_maxInternalRopeLength);
152 unsigned index = 0;
153 appendStringInConstruct(index, s1);
154 appendStringInConstruct(index, s2);
155 ASSERT(fiberCount == index);
156 }
157 // This constructor constructs a new string by concatenating s1 & s2.
158 // This should only be called with fiberCount <= 3.
159 JSString(JSGlobalData* globalData, unsigned fiberCount, JSString* s1, const UString& u2)
160 : JSCell(globalData->stringStructure.get())
161 , m_length(s1->length() + u2.size())
162 , m_fiberCount(fiberCount)
163 {
164 ASSERT(fiberCount <= s_maxInternalRopeLength);
165 unsigned index = 0;
166 appendStringInConstruct(index, s1);
167 appendStringInConstruct(index, u2);
168 ASSERT(fiberCount == index);
169 }
170 // This constructor constructs a new string by concatenating s1 & s2.
171 // This should only be called with fiberCount <= 3.
172 JSString(JSGlobalData* globalData, unsigned fiberCount, const UString& u1, JSString* s2)
173 : JSCell(globalData->stringStructure.get())
174 , m_length(u1.size() + s2->length())
175 , m_fiberCount(fiberCount)
176 {
177 ASSERT(fiberCount <= s_maxInternalRopeLength);
178 unsigned index = 0;
179 appendStringInConstruct(index, u1);
180 appendStringInConstruct(index, s2);
181 ASSERT(fiberCount == index);
182 }
183 // This constructor constructs a new string by concatenating v1, v2 & v3.
184 // This should only be called with fiberCount <= 3 ... which since every
185 // value must require a fiberCount of at least one implies that the length
186 // for each value must be exactly 1!
187 JSString(ExecState* exec, JSValue v1, JSValue v2, JSValue v3)
188 : JSCell(exec->globalData().stringStructure.get())
189 , m_length(0)
190 , m_fiberCount(s_maxInternalRopeLength)
191 {
192 unsigned index = 0;
193 appendValueInConstructAndIncrementLength(exec, index, v1);
194 appendValueInConstructAndIncrementLength(exec, index, v2);
195 appendValueInConstructAndIncrementLength(exec, index, v3);
196 ASSERT(index == s_maxInternalRopeLength);
197 }
198
199 JSString(JSGlobalData* globalData, const UString& value, JSStringFinalizerCallback finalizer, void* context)
200 : JSCell(globalData->stringStructure.get())
201 , m_length(value.size())
202 , m_value(value)
203 , m_fiberCount(0)
204 {
205 // nasty hack because we can't union non-POD types
206 m_other.m_finalizerCallback = finalizer;
207 m_other.m_finalizerContext = context;
208 Heap::heap(this)->reportExtraMemoryCost(value.cost());
209 }
210
211 ~JSString()
212 {
213 ASSERT(vptr() == JSGlobalData::jsStringVPtr);
214 for (unsigned i = 0; i < m_fiberCount; ++i)
215 m_other.m_fibers[i]->deref();
216
217 if (!m_fiberCount && m_other.m_finalizerCallback)
218 m_other.m_finalizerCallback(this, m_other.m_finalizerContext);
219 }
220
221 const UString& value(ExecState* exec) const
222 {
223 if (isRope())
224 resolveRope(exec);
225 return m_value;
226 }
227 const UString tryGetValue() const
228 {
229 // If this is a rope, m_value should be null -
230 // if this is not a rope, m_value should be non-null.
231 ASSERT(isRope() == m_value.isNull());
232 return m_value;
233 }
234 unsigned length() { return m_length; }
235
236 bool getStringPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
237 bool getStringPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
238 bool getStringPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor&);
239
240 bool canGetIndex(unsigned i) { return i < m_length; }
241 JSString* getIndex(ExecState*, unsigned);
242 JSString* getIndexSlowCase(ExecState*, unsigned);
243
244 static PassRefPtr<Structure> createStructure(JSValue proto) { return Structure::create(proto, TypeInfo(StringType, OverridesGetOwnPropertySlot | NeedsThisConversion), AnonymousSlotCount); }
245
246 private:
247 enum VPtrStealingHackType { VPtrStealingHack };
248 JSString(VPtrStealingHackType)
249 : JSCell(0)
250 , m_fiberCount(0)
251 {
252 }
253
254 void resolveRope(ExecState*) const;
255
256 void appendStringInConstruct(unsigned& index, const UString& string)
257 {
258 UStringImpl* impl = string.rep();
259 impl->ref();
260 m_other.m_fibers[index++] = impl;
261 }
262
263 void appendStringInConstruct(unsigned& index, JSString* jsString)
264 {
265 if (jsString->isRope()) {
266 for (unsigned i = 0; i < jsString->m_fiberCount; ++i) {
267 Rope::Fiber fiber = jsString->m_other.m_fibers[i];
268 fiber->ref();
269 m_other.m_fibers[index++] = fiber;
270 }
271 } else
272 appendStringInConstruct(index, jsString->string());
273 }
274
275 void appendValueInConstructAndIncrementLength(ExecState* exec, unsigned& index, JSValue v)
276 {
277 if (v.isString()) {
278 ASSERT(asCell(v)->isString());
279 JSString* s = static_cast<JSString*>(asCell(v));
280 ASSERT(s->fiberCount() == 1);
281 appendStringInConstruct(index, s);
282 m_length += s->length();
283 } else {
284 UString u(v.toString(exec));
285 UStringImpl* impl = u.rep();
286 impl->ref();
287 m_other.m_fibers[index++] = impl;
288 m_length += u.size();
289 }
290 }
291
292 virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const;
293 virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue& value);
294 virtual bool toBoolean(ExecState*) const;
295 virtual double toNumber(ExecState*) const;
296 virtual JSObject* toObject(ExecState*) const;
297 virtual UString toString(ExecState*) const;
298
299 virtual JSObject* toThisObject(ExecState*) const;
300
301 // Actually getPropertySlot, not getOwnPropertySlot (see JSCell).
302 virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
303 virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
304 virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&);
305
306 static const unsigned s_maxInternalRopeLength = 3;
307
308 // A string is represented either by a UString or a Rope.
309 unsigned m_length;
310 mutable UString m_value;
311 mutable unsigned m_fiberCount;
312 // This structure exists to support a temporary workaround for a GC issue.
313 struct JSStringFinalizerStruct {
314 JSStringFinalizerStruct() : m_finalizerCallback(0) {}
315 union {
316 mutable Rope::Fiber m_fibers[s_maxInternalRopeLength];
317 struct {
318 JSStringFinalizerCallback m_finalizerCallback;
319 void* m_finalizerContext;
320 };
321 };
322 } m_other;
323
324 bool isRope() const { return m_fiberCount; }
325 UString& string() { ASSERT(!isRope()); return m_value; }
326 unsigned fiberCount() { return m_fiberCount ? m_fiberCount : 1; }
327
328 friend JSValue jsString(ExecState* exec, JSString* s1, JSString* s2);
329 friend JSValue jsString(ExecState* exec, const UString& u1, JSString* s2);
330 friend JSValue jsString(ExecState* exec, JSString* s1, const UString& u2);
331 friend JSValue jsString(ExecState* exec, Register* strings, unsigned count);
332 friend JSValue jsString(ExecState* exec, JSValue thisValue, const ArgList& args);
333 friend JSString* jsStringWithFinalizer(ExecState*, const UString&, JSStringFinalizerCallback callback, void* context);
334 };
335
336 JSString* asString(JSValue);
337
338 // When an object is created from a different DLL, MSVC changes vptr to a "local" one right after invoking a constructor,
339 // see <http://groups.google.com/group/microsoft.public.vc.language/msg/55cdcefeaf770212>.
340 // This breaks isJSString(), and we don't need that hack anyway, so we change vptr back to primary one.
341 // The below function must be called by any inline function that invokes a JSString constructor.
342#if COMPILER(MSVC) && !defined(BUILDING_JavaScriptCore)
343 inline JSString* fixupVPtr(JSGlobalData* globalData, JSString* string) { string->setVPtr(globalData->jsStringVPtr); return string; }
344#else
345 inline JSString* fixupVPtr(JSGlobalData*, JSString* string) { return string; }
346#endif
347
348 inline JSString* asString(JSValue value)
349 {
350 ASSERT(asCell(value)->isString());
351 return static_cast<JSString*>(asCell(value));
352 }
353
354 inline JSString* jsEmptyString(JSGlobalData* globalData)
355 {
356 return globalData->smallStrings.emptyString(globalData);
357 }
358
359 inline JSString* jsSingleCharacterString(JSGlobalData* globalData, UChar c)
360 {
361 if (c <= 0xFF)
362 return globalData->smallStrings.singleCharacterString(globalData, c);
363 return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(&c, 1)));
364 }
365
366 inline JSString* jsSingleCharacterSubstring(ExecState* exec, const UString& s, unsigned offset)
367 {
368 JSGlobalData* globalData = &exec->globalData();
369 ASSERT(offset < static_cast<unsigned>(s.size()));
370 UChar c = s.data()[offset];
371 if (c <= 0xFF)
372 return globalData->smallStrings.singleCharacterString(globalData, c);
373 return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(UString::Rep::create(s.rep(), offset, 1))));
374 }
375
376 inline JSString* jsNontrivialString(JSGlobalData* globalData, const char* s)
377 {
378 ASSERT(s);
379 ASSERT(s[0]);
380 ASSERT(s[1]);
381 return fixupVPtr(globalData, new (globalData) JSString(globalData, s));
382 }
383
384 inline JSString* jsNontrivialString(JSGlobalData* globalData, const UString& s)
385 {
386 ASSERT(s.size() > 1);
387 return fixupVPtr(globalData, new (globalData) JSString(globalData, s));
388 }
389
390 inline JSString* JSString::getIndex(ExecState* exec, unsigned i)
391 {
392 ASSERT(canGetIndex(i));
393 if (isRope())
394 return getIndexSlowCase(exec, i);
395 ASSERT(i < m_value.size());
396 return jsSingleCharacterSubstring(exec, value(exec), i);
397 }
398
399 inline JSString* jsString(JSGlobalData* globalData, const UString& s)
400 {
401 int size = s.size();
402 if (!size)
403 return globalData->smallStrings.emptyString(globalData);
404 if (size == 1) {
405 UChar c = s.data()[0];
406 if (c <= 0xFF)
407 return globalData->smallStrings.singleCharacterString(globalData, c);
408 }
409 return fixupVPtr(globalData, new (globalData) JSString(globalData, s));
410 }
411
412 inline JSString* jsStringWithFinalizer(ExecState* exec, const UString& s, JSStringFinalizerCallback callback, void* context)
413 {
414 ASSERT(s.size() && (s.size() > 1 || s.data()[0] > 0xFF));
415 JSGlobalData* globalData = &exec->globalData();
416 return fixupVPtr(globalData, new (globalData) JSString(globalData, s, callback, context));
417 }
418
419 inline JSString* jsSubstring(JSGlobalData* globalData, const UString& s, unsigned offset, unsigned length)
420 {
421 ASSERT(offset <= static_cast<unsigned>(s.size()));
422 ASSERT(length <= static_cast<unsigned>(s.size()));
423 ASSERT(offset + length <= static_cast<unsigned>(s.size()));
424 if (!length)
425 return globalData->smallStrings.emptyString(globalData);
426 if (length == 1) {
427 UChar c = s.data()[offset];
428 if (c <= 0xFF)
429 return globalData->smallStrings.singleCharacterString(globalData, c);
430 }
431 return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(UString::Rep::create(s.rep(), offset, length)), JSString::HasOtherOwner));
432 }
433
434 inline JSString* jsOwnedString(JSGlobalData* globalData, const UString& s)
435 {
436 int size = s.size();
437 if (!size)
438 return globalData->smallStrings.emptyString(globalData);
439 if (size == 1) {
440 UChar c = s.data()[0];
441 if (c <= 0xFF)
442 return globalData->smallStrings.singleCharacterString(globalData, c);
443 }
444 return fixupVPtr(globalData, new (globalData) JSString(globalData, s, JSString::HasOtherOwner));
445 }
446
447 inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->globalData()); }
448 inline JSString* jsString(ExecState* exec, const UString& s) { return jsString(&exec->globalData(), s); }
449 inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->globalData(), c); }
450 inline JSString* jsSubstring(ExecState* exec, const UString& s, unsigned offset, unsigned length) { return jsSubstring(&exec->globalData(), s, offset, length); }
451 inline JSString* jsNontrivialString(ExecState* exec, const UString& s) { return jsNontrivialString(&exec->globalData(), s); }
452 inline JSString* jsNontrivialString(ExecState* exec, const char* s) { return jsNontrivialString(&exec->globalData(), s); }
453 inline JSString* jsOwnedString(ExecState* exec, const UString& s) { return jsOwnedString(&exec->globalData(), s); }
454
455 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
456 {
457 if (propertyName == exec->propertyNames().length) {
458 slot.setValue(jsNumber(exec, m_length));
459 return true;
460 }
461
462 bool isStrictUInt32;
463 unsigned i = propertyName.toStrictUInt32(&isStrictUInt32);
464 if (isStrictUInt32 && i < m_length) {
465 slot.setValue(getIndex(exec, i));
466 return true;
467 }
468
469 return false;
470 }
471
472 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
473 {
474 if (propertyName < m_length) {
475 slot.setValue(getIndex(exec, propertyName));
476 return true;
477 }
478
479 return false;
480 }
481
482 inline bool isJSString(JSGlobalData* globalData, JSValue v) { return v.isCell() && v.asCell()->vptr() == globalData->jsStringVPtr; }
483
484 // --- JSValue inlines ----------------------------
485
486 inline UString JSValue::toString(ExecState* exec) const
487 {
488 if (isString())
489 return static_cast<JSString*>(asCell())->value(exec);
490 if (isInt32())
491 return exec->globalData().numericStrings.add(asInt32());
492 if (isDouble())
493 return exec->globalData().numericStrings.add(asDouble());
494 if (isTrue())
495 return "true";
496 if (isFalse())
497 return "false";
498 if (isNull())
499 return "null";
500 if (isUndefined())
501 return "undefined";
502 ASSERT(isCell());
503 return asCell()->toString(exec);
504 }
505
506 inline UString JSValue::toPrimitiveString(ExecState* exec) const
507 {
508 if (isString())
509 return static_cast<JSString*>(asCell())->value(exec);
510 if (isInt32())
511 return exec->globalData().numericStrings.add(asInt32());
512 if (isDouble())
513 return exec->globalData().numericStrings.add(asDouble());
514 if (isTrue())
515 return "true";
516 if (isFalse())
517 return "false";
518 if (isNull())
519 return "null";
520 if (isUndefined())
521 return "undefined";
522 ASSERT(isCell());
523 return asCell()->toPrimitive(exec, NoPreference).toString(exec);
524 }
525
526} // namespace JSC
527
528#endif // JSString_h
Note: See TracBrowser for help on using the repository browser.