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

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

https://bugs.webkit.org/show_bug.cgi?id=35070
Addition of 2 strings of length 231 may result in a string of length 0.

Reviewed by Oliver Hunt.

Check for overflow when creating a new JSString as a result of an addition
or concatenation, throw an out of memory exception.

  • runtime/JSString.h:

(JSC::):

  • runtime/Operations.h:

(JSC::jsString):

  • Property svn:eol-style set to native
File size: 21.6 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(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);
48
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*);
55
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&);
61
62 typedef void (*JSStringFinalizerCallback)(JSString*, void* context);
63 JSString* jsStringWithFinalizer(ExecState*, const UString&, JSStringFinalizerCallback callback, void* context);
64
65 class JS_EXPORTCLASS JSString : public JSCell {
66 public:
67 friend class JIT;
68 friend class JSGlobalData;
69
70 typedef URopeImpl Rope;
71
72 class RopeBuilder {
73 public:
74 RopeBuilder(unsigned fiberCount)
75 : m_index(0)
76 , m_rope(Rope::tryCreateUninitialized(fiberCount))
77 {
78 }
79
80 bool isOutOfMemory() { return !m_rope; }
81
82 void append(Rope::Fiber& fiber)
83 {
84 ASSERT(m_rope);
85 m_rope->initializeFiber(m_index, fiber);
86 }
87 void append(const UString& string)
88 {
89 ASSERT(m_rope);
90 m_rope->initializeFiber(m_index, string.rep());
91 }
92 void append(JSString* jsString)
93 {
94 if (jsString->isRope()) {
95 for (unsigned i = 0; i < jsString->m_fiberCount; ++i)
96 append(jsString->m_other.m_fibers[i]);
97 } else
98 append(jsString->string());
99 }
100
101 PassRefPtr<Rope> release()
102 {
103 ASSERT(m_index == m_rope->fiberCount());
104 return m_rope.release();
105 }
106
107 unsigned length() { return m_rope->length(); }
108
109 private:
110 unsigned m_index;
111 RefPtr<Rope> m_rope;
112 };
113
114 ALWAYS_INLINE JSString(JSGlobalData* globalData, const UString& value)
115 : JSCell(globalData->stringStructure.get())
116 , m_length(value.size())
117 , m_value(value)
118 , m_fiberCount(0)
119 {
120 Heap::heap(this)->reportExtraMemoryCost(value.cost());
121 }
122
123 enum HasOtherOwnerType { HasOtherOwner };
124 JSString(JSGlobalData* globalData, const UString& value, HasOtherOwnerType)
125 : JSCell(globalData->stringStructure.get())
126 , m_length(value.size())
127 , m_value(value)
128 , m_fiberCount(0)
129 {
130 }
131 JSString(JSGlobalData* globalData, PassRefPtr<UString::Rep> value, HasOtherOwnerType)
132 : JSCell(globalData->stringStructure.get())
133 , m_length(value->length())
134 , m_value(value)
135 , m_fiberCount(0)
136 {
137 }
138 JSString(JSGlobalData* globalData, PassRefPtr<Rope> rope)
139 : JSCell(globalData->stringStructure.get())
140 , m_length(rope->length())
141 , m_fiberCount(1)
142 {
143 m_other.m_fibers[0] = rope.releaseRef();
144 }
145 // This constructor constructs a new string by concatenating s1 & s2.
146 // This should only be called with fiberCount <= 3.
147 JSString(JSGlobalData* globalData, unsigned fiberCount, JSString* s1, JSString* s2)
148 : JSCell(globalData->stringStructure.get())
149 , m_length(s1->length() + s2->length())
150 , m_fiberCount(fiberCount)
151 {
152 ASSERT(fiberCount <= s_maxInternalRopeLength);
153 unsigned index = 0;
154 appendStringInConstruct(index, s1);
155 appendStringInConstruct(index, s2);
156 ASSERT(fiberCount == index);
157 }
158 // This constructor constructs a new string by concatenating s1 & s2.
159 // This should only be called with fiberCount <= 3.
160 JSString(JSGlobalData* globalData, unsigned fiberCount, JSString* s1, const UString& u2)
161 : JSCell(globalData->stringStructure.get())
162 , m_length(s1->length() + u2.size())
163 , m_fiberCount(fiberCount)
164 {
165 ASSERT(fiberCount <= s_maxInternalRopeLength);
166 unsigned index = 0;
167 appendStringInConstruct(index, s1);
168 appendStringInConstruct(index, u2);
169 ASSERT(fiberCount == index);
170 }
171 // This constructor constructs a new string by concatenating s1 & s2.
172 // This should only be called with fiberCount <= 3.
173 JSString(JSGlobalData* globalData, unsigned fiberCount, const UString& u1, JSString* s2)
174 : JSCell(globalData->stringStructure.get())
175 , m_length(u1.size() + s2->length())
176 , m_fiberCount(fiberCount)
177 {
178 ASSERT(fiberCount <= s_maxInternalRopeLength);
179 unsigned index = 0;
180 appendStringInConstruct(index, u1);
181 appendStringInConstruct(index, s2);
182 ASSERT(fiberCount == index);
183 }
184 // This constructor constructs a new string by concatenating v1, v2 & v3.
185 // This should only be called with fiberCount <= 3 ... which since every
186 // value must require a fiberCount of at least one implies that the length
187 // for each value must be exactly 1!
188 JSString(ExecState* exec, JSValue v1, JSValue v2, JSValue v3)
189 : JSCell(exec->globalData().stringStructure.get())
190 , m_length(0)
191 , m_fiberCount(s_maxInternalRopeLength)
192 {
193 unsigned index = 0;
194 appendValueInConstructAndIncrementLength(exec, index, v1);
195 appendValueInConstructAndIncrementLength(exec, index, v2);
196 appendValueInConstructAndIncrementLength(exec, index, v3);
197 ASSERT(index == s_maxInternalRopeLength);
198 }
199
200 JSString(JSGlobalData* globalData, const UString& value, JSStringFinalizerCallback finalizer, void* context)
201 : JSCell(globalData->stringStructure.get())
202 , m_length(value.size())
203 , m_value(value)
204 , m_fiberCount(0)
205 {
206 // nasty hack because we can't union non-POD types
207 m_other.m_finalizerCallback = finalizer;
208 m_other.m_finalizerContext = context;
209 Heap::heap(this)->reportExtraMemoryCost(value.cost());
210 }
211
212 ~JSString()
213 {
214 ASSERT(vptr() == JSGlobalData::jsStringVPtr);
215 for (unsigned i = 0; i < m_fiberCount; ++i)
216 m_other.m_fibers[i]->deref();
217
218 if (!m_fiberCount && m_other.m_finalizerCallback)
219 m_other.m_finalizerCallback(this, m_other.m_finalizerContext);
220 }
221
222 const UString& value(ExecState* exec) const
223 {
224 if (isRope())
225 resolveRope(exec);
226 return m_value;
227 }
228 const UString tryGetValue() const
229 {
230 // If this is a rope, m_value should be null -
231 // if this is not a rope, m_value should be non-null.
232 ASSERT(isRope() == m_value.isNull());
233 return m_value;
234 }
235 unsigned length() { return m_length; }
236
237 bool getStringPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
238 bool getStringPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
239 bool getStringPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor&);
240
241 bool canGetIndex(unsigned i) { return i < m_length; }
242 JSString* getIndex(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(JSGlobalData* globalData, const UString& s, unsigned offset)
369 {
370 ASSERT(offset < static_cast<unsigned>(s.size()));
371 UChar c = s.data()[offset];
372 if (c <= 0xFF)
373 return globalData->smallStrings.singleCharacterString(globalData, c);
374 return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(UString::Rep::create(s.rep(), offset, 1))));
375 }
376
377 inline JSString* jsNontrivialString(JSGlobalData* globalData, const char* s)
378 {
379 ASSERT(s);
380 ASSERT(s[0]);
381 ASSERT(s[1]);
382 return fixupVPtr(globalData, new (globalData) JSString(globalData, s));
383 }
384
385 inline JSString* jsNontrivialString(JSGlobalData* globalData, const UString& s)
386 {
387 ASSERT(s.size() > 1);
388 return fixupVPtr(globalData, new (globalData) JSString(globalData, s));
389 }
390
391 inline JSString* JSString::getIndex(ExecState* exec, unsigned i)
392 {
393 ASSERT(canGetIndex(i));
394 return jsSingleCharacterSubstring(&exec->globalData(), value(exec), i);
395 }
396
397 inline JSString* jsString(JSGlobalData* globalData, const UString& s)
398 {
399 int size = s.size();
400 if (!size)
401 return globalData->smallStrings.emptyString(globalData);
402 if (size == 1) {
403 UChar c = s.data()[0];
404 if (c <= 0xFF)
405 return globalData->smallStrings.singleCharacterString(globalData, c);
406 }
407 return fixupVPtr(globalData, new (globalData) JSString(globalData, s));
408 }
409
410 inline JSString* jsStringWithFinalizer(ExecState* exec, const UString& s, JSStringFinalizerCallback callback, void* context)
411 {
412 ASSERT(s.size() && (s.size() > 1 || s.data()[0] > 0xFF));
413 JSGlobalData* globalData = &exec->globalData();
414 return fixupVPtr(globalData, new (globalData) JSString(globalData, s, callback, context));
415 }
416
417 inline JSString* jsSubstring(JSGlobalData* globalData, const UString& s, unsigned offset, unsigned length)
418 {
419 ASSERT(offset <= static_cast<unsigned>(s.size()));
420 ASSERT(length <= static_cast<unsigned>(s.size()));
421 ASSERT(offset + length <= static_cast<unsigned>(s.size()));
422 if (!length)
423 return globalData->smallStrings.emptyString(globalData);
424 if (length == 1) {
425 UChar c = s.data()[offset];
426 if (c <= 0xFF)
427 return globalData->smallStrings.singleCharacterString(globalData, c);
428 }
429 return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(UString::Rep::create(s.rep(), offset, length)), JSString::HasOtherOwner));
430 }
431
432 inline JSString* jsOwnedString(JSGlobalData* globalData, const UString& s)
433 {
434 int size = s.size();
435 if (!size)
436 return globalData->smallStrings.emptyString(globalData);
437 if (size == 1) {
438 UChar c = s.data()[0];
439 if (c <= 0xFF)
440 return globalData->smallStrings.singleCharacterString(globalData, c);
441 }
442 return fixupVPtr(globalData, new (globalData) JSString(globalData, s, JSString::HasOtherOwner));
443 }
444
445 inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->globalData()); }
446 inline JSString* jsString(ExecState* exec, const UString& s) { return jsString(&exec->globalData(), s); }
447 inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->globalData(), c); }
448 inline JSString* jsSingleCharacterSubstring(ExecState* exec, const UString& s, unsigned offset) { return jsSingleCharacterSubstring(&exec->globalData(), s, offset); }
449 inline JSString* jsSubstring(ExecState* exec, const UString& s, unsigned offset, unsigned length) { return jsSubstring(&exec->globalData(), s, offset, length); }
450 inline JSString* jsNontrivialString(ExecState* exec, const UString& s) { return jsNontrivialString(&exec->globalData(), s); }
451 inline JSString* jsNontrivialString(ExecState* exec, const char* s) { return jsNontrivialString(&exec->globalData(), s); }
452 inline JSString* jsOwnedString(ExecState* exec, const UString& s) { return jsOwnedString(&exec->globalData(), s); }
453
454 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
455 {
456 if (propertyName == exec->propertyNames().length) {
457 slot.setValue(jsNumber(exec, m_length));
458 return true;
459 }
460
461 bool isStrictUInt32;
462 unsigned i = propertyName.toStrictUInt32(&isStrictUInt32);
463 if (isStrictUInt32 && i < m_length) {
464 slot.setValue(jsSingleCharacterSubstring(exec, value(exec), i));
465 return true;
466 }
467
468 return false;
469 }
470
471 ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
472 {
473 if (propertyName < m_length) {
474 slot.setValue(jsSingleCharacterSubstring(exec, value(exec), propertyName));
475 return true;
476 }
477
478 return false;
479 }
480
481 inline bool isJSString(JSGlobalData* globalData, JSValue v) { return v.isCell() && v.asCell()->vptr() == globalData->jsStringVPtr; }
482
483 // --- JSValue inlines ----------------------------
484
485 inline JSString* JSValue::toThisJSString(ExecState* exec)
486 {
487 return isCell() ? asCell()->toThisJSString(exec) : jsString(exec, toString(exec));
488 }
489
490 inline UString JSValue::toString(ExecState* exec) const
491 {
492 if (isString())
493 return static_cast<JSString*>(asCell())->value(exec);
494 if (isInt32())
495 return exec->globalData().numericStrings.add(asInt32());
496 if (isDouble())
497 return exec->globalData().numericStrings.add(asDouble());
498 if (isTrue())
499 return "true";
500 if (isFalse())
501 return "false";
502 if (isNull())
503 return "null";
504 if (isUndefined())
505 return "undefined";
506 ASSERT(isCell());
507 return asCell()->toString(exec);
508 }
509
510 inline UString JSValue::toPrimitiveString(ExecState* exec) const
511 {
512 if (isString())
513 return static_cast<JSString*>(asCell())->value(exec);
514 if (isInt32())
515 return exec->globalData().numericStrings.add(asInt32());
516 if (isDouble())
517 return exec->globalData().numericStrings.add(asDouble());
518 if (isTrue())
519 return "true";
520 if (isFalse())
521 return "false";
522 if (isNull())
523 return "null";
524 if (isUndefined())
525 return "undefined";
526 ASSERT(isCell());
527 return asCell()->toPrimitive(exec, NoPreference).toString(exec);
528 }
529
530} // namespace JSC
531
532#endif // JSString_h
Note: See TracBrowser for help on using the repository browser.