]>
Commit | Line | Data |
---|---|---|
9dae56ea A |
1 | /* |
2 | * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) | |
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 | ||
4e4e5a6f | 25 | #include "ExceptionHelpers.h" |
ba379fdc | 26 | #include "Interpreter.h" |
9dae56ea A |
27 | #include "JSImmediate.h" |
28 | #include "JSNumberCell.h" | |
29 | #include "JSString.h" | |
30 | ||
31 | namespace JSC { | |
32 | ||
ba379fdc A |
33 | NEVER_INLINE JSValue jsAddSlowCase(CallFrame*, JSValue, JSValue); |
34 | JSValue jsTypeStringForValue(CallFrame*, JSValue); | |
35 | bool jsIsObjectType(JSValue); | |
36 | bool jsIsFunctionType(JSValue); | |
37 | ||
f9bf01c6 A |
38 | ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, JSString* s2) |
39 | { | |
4e4e5a6f A |
40 | unsigned length1 = s1->length(); |
41 | if (!length1) | |
f9bf01c6 | 42 | return s2; |
4e4e5a6f A |
43 | unsigned length2 = s2->length(); |
44 | if (!length2) | |
f9bf01c6 | 45 | return s1; |
4e4e5a6f A |
46 | if ((length1 + length2) < length1) |
47 | return throwOutOfMemoryError(exec); | |
f9bf01c6 | 48 | |
4e4e5a6f | 49 | unsigned fiberCount = s1->size() + s2->size(); |
f9bf01c6 A |
50 | JSGlobalData* globalData = &exec->globalData(); |
51 | ||
4e4e5a6f A |
52 | if (fiberCount <= JSString::s_maxInternalRopeLength) |
53 | return new (globalData) JSString(globalData, fiberCount, s1, s2); | |
f9bf01c6 | 54 | |
4e4e5a6f A |
55 | JSString::RopeBuilder ropeBuilder(fiberCount); |
56 | if (UNLIKELY(ropeBuilder.isOutOfMemory())) | |
f9bf01c6 | 57 | return throwOutOfMemoryError(exec); |
4e4e5a6f A |
58 | ropeBuilder.append(s1); |
59 | ropeBuilder.append(s2); | |
60 | return new (globalData) JSString(globalData, ropeBuilder.release()); | |
f9bf01c6 A |
61 | } |
62 | ||
63 | ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, JSString* s2) | |
64 | { | |
4e4e5a6f A |
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->size(); | |
f9bf01c6 A |
75 | JSGlobalData* globalData = &exec->globalData(); |
76 | ||
4e4e5a6f A |
77 | if (fiberCount <= JSString::s_maxInternalRopeLength) |
78 | return new (globalData) JSString(globalData, fiberCount, u1, s2); | |
f9bf01c6 | 79 | |
4e4e5a6f A |
80 | JSString::RopeBuilder ropeBuilder(fiberCount); |
81 | if (UNLIKELY(ropeBuilder.isOutOfMemory())) | |
f9bf01c6 | 82 | return throwOutOfMemoryError(exec); |
4e4e5a6f A |
83 | ropeBuilder.append(u1); |
84 | ropeBuilder.append(s2); | |
85 | return new (globalData) JSString(globalData, ropeBuilder.release()); | |
f9bf01c6 A |
86 | } |
87 | ||
88 | ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, const UString& u2) | |
89 | { | |
4e4e5a6f A |
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->size() + 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, const UString& u1, const UString& u2) | |
114 | { | |
115 | unsigned length1 = u1.size(); | |
116 | if (!length1) | |
117 | return jsString(exec, u2); | |
118 | unsigned length2 = u2.size(); | |
119 | if (!length2) | |
120 | return jsString(exec, u1); | |
121 | if ((length1 + length2) < length1) | |
122 | return throwOutOfMemoryError(exec); | |
123 | ||
f9bf01c6 | 124 | JSGlobalData* globalData = &exec->globalData(); |
4e4e5a6f A |
125 | return new (globalData) JSString(globalData, u1, u2); |
126 | } | |
f9bf01c6 | 127 | |
4e4e5a6f A |
128 | ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, const UString& u2, const UString& u3) |
129 | { | |
130 | unsigned length1 = u1.size(); | |
131 | unsigned length2 = u2.size(); | |
132 | unsigned length3 = u3.size(); | |
133 | if (!length1) | |
134 | return jsString(exec, u2, u3); | |
135 | if (!length2) | |
136 | return jsString(exec, u1, u3); | |
137 | if (!length3) | |
138 | return jsString(exec, u1, u2); | |
f9bf01c6 | 139 | |
4e4e5a6f A |
140 | if ((length1 + length2) < length1) |
141 | return throwOutOfMemoryError(exec); | |
142 | if ((length1 + length2 + length3) < length3) | |
f9bf01c6 | 143 | return throwOutOfMemoryError(exec); |
4e4e5a6f A |
144 | |
145 | JSGlobalData* globalData = &exec->globalData(); | |
146 | return new (globalData) JSString(globalData, u1, u2, u3); | |
f9bf01c6 A |
147 | } |
148 | ||
149 | ALWAYS_INLINE JSValue jsString(ExecState* exec, Register* strings, unsigned count) | |
150 | { | |
151 | ASSERT(count >= 3); | |
152 | ||
4e4e5a6f | 153 | unsigned fiberCount = 0; |
f9bf01c6 A |
154 | for (unsigned i = 0; i < count; ++i) { |
155 | JSValue v = strings[i].jsValue(); | |
156 | if (LIKELY(v.isString())) | |
4e4e5a6f | 157 | fiberCount += asString(v)->size(); |
f9bf01c6 | 158 | else |
4e4e5a6f | 159 | ++fiberCount; |
f9bf01c6 A |
160 | } |
161 | ||
162 | JSGlobalData* globalData = &exec->globalData(); | |
4e4e5a6f | 163 | if (fiberCount == 3) |
f9bf01c6 A |
164 | return new (globalData) JSString(exec, strings[0].jsValue(), strings[1].jsValue(), strings[2].jsValue()); |
165 | ||
4e4e5a6f A |
166 | JSString::RopeBuilder ropeBuilder(fiberCount); |
167 | if (UNLIKELY(ropeBuilder.isOutOfMemory())) | |
f9bf01c6 A |
168 | return throwOutOfMemoryError(exec); |
169 | ||
4e4e5a6f A |
170 | unsigned length = 0; |
171 | bool overflow = false; | |
172 | ||
f9bf01c6 A |
173 | for (unsigned i = 0; i < count; ++i) { |
174 | JSValue v = strings[i].jsValue(); | |
175 | if (LIKELY(v.isString())) | |
4e4e5a6f | 176 | ropeBuilder.append(asString(v)); |
f9bf01c6 | 177 | else |
4e4e5a6f A |
178 | ropeBuilder.append(v.toString(exec)); |
179 | ||
180 | unsigned newLength = ropeBuilder.length(); | |
181 | if (newLength < length) | |
182 | overflow = true; | |
183 | length = newLength; | |
f9bf01c6 A |
184 | } |
185 | ||
4e4e5a6f A |
186 | if (overflow) |
187 | return throwOutOfMemoryError(exec); | |
188 | ||
189 | return new (globalData) JSString(globalData, ropeBuilder.release()); | |
f9bf01c6 A |
190 | } |
191 | ||
192 | ALWAYS_INLINE JSValue jsString(ExecState* exec, JSValue thisValue, const ArgList& args) | |
193 | { | |
4e4e5a6f | 194 | unsigned fiberCount = 0; |
f9bf01c6 | 195 | if (LIKELY(thisValue.isString())) |
4e4e5a6f | 196 | fiberCount += asString(thisValue)->size(); |
f9bf01c6 | 197 | else |
4e4e5a6f | 198 | ++fiberCount; |
f9bf01c6 A |
199 | for (unsigned i = 0; i < args.size(); ++i) { |
200 | JSValue v = args.at(i); | |
201 | if (LIKELY(v.isString())) | |
4e4e5a6f | 202 | fiberCount += asString(v)->size(); |
f9bf01c6 | 203 | else |
4e4e5a6f | 204 | ++fiberCount; |
f9bf01c6 A |
205 | } |
206 | ||
4e4e5a6f A |
207 | JSString::RopeBuilder ropeBuilder(fiberCount); |
208 | if (UNLIKELY(ropeBuilder.isOutOfMemory())) | |
f9bf01c6 A |
209 | return throwOutOfMemoryError(exec); |
210 | ||
f9bf01c6 | 211 | if (LIKELY(thisValue.isString())) |
4e4e5a6f | 212 | ropeBuilder.append(asString(thisValue)); |
f9bf01c6 | 213 | else |
4e4e5a6f A |
214 | ropeBuilder.append(thisValue.toString(exec)); |
215 | ||
216 | unsigned length = 0; | |
217 | bool overflow = false; | |
218 | ||
f9bf01c6 A |
219 | for (unsigned i = 0; i < args.size(); ++i) { |
220 | JSValue v = args.at(i); | |
221 | if (LIKELY(v.isString())) | |
4e4e5a6f | 222 | ropeBuilder.append(asString(v)); |
f9bf01c6 | 223 | else |
4e4e5a6f A |
224 | ropeBuilder.append(v.toString(exec)); |
225 | ||
226 | unsigned newLength = ropeBuilder.length(); | |
227 | if (newLength < length) | |
228 | overflow = true; | |
229 | length = newLength; | |
f9bf01c6 | 230 | } |
4e4e5a6f A |
231 | |
232 | if (overflow) | |
233 | return throwOutOfMemoryError(exec); | |
f9bf01c6 A |
234 | |
235 | JSGlobalData* globalData = &exec->globalData(); | |
4e4e5a6f | 236 | return new (globalData) JSString(globalData, ropeBuilder.release()); |
f9bf01c6 A |
237 | } |
238 | ||
9dae56ea | 239 | // ECMA 11.9.3 |
ba379fdc | 240 | inline bool JSValue::equal(ExecState* exec, JSValue v1, JSValue v2) |
9dae56ea | 241 | { |
ba379fdc | 242 | if (v1.isInt32() && v2.isInt32()) |
9dae56ea A |
243 | return v1 == v2; |
244 | ||
245 | return equalSlowCase(exec, v1, v2); | |
246 | } | |
247 | ||
ba379fdc | 248 | ALWAYS_INLINE bool JSValue::equalSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2) |
9dae56ea | 249 | { |
9dae56ea A |
250 | do { |
251 | if (v1.isNumber() && v2.isNumber()) | |
252 | return v1.uncheckedGetNumber() == v2.uncheckedGetNumber(); | |
253 | ||
254 | bool s1 = v1.isString(); | |
255 | bool s2 = v2.isString(); | |
256 | if (s1 && s2) | |
f9bf01c6 | 257 | return asString(v1)->value(exec) == asString(v2)->value(exec); |
9dae56ea A |
258 | |
259 | if (v1.isUndefinedOrNull()) { | |
260 | if (v2.isUndefinedOrNull()) | |
261 | return true; | |
ba379fdc | 262 | if (!v2.isCell()) |
9dae56ea A |
263 | return false; |
264 | return v2.asCell()->structure()->typeInfo().masqueradesAsUndefined(); | |
265 | } | |
266 | ||
267 | if (v2.isUndefinedOrNull()) { | |
ba379fdc | 268 | if (!v1.isCell()) |
9dae56ea A |
269 | return false; |
270 | return v1.asCell()->structure()->typeInfo().masqueradesAsUndefined(); | |
271 | } | |
272 | ||
273 | if (v1.isObject()) { | |
274 | if (v2.isObject()) | |
275 | return v1 == v2; | |
ba379fdc | 276 | JSValue p1 = v1.toPrimitive(exec); |
9dae56ea A |
277 | if (exec->hadException()) |
278 | return false; | |
279 | v1 = p1; | |
ba379fdc | 280 | if (v1.isInt32() && v2.isInt32()) |
9dae56ea A |
281 | return v1 == v2; |
282 | continue; | |
283 | } | |
284 | ||
285 | if (v2.isObject()) { | |
ba379fdc | 286 | JSValue p2 = v2.toPrimitive(exec); |
9dae56ea A |
287 | if (exec->hadException()) |
288 | return false; | |
289 | v2 = p2; | |
ba379fdc | 290 | if (v1.isInt32() && v2.isInt32()) |
9dae56ea A |
291 | return v1 == v2; |
292 | continue; | |
293 | } | |
294 | ||
295 | if (s1 || s2) { | |
296 | double d1 = v1.toNumber(exec); | |
297 | double d2 = v2.toNumber(exec); | |
298 | return d1 == d2; | |
299 | } | |
300 | ||
301 | if (v1.isBoolean()) { | |
302 | if (v2.isNumber()) | |
303 | return static_cast<double>(v1.getBoolean()) == v2.uncheckedGetNumber(); | |
304 | } else if (v2.isBoolean()) { | |
305 | if (v1.isNumber()) | |
306 | return v1.uncheckedGetNumber() == static_cast<double>(v2.getBoolean()); | |
307 | } | |
308 | ||
309 | return v1 == v2; | |
310 | } while (true); | |
311 | } | |
312 | ||
313 | // ECMA 11.9.3 | |
f9bf01c6 | 314 | ALWAYS_INLINE bool JSValue::strictEqualSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2) |
ba379fdc A |
315 | { |
316 | ASSERT(v1.isCell() && v2.isCell()); | |
317 | ||
318 | if (v1.asCell()->isString() && v2.asCell()->isString()) | |
f9bf01c6 | 319 | return asString(v1)->value(exec) == asString(v2)->value(exec); |
ba379fdc A |
320 | |
321 | return v1 == v2; | |
322 | } | |
323 | ||
f9bf01c6 | 324 | inline bool JSValue::strictEqual(ExecState* exec, JSValue v1, JSValue v2) |
9dae56ea | 325 | { |
ba379fdc | 326 | if (v1.isInt32() && v2.isInt32()) |
9dae56ea A |
327 | return v1 == v2; |
328 | ||
329 | if (v1.isNumber() && v2.isNumber()) | |
330 | return v1.uncheckedGetNumber() == v2.uncheckedGetNumber(); | |
331 | ||
ba379fdc | 332 | if (!v1.isCell() || !v2.isCell()) |
9dae56ea A |
333 | return v1 == v2; |
334 | ||
f9bf01c6 | 335 | return strictEqualSlowCaseInline(exec, v1, v2); |
9dae56ea A |
336 | } |
337 | ||
f9bf01c6 | 338 | ALWAYS_INLINE bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2) |
9dae56ea | 339 | { |
ba379fdc A |
340 | if (v1.isInt32() && v2.isInt32()) |
341 | return v1.asInt32() < v2.asInt32(); | |
9dae56ea | 342 | |
ba379fdc A |
343 | double n1; |
344 | double n2; | |
345 | if (v1.getNumber(n1) && v2.getNumber(n2)) | |
346 | return n1 < n2; | |
9dae56ea | 347 | |
ba379fdc A |
348 | JSGlobalData* globalData = &callFrame->globalData(); |
349 | if (isJSString(globalData, v1) && isJSString(globalData, v2)) | |
f9bf01c6 | 350 | return asString(v1)->value(callFrame) < asString(v2)->value(callFrame); |
ba379fdc A |
351 | |
352 | JSValue p1; | |
353 | JSValue p2; | |
354 | bool wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1); | |
355 | bool wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2); | |
356 | ||
357 | if (wasNotString1 | wasNotString2) | |
358 | return n1 < n2; | |
359 | ||
f9bf01c6 | 360 | return asString(p1)->value(callFrame) < asString(p2)->value(callFrame); |
ba379fdc A |
361 | } |
362 | ||
363 | inline bool jsLessEq(CallFrame* callFrame, JSValue v1, JSValue v2) | |
364 | { | |
365 | if (v1.isInt32() && v2.isInt32()) | |
366 | return v1.asInt32() <= v2.asInt32(); | |
367 | ||
368 | double n1; | |
369 | double n2; | |
370 | if (v1.getNumber(n1) && v2.getNumber(n2)) | |
371 | return n1 <= n2; | |
372 | ||
373 | JSGlobalData* globalData = &callFrame->globalData(); | |
374 | if (isJSString(globalData, v1) && isJSString(globalData, v2)) | |
f9bf01c6 | 375 | return !(asString(v2)->value(callFrame) < asString(v1)->value(callFrame)); |
ba379fdc A |
376 | |
377 | JSValue p1; | |
378 | JSValue p2; | |
379 | bool wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1); | |
380 | bool wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2); | |
381 | ||
382 | if (wasNotString1 | wasNotString2) | |
383 | return n1 <= n2; | |
384 | ||
f9bf01c6 | 385 | return !(asString(p2)->value(callFrame) < asString(p1)->value(callFrame)); |
ba379fdc A |
386 | } |
387 | ||
388 | // Fast-path choices here are based on frequency data from SunSpider: | |
389 | // <times> Add case: <t1> <t2> | |
390 | // --------------------------- | |
391 | // 5626160 Add case: 3 3 (of these, 3637690 are for immediate values) | |
392 | // 247412 Add case: 5 5 | |
393 | // 20900 Add case: 5 6 | |
394 | // 13962 Add case: 5 3 | |
395 | // 4000 Add case: 3 5 | |
396 | ||
397 | ALWAYS_INLINE JSValue jsAdd(CallFrame* callFrame, JSValue v1, JSValue v2) | |
398 | { | |
f9bf01c6 A |
399 | double left = 0.0, right; |
400 | if (v1.getNumber(left) && v2.getNumber(right)) | |
ba379fdc A |
401 | return jsNumber(callFrame, left + right); |
402 | ||
f9bf01c6 A |
403 | if (v1.isString()) { |
404 | return v2.isString() | |
405 | ? jsString(callFrame, asString(v1), asString(v2)) | |
406 | : jsString(callFrame, asString(v1), v2.toPrimitiveString(callFrame)); | |
ba379fdc A |
407 | } |
408 | ||
409 | // All other cases are pretty uncommon | |
410 | return jsAddSlowCase(callFrame, v1, v2); | |
411 | } | |
412 | ||
413 | inline size_t normalizePrototypeChain(CallFrame* callFrame, JSValue base, JSValue slotBase, const Identifier& propertyName, size_t& slotOffset) | |
414 | { | |
415 | JSCell* cell = asCell(base); | |
416 | size_t count = 0; | |
417 | ||
418 | while (slotBase != cell) { | |
419 | JSValue v = cell->structure()->prototypeForLookup(callFrame); | |
420 | ||
421 | // If we didn't find slotBase in base's prototype chain, then base | |
422 | // must be a proxy for another object. | |
423 | ||
424 | if (v.isNull()) | |
425 | return 0; | |
426 | ||
427 | cell = asCell(v); | |
428 | ||
429 | // Since we're accessing a prototype in a loop, it's a good bet that it | |
430 | // should not be treated as a dictionary. | |
431 | if (cell->structure()->isDictionary()) { | |
432 | asObject(cell)->flattenDictionaryObject(); | |
433 | if (slotBase == cell) | |
434 | slotOffset = cell->structure()->get(propertyName); | |
435 | } | |
436 | ||
437 | ++count; | |
438 | } | |
439 | ||
440 | ASSERT(count); | |
441 | return count; | |
442 | } | |
443 | ||
f9bf01c6 A |
444 | inline size_t normalizePrototypeChain(CallFrame* callFrame, JSCell* base) |
445 | { | |
446 | size_t count = 0; | |
447 | while (1) { | |
448 | JSValue v = base->structure()->prototypeForLookup(callFrame); | |
449 | if (v.isNull()) | |
450 | return count; | |
451 | ||
452 | base = asCell(v); | |
453 | ||
454 | // Since we're accessing a prototype in a loop, it's a good bet that it | |
455 | // should not be treated as a dictionary. | |
456 | if (base->structure()->isDictionary()) | |
457 | asObject(base)->flattenDictionaryObject(); | |
458 | ||
459 | ++count; | |
460 | } | |
461 | } | |
462 | ||
ba379fdc A |
463 | ALWAYS_INLINE JSValue resolveBase(CallFrame* callFrame, Identifier& property, ScopeChainNode* scopeChain) |
464 | { | |
465 | ScopeChainIterator iter = scopeChain->begin(); | |
466 | ScopeChainIterator next = iter; | |
467 | ++next; | |
468 | ScopeChainIterator end = scopeChain->end(); | |
469 | ASSERT(iter != end); | |
470 | ||
471 | PropertySlot slot; | |
472 | JSObject* base; | |
473 | while (true) { | |
474 | base = *iter; | |
475 | if (next == end || base->getPropertySlot(callFrame, property, slot)) | |
476 | return base; | |
477 | ||
478 | iter = next; | |
479 | ++next; | |
480 | } | |
481 | ||
482 | ASSERT_NOT_REACHED(); | |
483 | return JSValue(); | |
484 | } | |
ba379fdc | 485 | } // namespace JSC |
9dae56ea | 486 | |
ba379fdc | 487 | #endif // Operations_h |