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

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

https://bugs.webkit.org/show_bug.cgi?id=35843
Re-land reverted fix to JSString::getIndex()

Reviewed by Sam Weinig.

Calling getIndex() on a JSString in rope form may result in a JSException being thrown
if there is insuficient memory so value(exec) returns UString() with length zero,
which will be passed to jsSingleCharacterSubstring.
Add a slow case function to trap the error & return a safe null value, until the
exception is handled.

  • runtime/JSString.cpp:

(JSC::JSString::getIndexSlowCase):
(JSC::JSString::getStringPropertyDescriptor):

  • runtime/JSString.h:

(JSC::jsSingleCharacterSubstring):
(JSC::JSString::getIndex):
(JSC::jsSingleCharacterString):
(JSC::JSString::getStringPropertySlot):

  • Property svn:eol-style set to native
File size: 21.5 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 virtual UString toThisString(ExecState*) const;
301 virtual JSString* toThisJSString(ExecState*);
302
303 // Actually getPropertySlot, not getOwnPropertySlot (see JSCell).
304 virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
305 virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
306 virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&);
307
308 static const unsigned s_maxInternalRopeLength = 3;
309
310 // A string is represented either by a UString or a Rope.
311 unsigned m_length;
312 mutable UString m_value;
313 mutable unsigned m_fiberCount;
314 // This structure exists to support a temporary workaround for a GC issue.
315 struct JSStringFinalizerStruct {
316 JSStringFinalizerStruct() : m_finalizerCallback(0) {}
317 union {
318 mutable Rope::Fiber m_fibers[s_maxInternalRopeLength];
319 struct {
320 JSStringFinalizerCallback m_finalizerCallback;
321 void* m_finalizerContext;
322 };
323 };
324 } m_other;
325
326 bool isRope() const { return m_fiberCount; }
327 UString& string() { ASSERT(!isRope()); return m_value; }
328 unsigned fiberCount() { return m_fiberCount ? m_fiberCount : 1; }
329
330 friend JSValue jsString(ExecState* exec, JSString* s1, JSString* s2);
331 friend JSValue jsString(ExecState* exec, const UString& u1, JSString* s2);
332 friend JSValue jsString(ExecState* exec, JSString* s1, const UString& u2);
333 friend JSValue jsString(ExecState* exec, Register* strings, unsigned count);
334 friend JSValue jsString(ExecState* exec, JSValue thisValue, const ArgList& args);
335 friend JSString* jsStringWithFinalizer(ExecState*, const UString&, JSStringFinalizerCallback callback, void* context);
336 };
337
338 JSString* asString(JSValue);
339
340 // When an object is created from a different DLL, MSVC changes vptr to a "local" one right after invoking a constructor,
341 // see <http://groups.google.com/group/microsoft.public.vc.language/msg/55cdcefeaf770212>.
342 // This breaks isJSString(), and we don't need that hack anyway, so we change vptr back to primary one.
343 // The below function must be called by any inline function that invokes a JSString constructor.
344#if COMPILER(MSVC) && !defined(BUILDING_JavaScriptCore)
345 inline JSString* fixupVPtr(JSGlobalData* globalData, JSString* string) { string->setVPtr(globalData->jsStringVPtr); return string; }
346#else
347 inline JSString* fixupVPtr(JSGlobalData*, JSString* string) { return string; }
348#endif
349
350 inline JSString* asString(JSValue value)
351 {
352 ASSERT(asCell(value)->isString());
353 return static_cast<JSString*>(asCell(value));
354 }
355
356 inline JSString* jsEmptyString(JSGlobalData* globalData)
357 {
358 return globalData->smallStrings.emptyString(globalData);
359 }
360
361 inline JSString* jsSingleCharacterString(JSGlobalData* globalData, UChar c)
362 {
363 if (c <= 0xFF)
364 return globalData->smallStrings.singleCharacterString(globalData, c);
365 return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(&c, 1)));
366 }
367
368 inline JSString* jsSingleCharacterSubstring(ExecState* exec, const UString& s, unsigned offset)
369 {
370 JSGlobalData* globalData = &exec->globalData();
371 ASSERT(offset < static_cast<unsigned>(s.size()));
372 UChar c = s.data()[offset];
373 if (c <= 0xFF)
374 return globalData->smallStrings.singleCharacterString(globalData, c);
375 return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(UString::Rep::create(s.rep(), offset, 1))));
376 }
377
378 inline JSString* jsNontrivialString(JSGlobalData* globalData, const char* s)
379 {
380 ASSERT(s);
381 ASSERT(s[0]);
382 ASSERT(s[1]);
383 return fixupVPtr(globalData, new (globalData) JSString(globalData, s));
384 }
385
386 inline JSString* jsNontrivialString(JSGlobalData* globalData, const UString& s)
387 {
388 ASSERT(s.size() > 1);
389 return fixupVPtr(globalData, new (globalData) JSString(globalData, s));
390 }
391
392 inline JSString* JSString::getIndex(ExecState* exec, unsigned i)
393 {
394 ASSERT(canGetIndex(i));
395 if (isRope())
396 return getIndexSlowCase(exec, i);
397 ASSERT(i < m_value.size());
398 return jsSingleCharacterSubstring(exec, value(exec), i);
399 }
400
401 inline JSString* jsString(JSGlobalData* globalData, const UString& s)
402 {
403 int size = s.size();
404 if (!size)
405 return globalData->smallStrings.emptyString(globalData);
406 if (size == 1) {
407 UChar c = s.data()[0];
408 if (c <= 0xFF)
409 return globalData->smallStrings.singleCharacterString(globalData, c);
410 }
411 return fixupVPtr(globalData, new (globalData) JSString(globalData, s));
412 }
413
414 inline JSString* jsStringWithFinalizer(ExecState* exec, const UString& s, JSStringFinalizerCallback callback, void* context)
415 {
416 ASSERT(s.size() && (s.size() > 1 || s.data()[0] > 0xFF));
417 JSGlobalData* globalData = &exec->globalData();
418 return fixupVPtr(globalData, new (globalData) JSString(globalData, s, callback, context));
419 }
420
421 inline JSString* jsSubstring(JSGlobalData* globalData, const UString& s, unsigned offset, unsigned length)
422 {
423 ASSERT(offset <= static_cast<unsigned>(s.size()));
424 ASSERT(length <= static_cast<unsigned>(s.size()));
425 ASSERT(offset + length <= static_cast<unsigned>(s.size()));
426 if (!length)
427 return globalData->smallStrings.emptyString(globalData);
428 if (length == 1) {
429 UChar c = s.data()[offset];
430 if (c <= 0xFF)
431 return globalData->smallStrings.singleCharacterString(globalData, c);
432 }
433 return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(UString::Rep::create(s.rep(), offset, length)), JSString::HasOtherOwner));
434 }
435
436 inline JSString* jsOwnedString(JSGlobalData* globalData, const UString& s)
437 {
438 int size = s.size();
439 if (!size)
440 return globalData->smallStrings.emptyString(globalData);
441 if (size == 1) {
442 UChar c = s.data()[0];
443 if (c <= 0xFF)
444 return globalData->smallStrings.singleCharacterString(globalData, c);
445 }
446 return fixupVPtr(globalData, new (globalData) JSString(globalData, s, JSString::HasOtherOwner));
447 }
448
449 inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->globalData()); }
450 inline JSString* jsString(ExecState* exec, const UString& s) { return jsString(&exec->globalData(), s); }
451 inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->globalData(), c); }
452 inline JSString* jsSubstring(ExecState* exec, const UString& s, unsigned offset, unsigned length) { return jsSubstring(&exec->globalData(), s, offset, length); }
453 inline JSString* jsNontrivialString(ExecState* exec, const UString& s) { return jsNontrivialString(&exec->globalData(), s); }
454 inline JSString* jsNontrivialString(ExecState* exec, const char* s) { return jsNontrivialString(&exec->globalData(), s); }
455 inline JSString* jsOwnedString(ExecState* exec, const UString& s) { return jsOwnedString(&exec->globalData(), s); }
456
457 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
458 {
459 if (propertyName == exec->propertyNames().length) {
460 slot.setValue(jsNumber(exec, m_length));
461 return true;
462 }
463
464 bool isStrictUInt32;
465 unsigned i = propertyName.toStrictUInt32(&isStrictUInt32);
466 if (isStrictUInt32 && i < m_length) {
467 slot.setValue(getIndex(exec, i));
468 return true;
469 }
470
471 return false;
472 }
473
474 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
475 {
476 if (propertyName < m_length) {
477 slot.setValue(getIndex(exec, propertyName));
478 return true;
479 }
480
481 return false;
482 }
483
484 inline bool isJSString(JSGlobalData* globalData, JSValue v) { return v.isCell() && v.asCell()->vptr() == globalData->jsStringVPtr; }
485
486 // --- JSValue inlines ----------------------------
487
488 inline JSString* JSValue::toThisJSString(ExecState* exec)
489 {
490 return isCell() ? asCell()->toThisJSString(exec) : jsString(exec, toString(exec));
491 }
492
493 inline UString JSValue::toString(ExecState* exec) const
494 {
495 if (isString())
496 return static_cast<JSString*>(asCell())->value(exec);
497 if (isInt32())
498 return exec->globalData().numericStrings.add(asInt32());
499 if (isDouble())
500 return exec->globalData().numericStrings.add(asDouble());
501 if (isTrue())
502 return "true";
503 if (isFalse())
504 return "false";
505 if (isNull())
506 return "null";
507 if (isUndefined())
508 return "undefined";
509 ASSERT(isCell());
510 return asCell()->toString(exec);
511 }
512
513 inline UString JSValue::toPrimitiveString(ExecState* exec) const
514 {
515 if (isString())
516 return static_cast<JSString*>(asCell())->value(exec);
517 if (isInt32())
518 return exec->globalData().numericStrings.add(asInt32());
519 if (isDouble())
520 return exec->globalData().numericStrings.add(asDouble());
521 if (isTrue())
522 return "true";
523 if (isFalse())
524 return "false";
525 if (isNull())
526 return "null";
527 if (isUndefined())
528 return "undefined";
529 ASSERT(isCell());
530 return asCell()->toPrimitive(exec, NoPreference).toString(exec);
531 }
532
533} // namespace JSC
534
535#endif // JSString_h
Note: See TracBrowser for help on using the repository browser.