source: webkit/trunk/JavaScriptCore/runtime/Operations.h@ 58990

Last change on this file since 58990 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: 14.7 KB
Line 
1/*
2 * Copyright (C) 1999-2000 Harri Porten ([email protected])
3 * Copyright (C) 2002, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 *
20 */
21
22#ifndef Operations_h
23#define Operations_h
24
25#include "ExceptionHelpers.h"
26#include "Interpreter.h"
27#include "JSImmediate.h"
28#include "JSNumberCell.h"
29#include "JSString.h"
30
31namespace JSC {
32
33 NEVER_INLINE JSValue jsAddSlowCase(CallFrame*, JSValue, JSValue);
34 JSValue jsTypeStringForValue(CallFrame*, JSValue);
35 bool jsIsObjectType(JSValue);
36 bool jsIsFunctionType(JSValue);
37
38 ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, JSString* s2)
39 {
40 unsigned length1 = s1->length();
41 if (!length1)
42 return s2;
43 unsigned length2 = s2->length();
44 if (!length2)
45 return s1;
46 if ((length1 + length2) < length1)
47 return throwOutOfMemoryError(exec);
48
49 unsigned fiberCount = s1->fiberCount() + s2->fiberCount();
50 JSGlobalData* globalData = &exec->globalData();
51
52 if (fiberCount <= JSString::s_maxInternalRopeLength)
53 return new (globalData) JSString(globalData, fiberCount, s1, s2);
54
55 JSString::RopeBuilder ropeBuilder(fiberCount);
56 if (UNLIKELY(ropeBuilder.isOutOfMemory()))
57 return throwOutOfMemoryError(exec);
58 ropeBuilder.append(s1);
59 ropeBuilder.append(s2);
60 return new (globalData) JSString(globalData, ropeBuilder.release());
61 }
62
63 ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, JSString* s2)
64 {
65 unsigned length1 = u1.size();
66 if (!length1)
67 return s2;
68 unsigned length2 = s2->length();
69 if (!length2)
70 return jsString(exec, u1);
71 if ((length1 + length2) < length1)
72 return throwOutOfMemoryError(exec);
73
74 unsigned fiberCount = 1 + s2->fiberCount();
75 JSGlobalData* globalData = &exec->globalData();
76
77 if (fiberCount <= JSString::s_maxInternalRopeLength)
78 return new (globalData) JSString(globalData, fiberCount, u1, s2);
79
80 JSString::RopeBuilder ropeBuilder(fiberCount);
81 if (UNLIKELY(ropeBuilder.isOutOfMemory()))
82 return throwOutOfMemoryError(exec);
83 ropeBuilder.append(u1);
84 ropeBuilder.append(s2);
85 return new (globalData) JSString(globalData, ropeBuilder.release());
86 }
87
88 ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, const UString& u2)
89 {
90 unsigned length1 = s1->length();
91 if (!length1)
92 return jsString(exec, u2);
93 unsigned length2 = u2.size();
94 if (!length2)
95 return s1;
96 if ((length1 + length2) < length1)
97 return throwOutOfMemoryError(exec);
98
99 unsigned fiberCount = s1->fiberCount() + 1;
100 JSGlobalData* globalData = &exec->globalData();
101
102 if (fiberCount <= JSString::s_maxInternalRopeLength)
103 return new (globalData) JSString(globalData, fiberCount, s1, u2);
104
105 JSString::RopeBuilder ropeBuilder(fiberCount);
106 if (UNLIKELY(ropeBuilder.isOutOfMemory()))
107 return throwOutOfMemoryError(exec);
108 ropeBuilder.append(s1);
109 ropeBuilder.append(u2);
110 return new (globalData) JSString(globalData, ropeBuilder.release());
111 }
112
113 ALWAYS_INLINE JSValue jsString(ExecState* exec, Register* strings, unsigned count)
114 {
115 ASSERT(count >= 3);
116
117 unsigned fiberCount = 0;
118 for (unsigned i = 0; i < count; ++i) {
119 JSValue v = strings[i].jsValue();
120 if (LIKELY(v.isString()))
121 fiberCount += asString(v)->fiberCount();
122 else
123 ++fiberCount;
124 }
125
126 JSGlobalData* globalData = &exec->globalData();
127 if (fiberCount == 3)
128 return new (globalData) JSString(exec, strings[0].jsValue(), strings[1].jsValue(), strings[2].jsValue());
129
130 JSString::RopeBuilder ropeBuilder(fiberCount);
131 if (UNLIKELY(ropeBuilder.isOutOfMemory()))
132 return throwOutOfMemoryError(exec);
133
134 unsigned length = 0;
135 bool overflow = false;
136
137 for (unsigned i = 0; i < count; ++i) {
138 JSValue v = strings[i].jsValue();
139 if (LIKELY(v.isString()))
140 ropeBuilder.append(asString(v));
141 else
142 ropeBuilder.append(v.toString(exec));
143
144 unsigned newLength = ropeBuilder.length();
145 if (newLength < length)
146 overflow = true;
147 length = newLength;
148 }
149
150 if (overflow)
151 return throwOutOfMemoryError(exec);
152
153 return new (globalData) JSString(globalData, ropeBuilder.release());
154 }
155
156 ALWAYS_INLINE JSValue jsString(ExecState* exec, JSValue thisValue, const ArgList& args)
157 {
158 unsigned fiberCount = 0;
159 if (LIKELY(thisValue.isString()))
160 fiberCount += asString(thisValue)->fiberCount();
161 else
162 ++fiberCount;
163 for (unsigned i = 0; i < args.size(); ++i) {
164 JSValue v = args.at(i);
165 if (LIKELY(v.isString()))
166 fiberCount += asString(v)->fiberCount();
167 else
168 ++fiberCount;
169 }
170
171 JSString::RopeBuilder ropeBuilder(fiberCount);
172 if (UNLIKELY(ropeBuilder.isOutOfMemory()))
173 return throwOutOfMemoryError(exec);
174
175 if (LIKELY(thisValue.isString()))
176 ropeBuilder.append(asString(thisValue));
177 else
178 ropeBuilder.append(thisValue.toString(exec));
179
180 unsigned length = 0;
181 bool overflow = false;
182
183 for (unsigned i = 0; i < args.size(); ++i) {
184 JSValue v = args.at(i);
185 if (LIKELY(v.isString()))
186 ropeBuilder.append(asString(v));
187 else
188 ropeBuilder.append(v.toString(exec));
189
190 unsigned newLength = ropeBuilder.length();
191 if (newLength < length)
192 overflow = true;
193 length = newLength;
194 }
195
196 if (overflow)
197 return throwOutOfMemoryError(exec);
198
199 JSGlobalData* globalData = &exec->globalData();
200 return new (globalData) JSString(globalData, ropeBuilder.release());
201 }
202
203 // ECMA 11.9.3
204 inline bool JSValue::equal(ExecState* exec, JSValue v1, JSValue v2)
205 {
206 if (v1.isInt32() && v2.isInt32())
207 return v1 == v2;
208
209 return equalSlowCase(exec, v1, v2);
210 }
211
212 ALWAYS_INLINE bool JSValue::equalSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2)
213 {
214 do {
215 if (v1.isNumber() && v2.isNumber())
216 return v1.uncheckedGetNumber() == v2.uncheckedGetNumber();
217
218 bool s1 = v1.isString();
219 bool s2 = v2.isString();
220 if (s1 && s2)
221 return asString(v1)->value(exec) == asString(v2)->value(exec);
222
223 if (v1.isUndefinedOrNull()) {
224 if (v2.isUndefinedOrNull())
225 return true;
226 if (!v2.isCell())
227 return false;
228 return v2.asCell()->structure()->typeInfo().masqueradesAsUndefined();
229 }
230
231 if (v2.isUndefinedOrNull()) {
232 if (!v1.isCell())
233 return false;
234 return v1.asCell()->structure()->typeInfo().masqueradesAsUndefined();
235 }
236
237 if (v1.isObject()) {
238 if (v2.isObject())
239 return v1 == v2;
240 JSValue p1 = v1.toPrimitive(exec);
241 if (exec->hadException())
242 return false;
243 v1 = p1;
244 if (v1.isInt32() && v2.isInt32())
245 return v1 == v2;
246 continue;
247 }
248
249 if (v2.isObject()) {
250 JSValue p2 = v2.toPrimitive(exec);
251 if (exec->hadException())
252 return false;
253 v2 = p2;
254 if (v1.isInt32() && v2.isInt32())
255 return v1 == v2;
256 continue;
257 }
258
259 if (s1 || s2) {
260 double d1 = v1.toNumber(exec);
261 double d2 = v2.toNumber(exec);
262 return d1 == d2;
263 }
264
265 if (v1.isBoolean()) {
266 if (v2.isNumber())
267 return static_cast<double>(v1.getBoolean()) == v2.uncheckedGetNumber();
268 } else if (v2.isBoolean()) {
269 if (v1.isNumber())
270 return v1.uncheckedGetNumber() == static_cast<double>(v2.getBoolean());
271 }
272
273 return v1 == v2;
274 } while (true);
275 }
276
277 // ECMA 11.9.3
278 ALWAYS_INLINE bool JSValue::strictEqualSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2)
279 {
280 ASSERT(v1.isCell() && v2.isCell());
281
282 if (v1.asCell()->isString() && v2.asCell()->isString())
283 return asString(v1)->value(exec) == asString(v2)->value(exec);
284
285 return v1 == v2;
286 }
287
288 inline bool JSValue::strictEqual(ExecState* exec, JSValue v1, JSValue v2)
289 {
290 if (v1.isInt32() && v2.isInt32())
291 return v1 == v2;
292
293 if (v1.isNumber() && v2.isNumber())
294 return v1.uncheckedGetNumber() == v2.uncheckedGetNumber();
295
296 if (!v1.isCell() || !v2.isCell())
297 return v1 == v2;
298
299 return strictEqualSlowCaseInline(exec, v1, v2);
300 }
301
302 ALWAYS_INLINE bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2)
303 {
304 if (v1.isInt32() && v2.isInt32())
305 return v1.asInt32() < v2.asInt32();
306
307 double n1;
308 double n2;
309 if (v1.getNumber(n1) && v2.getNumber(n2))
310 return n1 < n2;
311
312 JSGlobalData* globalData = &callFrame->globalData();
313 if (isJSString(globalData, v1) && isJSString(globalData, v2))
314 return asString(v1)->value(callFrame) < asString(v2)->value(callFrame);
315
316 JSValue p1;
317 JSValue p2;
318 bool wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1);
319 bool wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2);
320
321 if (wasNotString1 | wasNotString2)
322 return n1 < n2;
323
324 return asString(p1)->value(callFrame) < asString(p2)->value(callFrame);
325 }
326
327 inline bool jsLessEq(CallFrame* callFrame, JSValue v1, JSValue v2)
328 {
329 if (v1.isInt32() && v2.isInt32())
330 return v1.asInt32() <= v2.asInt32();
331
332 double n1;
333 double n2;
334 if (v1.getNumber(n1) && v2.getNumber(n2))
335 return n1 <= n2;
336
337 JSGlobalData* globalData = &callFrame->globalData();
338 if (isJSString(globalData, v1) && isJSString(globalData, v2))
339 return !(asString(v2)->value(callFrame) < asString(v1)->value(callFrame));
340
341 JSValue p1;
342 JSValue p2;
343 bool wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1);
344 bool wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2);
345
346 if (wasNotString1 | wasNotString2)
347 return n1 <= n2;
348
349 return !(asString(p2)->value(callFrame) < asString(p1)->value(callFrame));
350 }
351
352 // Fast-path choices here are based on frequency data from SunSpider:
353 // <times> Add case: <t1> <t2>
354 // ---------------------------
355 // 5626160 Add case: 3 3 (of these, 3637690 are for immediate values)
356 // 247412 Add case: 5 5
357 // 20900 Add case: 5 6
358 // 13962 Add case: 5 3
359 // 4000 Add case: 3 5
360
361 ALWAYS_INLINE JSValue jsAdd(CallFrame* callFrame, JSValue v1, JSValue v2)
362 {
363 double left = 0.0, right;
364 if (v1.getNumber(left) && v2.getNumber(right))
365 return jsNumber(callFrame, left + right);
366
367 if (v1.isString()) {
368 return v2.isString()
369 ? jsString(callFrame, asString(v1), asString(v2))
370 : jsString(callFrame, asString(v1), v2.toPrimitiveString(callFrame));
371 }
372
373 // All other cases are pretty uncommon
374 return jsAddSlowCase(callFrame, v1, v2);
375 }
376
377 inline size_t normalizePrototypeChain(CallFrame* callFrame, JSValue base, JSValue slotBase, const Identifier& propertyName, size_t& slotOffset)
378 {
379 JSCell* cell = asCell(base);
380 size_t count = 0;
381
382 while (slotBase != cell) {
383 JSValue v = cell->structure()->prototypeForLookup(callFrame);
384
385 // If we didn't find slotBase in base's prototype chain, then base
386 // must be a proxy for another object.
387
388 if (v.isNull())
389 return 0;
390
391 cell = asCell(v);
392
393 // Since we're accessing a prototype in a loop, it's a good bet that it
394 // should not be treated as a dictionary.
395 if (cell->structure()->isDictionary()) {
396 asObject(cell)->flattenDictionaryObject();
397 if (slotBase == cell)
398 slotOffset = cell->structure()->get(propertyName);
399 }
400
401 ++count;
402 }
403
404 ASSERT(count);
405 return count;
406 }
407
408 inline size_t normalizePrototypeChain(CallFrame* callFrame, JSCell* base)
409 {
410 size_t count = 0;
411 while (1) {
412 JSValue v = base->structure()->prototypeForLookup(callFrame);
413 if (v.isNull())
414 return count;
415
416 base = asCell(v);
417
418 // Since we're accessing a prototype in a loop, it's a good bet that it
419 // should not be treated as a dictionary.
420 if (base->structure()->isDictionary())
421 asObject(base)->flattenDictionaryObject();
422
423 ++count;
424 }
425 }
426
427 ALWAYS_INLINE JSValue resolveBase(CallFrame* callFrame, Identifier& property, ScopeChainNode* scopeChain)
428 {
429 ScopeChainIterator iter = scopeChain->begin();
430 ScopeChainIterator next = iter;
431 ++next;
432 ScopeChainIterator end = scopeChain->end();
433 ASSERT(iter != end);
434
435 PropertySlot slot;
436 JSObject* base;
437 while (true) {
438 base = *iter;
439 if (next == end || base->getPropertySlot(callFrame, property, slot))
440 return base;
441
442 iter = next;
443 ++next;
444 }
445
446 ASSERT_NOT_REACHED();
447 return JSValue();
448 }
449} // namespace JSC
450
451#endif // Operations_h
Note: See TracBrowser for help on using the repository browser.