|
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 |
|
25 #include "ExceptionHelpers.h" |
|
26 #include "Interpreter.h" |
|
27 #include "JSImmediate.h" |
|
28 #include "JSNumberCell.h" |
|
29 #include "JSString.h" |
|
30 |
|
31 namespace 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->size() + s2->size(); |
|
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->size(); |
|
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->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 |
|
124 JSGlobalData* globalData = &exec->globalData(); |
|
125 return new (globalData) JSString(globalData, u1, u2); |
|
126 } |
|
127 |
|
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); |
|
139 |
|
140 if ((length1 + length2) < length1) |
|
141 return throwOutOfMemoryError(exec); |
|
142 if ((length1 + length2 + length3) < length3) |
|
143 return throwOutOfMemoryError(exec); |
|
144 |
|
145 JSGlobalData* globalData = &exec->globalData(); |
|
146 return new (globalData) JSString(globalData, u1, u2, u3); |
|
147 } |
|
148 |
|
149 ALWAYS_INLINE JSValue jsString(ExecState* exec, Register* strings, unsigned count) |
|
150 { |
|
151 ASSERT(count >= 3); |
|
152 |
|
153 unsigned fiberCount = 0; |
|
154 for (unsigned i = 0; i < count; ++i) { |
|
155 JSValue v = strings[i].jsValue(); |
|
156 if (LIKELY(v.isString())) |
|
157 fiberCount += asString(v)->size(); |
|
158 else |
|
159 ++fiberCount; |
|
160 } |
|
161 |
|
162 JSGlobalData* globalData = &exec->globalData(); |
|
163 if (fiberCount == 3) |
|
164 return new (globalData) JSString(exec, strings[0].jsValue(), strings[1].jsValue(), strings[2].jsValue()); |
|
165 |
|
166 JSString::RopeBuilder ropeBuilder(fiberCount); |
|
167 if (UNLIKELY(ropeBuilder.isOutOfMemory())) |
|
168 return throwOutOfMemoryError(exec); |
|
169 |
|
170 unsigned length = 0; |
|
171 bool overflow = false; |
|
172 |
|
173 for (unsigned i = 0; i < count; ++i) { |
|
174 JSValue v = strings[i].jsValue(); |
|
175 if (LIKELY(v.isString())) |
|
176 ropeBuilder.append(asString(v)); |
|
177 else |
|
178 ropeBuilder.append(v.toString(exec)); |
|
179 |
|
180 unsigned newLength = ropeBuilder.length(); |
|
181 if (newLength < length) |
|
182 overflow = true; |
|
183 length = newLength; |
|
184 } |
|
185 |
|
186 if (overflow) |
|
187 return throwOutOfMemoryError(exec); |
|
188 |
|
189 return new (globalData) JSString(globalData, ropeBuilder.release()); |
|
190 } |
|
191 |
|
192 ALWAYS_INLINE JSValue jsString(ExecState* exec, JSValue thisValue) |
|
193 { |
|
194 unsigned fiberCount = 0; |
|
195 if (LIKELY(thisValue.isString())) |
|
196 fiberCount += asString(thisValue)->size(); |
|
197 else |
|
198 ++fiberCount; |
|
199 for (unsigned i = 0; i < exec->argumentCount(); ++i) { |
|
200 JSValue v = exec->argument(i); |
|
201 if (LIKELY(v.isString())) |
|
202 fiberCount += asString(v)->size(); |
|
203 else |
|
204 ++fiberCount; |
|
205 } |
|
206 |
|
207 JSString::RopeBuilder ropeBuilder(fiberCount); |
|
208 if (UNLIKELY(ropeBuilder.isOutOfMemory())) |
|
209 return throwOutOfMemoryError(exec); |
|
210 |
|
211 if (LIKELY(thisValue.isString())) |
|
212 ropeBuilder.append(asString(thisValue)); |
|
213 else |
|
214 ropeBuilder.append(thisValue.toString(exec)); |
|
215 |
|
216 unsigned length = 0; |
|
217 bool overflow = false; |
|
218 |
|
219 for (unsigned i = 0; i < exec->argumentCount(); ++i) { |
|
220 JSValue v = exec->argument(i); |
|
221 if (LIKELY(v.isString())) |
|
222 ropeBuilder.append(asString(v)); |
|
223 else |
|
224 ropeBuilder.append(v.toString(exec)); |
|
225 |
|
226 unsigned newLength = ropeBuilder.length(); |
|
227 if (newLength < length) |
|
228 overflow = true; |
|
229 length = newLength; |
|
230 } |
|
231 |
|
232 if (overflow) |
|
233 return throwOutOfMemoryError(exec); |
|
234 |
|
235 JSGlobalData* globalData = &exec->globalData(); |
|
236 return new (globalData) JSString(globalData, ropeBuilder.release()); |
|
237 } |
|
238 |
|
239 // ECMA 11.9.3 |
|
240 inline bool JSValue::equal(ExecState* exec, JSValue v1, JSValue v2) |
|
241 { |
|
242 if (v1.isInt32() && v2.isInt32()) |
|
243 return v1 == v2; |
|
244 |
|
245 return equalSlowCase(exec, v1, v2); |
|
246 } |
|
247 |
|
248 ALWAYS_INLINE bool JSValue::equalSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2) |
|
249 { |
|
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) |
|
257 return asString(v1)->value(exec) == asString(v2)->value(exec); |
|
258 |
|
259 if (v1.isUndefinedOrNull()) { |
|
260 if (v2.isUndefinedOrNull()) |
|
261 return true; |
|
262 if (!v2.isCell()) |
|
263 return false; |
|
264 return v2.asCell()->structure()->typeInfo().masqueradesAsUndefined(); |
|
265 } |
|
266 |
|
267 if (v2.isUndefinedOrNull()) { |
|
268 if (!v1.isCell()) |
|
269 return false; |
|
270 return v1.asCell()->structure()->typeInfo().masqueradesAsUndefined(); |
|
271 } |
|
272 |
|
273 if (v1.isObject()) { |
|
274 if (v2.isObject()) |
|
275 return v1 == v2; |
|
276 JSValue p1 = v1.toPrimitive(exec); |
|
277 if (exec->hadException()) |
|
278 return false; |
|
279 v1 = p1; |
|
280 if (v1.isInt32() && v2.isInt32()) |
|
281 return v1 == v2; |
|
282 continue; |
|
283 } |
|
284 |
|
285 if (v2.isObject()) { |
|
286 JSValue p2 = v2.toPrimitive(exec); |
|
287 if (exec->hadException()) |
|
288 return false; |
|
289 v2 = p2; |
|
290 if (v1.isInt32() && v2.isInt32()) |
|
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 |
|
314 ALWAYS_INLINE bool JSValue::strictEqualSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2) |
|
315 { |
|
316 ASSERT(v1.isCell() && v2.isCell()); |
|
317 |
|
318 if (v1.asCell()->isString() && v2.asCell()->isString()) |
|
319 return asString(v1)->value(exec) == asString(v2)->value(exec); |
|
320 |
|
321 return v1 == v2; |
|
322 } |
|
323 |
|
324 inline bool JSValue::strictEqual(ExecState* exec, JSValue v1, JSValue v2) |
|
325 { |
|
326 if (v1.isInt32() && v2.isInt32()) |
|
327 return v1 == v2; |
|
328 |
|
329 if (v1.isNumber() && v2.isNumber()) |
|
330 return v1.uncheckedGetNumber() == v2.uncheckedGetNumber(); |
|
331 |
|
332 if (!v1.isCell() || !v2.isCell()) |
|
333 return v1 == v2; |
|
334 |
|
335 return strictEqualSlowCaseInline(exec, v1, v2); |
|
336 } |
|
337 |
|
338 ALWAYS_INLINE bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2) |
|
339 { |
|
340 if (v1.isInt32() && v2.isInt32()) |
|
341 return v1.asInt32() < v2.asInt32(); |
|
342 |
|
343 double n1; |
|
344 double n2; |
|
345 if (v1.getNumber(n1) && v2.getNumber(n2)) |
|
346 return n1 < n2; |
|
347 |
|
348 JSGlobalData* globalData = &callFrame->globalData(); |
|
349 if (isJSString(globalData, v1) && isJSString(globalData, v2)) |
|
350 return asString(v1)->value(callFrame) < asString(v2)->value(callFrame); |
|
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 |
|
360 return asString(p1)->value(callFrame) < asString(p2)->value(callFrame); |
|
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)) |
|
375 return !(asString(v2)->value(callFrame) < asString(v1)->value(callFrame)); |
|
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 |
|
385 return !(asString(p2)->value(callFrame) < asString(p1)->value(callFrame)); |
|
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 { |
|
399 double left = 0.0, right; |
|
400 if (v1.getNumber(left) && v2.getNumber(right)) |
|
401 return jsNumber(callFrame, left + right); |
|
402 |
|
403 if (v1.isString()) { |
|
404 return v2.isString() |
|
405 ? jsString(callFrame, asString(v1), asString(v2)) |
|
406 : jsString(callFrame, asString(v1), v2.toPrimitiveString(callFrame)); |
|
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 |
|
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 |
|
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 } |
|
485 } // namespace JSC |
|
486 |
|
487 #endif // Operations_h |