|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #ifndef vm_Interpreter_inl_h |
|
8 #define vm_Interpreter_inl_h |
|
9 |
|
10 #include "vm/Interpreter.h" |
|
11 |
|
12 #include "jscompartment.h" |
|
13 #include "jsinfer.h" |
|
14 #include "jsnum.h" |
|
15 #include "jsstr.h" |
|
16 |
|
17 #include "jit/Ion.h" |
|
18 #include "vm/ArgumentsObject.h" |
|
19 #include "vm/ForkJoin.h" |
|
20 |
|
21 #include "jsatominlines.h" |
|
22 #include "jsinferinlines.h" |
|
23 #include "jsobjinlines.h" |
|
24 |
|
25 #include "vm/Stack-inl.h" |
|
26 #include "vm/String-inl.h" |
|
27 |
|
28 namespace js { |
|
29 |
|
30 inline bool |
|
31 ComputeThis(JSContext *cx, AbstractFramePtr frame) |
|
32 { |
|
33 JS_ASSERT_IF(frame.isInterpreterFrame(), !frame.asInterpreterFrame()->runningInJit()); |
|
34 |
|
35 if (frame.isFunctionFrame() && frame.fun()->isArrow()) { |
|
36 /* |
|
37 * Arrow functions store their (lexical) |this| value in an |
|
38 * extended slot. |
|
39 */ |
|
40 frame.thisValue() = frame.fun()->getExtendedSlot(0); |
|
41 return true; |
|
42 } |
|
43 |
|
44 if (frame.thisValue().isObject()) |
|
45 return true; |
|
46 RootedValue thisv(cx, frame.thisValue()); |
|
47 if (frame.isFunctionFrame()) { |
|
48 if (frame.fun()->strict() || frame.fun()->isSelfHostedBuiltin()) |
|
49 return true; |
|
50 /* |
|
51 * Eval function frames have their own |this| slot, which is a copy of the function's |
|
52 * |this| slot. If we lazily wrap a primitive |this| in an eval function frame, the |
|
53 * eval's frame will get the wrapper, but the function's frame will not. To prevent |
|
54 * this, we always wrap a function's |this| before pushing an eval frame, and should |
|
55 * thus never see an unwrapped primitive in a non-strict eval function frame. Null |
|
56 * and undefined |this| values will unwrap to the same object in the function and |
|
57 * eval frames, so are not required to be wrapped. |
|
58 */ |
|
59 JS_ASSERT_IF(frame.isEvalFrame(), thisv.isUndefined() || thisv.isNull()); |
|
60 } |
|
61 |
|
62 JSObject *thisObj = BoxNonStrictThis(cx, thisv); |
|
63 if (!thisObj) |
|
64 return false; |
|
65 |
|
66 frame.thisValue().setObject(*thisObj); |
|
67 return true; |
|
68 } |
|
69 |
|
70 /* |
|
71 * Every possible consumer of MagicValue(JS_OPTIMIZED_ARGUMENTS) (as determined |
|
72 * by ScriptAnalysis::needsArgsObj) must check for these magic values and, when |
|
73 * one is received, act as if the value were the function's ArgumentsObject. |
|
74 * Additionally, it is possible that, after 'arguments' was copied into a |
|
75 * temporary, the arguments object has been created a some other failed guard |
|
76 * that called JSScript::argumentsOptimizationFailed. In this case, it is |
|
77 * always valid (and necessary) to replace JS_OPTIMIZED_ARGUMENTS with the real |
|
78 * arguments object. |
|
79 */ |
|
80 static inline bool |
|
81 IsOptimizedArguments(AbstractFramePtr frame, Value *vp) |
|
82 { |
|
83 if (vp->isMagic(JS_OPTIMIZED_ARGUMENTS) && frame.script()->needsArgsObj()) |
|
84 *vp = ObjectValue(frame.argsObj()); |
|
85 return vp->isMagic(JS_OPTIMIZED_ARGUMENTS); |
|
86 } |
|
87 |
|
88 /* |
|
89 * One optimized consumer of MagicValue(JS_OPTIMIZED_ARGUMENTS) is f.apply. |
|
90 * However, this speculation must be guarded before calling 'apply' in case it |
|
91 * is not the builtin Function.prototype.apply. |
|
92 */ |
|
93 static inline bool |
|
94 GuardFunApplyArgumentsOptimization(JSContext *cx, AbstractFramePtr frame, HandleValue callee, |
|
95 Value *args, uint32_t argc) |
|
96 { |
|
97 if (argc == 2 && IsOptimizedArguments(frame, &args[1])) { |
|
98 if (!IsNativeFunction(callee, js_fun_apply)) { |
|
99 RootedScript script(cx, frame.script()); |
|
100 if (!JSScript::argumentsOptimizationFailed(cx, script)) |
|
101 return false; |
|
102 args[1] = ObjectValue(frame.argsObj()); |
|
103 } |
|
104 } |
|
105 |
|
106 return true; |
|
107 } |
|
108 |
|
109 /* |
|
110 * Return an object on which we should look for the properties of |value|. |
|
111 * This helps us implement the custom [[Get]] method that ES5's GetValue |
|
112 * algorithm uses for primitive values, without actually constructing the |
|
113 * temporary object that the specification does. |
|
114 * |
|
115 * For objects, return the object itself. For string, boolean, and number |
|
116 * primitive values, return the appropriate constructor's prototype. For |
|
117 * undefined and null, throw an error and return nullptr, attributing the |
|
118 * problem to the value at |spindex| on the stack. |
|
119 */ |
|
120 MOZ_ALWAYS_INLINE JSObject * |
|
121 ValuePropertyBearer(JSContext *cx, InterpreterFrame *fp, HandleValue v, int spindex) |
|
122 { |
|
123 if (v.isObject()) |
|
124 return &v.toObject(); |
|
125 |
|
126 Rooted<GlobalObject*> global(cx, &fp->global()); |
|
127 |
|
128 if (v.isString()) |
|
129 return GlobalObject::getOrCreateStringPrototype(cx, global); |
|
130 if (v.isNumber()) |
|
131 return GlobalObject::getOrCreateNumberPrototype(cx, global); |
|
132 if (v.isBoolean()) |
|
133 return GlobalObject::getOrCreateBooleanPrototype(cx, global); |
|
134 |
|
135 JS_ASSERT(v.isNull() || v.isUndefined()); |
|
136 js_ReportIsNullOrUndefined(cx, spindex, v, NullPtr()); |
|
137 return nullptr; |
|
138 } |
|
139 |
|
140 inline bool |
|
141 GetLengthProperty(const Value &lval, MutableHandleValue vp) |
|
142 { |
|
143 /* Optimize length accesses on strings, arrays, and arguments. */ |
|
144 if (lval.isString()) { |
|
145 vp.setInt32(lval.toString()->length()); |
|
146 return true; |
|
147 } |
|
148 if (lval.isObject()) { |
|
149 JSObject *obj = &lval.toObject(); |
|
150 if (obj->is<ArrayObject>()) { |
|
151 vp.setNumber(obj->as<ArrayObject>().length()); |
|
152 return true; |
|
153 } |
|
154 |
|
155 if (obj->is<ArgumentsObject>()) { |
|
156 ArgumentsObject *argsobj = &obj->as<ArgumentsObject>(); |
|
157 if (!argsobj->hasOverriddenLength()) { |
|
158 uint32_t length = argsobj->initialLength(); |
|
159 JS_ASSERT(length < INT32_MAX); |
|
160 vp.setInt32(int32_t(length)); |
|
161 return true; |
|
162 } |
|
163 } |
|
164 } |
|
165 |
|
166 return false; |
|
167 } |
|
168 |
|
169 template <bool TypeOf> inline bool |
|
170 FetchName(JSContext *cx, HandleObject obj, HandleObject obj2, HandlePropertyName name, |
|
171 HandleShape shape, MutableHandleValue vp) |
|
172 { |
|
173 if (!shape) { |
|
174 if (TypeOf) { |
|
175 vp.setUndefined(); |
|
176 return true; |
|
177 } |
|
178 JSAutoByteString printable; |
|
179 if (AtomToPrintableString(cx, name, &printable)) |
|
180 js_ReportIsNotDefined(cx, printable.ptr()); |
|
181 return false; |
|
182 } |
|
183 |
|
184 /* Take the slow path if shape was not found in a native object. */ |
|
185 if (!obj->isNative() || !obj2->isNative()) { |
|
186 Rooted<jsid> id(cx, NameToId(name)); |
|
187 if (!JSObject::getGeneric(cx, obj, obj, id, vp)) |
|
188 return false; |
|
189 } else { |
|
190 Rooted<JSObject*> normalized(cx, obj); |
|
191 if (normalized->is<DynamicWithObject>() && !shape->hasDefaultGetter()) |
|
192 normalized = &normalized->as<DynamicWithObject>().object(); |
|
193 if (shape->isDataDescriptor() && shape->hasDefaultGetter()) { |
|
194 /* Fast path for Object instance properties. */ |
|
195 JS_ASSERT(shape->hasSlot()); |
|
196 vp.set(obj2->nativeGetSlot(shape->slot())); |
|
197 } else if (!NativeGet(cx, normalized, obj2, shape, vp)) { |
|
198 return false; |
|
199 } |
|
200 } |
|
201 return true; |
|
202 } |
|
203 |
|
204 inline bool |
|
205 FetchNameNoGC(JSObject *pobj, Shape *shape, MutableHandleValue vp) |
|
206 { |
|
207 if (!shape || !pobj->isNative() || !shape->isDataDescriptor() || !shape->hasDefaultGetter()) |
|
208 return false; |
|
209 |
|
210 vp.set(pobj->nativeGetSlot(shape->slot())); |
|
211 return true; |
|
212 } |
|
213 |
|
214 inline bool |
|
215 GetIntrinsicOperation(JSContext *cx, jsbytecode *pc, MutableHandleValue vp) |
|
216 { |
|
217 RootedPropertyName name(cx, cx->currentScript()->getName(pc)); |
|
218 return GlobalObject::getIntrinsicValue(cx, cx->global(), name, vp); |
|
219 } |
|
220 |
|
221 inline bool |
|
222 SetIntrinsicOperation(JSContext *cx, JSScript *script, jsbytecode *pc, HandleValue val) |
|
223 { |
|
224 RootedPropertyName name(cx, script->getName(pc)); |
|
225 return cx->global()->setIntrinsicValue(cx, name, val); |
|
226 } |
|
227 |
|
228 inline bool |
|
229 SetNameOperation(JSContext *cx, JSScript *script, jsbytecode *pc, HandleObject scope, |
|
230 HandleValue val) |
|
231 { |
|
232 JS_ASSERT(*pc == JSOP_SETNAME || *pc == JSOP_SETGNAME); |
|
233 JS_ASSERT_IF(*pc == JSOP_SETGNAME, scope == cx->global()); |
|
234 |
|
235 bool strict = script->strict(); |
|
236 RootedPropertyName name(cx, script->getName(pc)); |
|
237 RootedValue valCopy(cx, val); |
|
238 |
|
239 /* |
|
240 * In strict-mode, we need to trigger an error when trying to assign to an |
|
241 * undeclared global variable. To do this, we call SetPropertyHelper |
|
242 * directly and pass Unqualified. |
|
243 */ |
|
244 if (scope->is<GlobalObject>()) { |
|
245 JS_ASSERT(!scope->getOps()->setProperty); |
|
246 RootedId id(cx, NameToId(name)); |
|
247 return baseops::SetPropertyHelper<SequentialExecution>(cx, scope, scope, id, |
|
248 baseops::Unqualified, &valCopy, |
|
249 strict); |
|
250 } |
|
251 |
|
252 return JSObject::setProperty(cx, scope, scope, name, &valCopy, strict); |
|
253 } |
|
254 |
|
255 inline bool |
|
256 DefVarOrConstOperation(JSContext *cx, HandleObject varobj, HandlePropertyName dn, unsigned attrs) |
|
257 { |
|
258 JS_ASSERT(varobj->isVarObj()); |
|
259 |
|
260 RootedShape prop(cx); |
|
261 RootedObject obj2(cx); |
|
262 if (!JSObject::lookupProperty(cx, varobj, dn, &obj2, &prop)) |
|
263 return false; |
|
264 |
|
265 /* Steps 8c, 8d. */ |
|
266 if (!prop || (obj2 != varobj && varobj->is<GlobalObject>())) { |
|
267 if (!JSObject::defineProperty(cx, varobj, dn, UndefinedHandleValue, JS_PropertyStub, |
|
268 JS_StrictPropertyStub, attrs)) { |
|
269 return false; |
|
270 } |
|
271 } else if (attrs & JSPROP_READONLY) { |
|
272 /* |
|
273 * Extension: ordinarily we'd be done here -- but for |const|. If we |
|
274 * see a redeclaration that's |const|, we consider it a conflict. |
|
275 */ |
|
276 unsigned oldAttrs; |
|
277 RootedId id(cx, NameToId(dn)); |
|
278 if (!JSObject::getGenericAttributes(cx, varobj, id, &oldAttrs)) |
|
279 return false; |
|
280 |
|
281 JSAutoByteString bytes; |
|
282 if (AtomToPrintableString(cx, dn, &bytes)) { |
|
283 JS_ALWAYS_FALSE(JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, |
|
284 js_GetErrorMessage, |
|
285 nullptr, JSMSG_REDECLARED_VAR, |
|
286 (oldAttrs & JSPROP_READONLY) |
|
287 ? "const" |
|
288 : "var", |
|
289 bytes.ptr())); |
|
290 } |
|
291 return false; |
|
292 } |
|
293 |
|
294 return true; |
|
295 } |
|
296 |
|
297 static MOZ_ALWAYS_INLINE bool |
|
298 NegOperation(JSContext *cx, HandleScript script, jsbytecode *pc, HandleValue val, |
|
299 MutableHandleValue res) |
|
300 { |
|
301 /* |
|
302 * When the operand is int jsval, INT32_FITS_IN_JSVAL(i) implies |
|
303 * INT32_FITS_IN_JSVAL(-i) unless i is 0 or INT32_MIN when the |
|
304 * results, -0.0 or INT32_MAX + 1, are double values. |
|
305 */ |
|
306 int32_t i; |
|
307 if (val.isInt32() && (i = val.toInt32()) != 0 && i != INT32_MIN) { |
|
308 res.setInt32(-i); |
|
309 } else { |
|
310 double d; |
|
311 if (!ToNumber(cx, val, &d)) |
|
312 return false; |
|
313 res.setNumber(-d); |
|
314 } |
|
315 |
|
316 return true; |
|
317 } |
|
318 |
|
319 static MOZ_ALWAYS_INLINE bool |
|
320 ToIdOperation(JSContext *cx, HandleScript script, jsbytecode *pc, HandleValue objval, |
|
321 HandleValue idval, MutableHandleValue res) |
|
322 { |
|
323 if (idval.isInt32()) { |
|
324 res.set(idval); |
|
325 return true; |
|
326 } |
|
327 |
|
328 JSObject *obj = ToObjectFromStack(cx, objval); |
|
329 if (!obj) |
|
330 return false; |
|
331 |
|
332 RootedId id(cx); |
|
333 if (!ValueToId<CanGC>(cx, idval, &id)) |
|
334 return false; |
|
335 |
|
336 res.set(IdToValue(id)); |
|
337 return true; |
|
338 } |
|
339 |
|
340 static MOZ_ALWAYS_INLINE bool |
|
341 GetObjectElementOperation(JSContext *cx, JSOp op, JSObject *objArg, bool wasObject, |
|
342 HandleValue rref, MutableHandleValue res) |
|
343 { |
|
344 JS_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM); |
|
345 |
|
346 do { |
|
347 uint32_t index; |
|
348 if (IsDefinitelyIndex(rref, &index)) { |
|
349 if (JSObject::getElementNoGC(cx, objArg, objArg, index, res.address())) |
|
350 break; |
|
351 |
|
352 RootedObject obj(cx, objArg); |
|
353 if (!JSObject::getElement(cx, obj, obj, index, res)) |
|
354 return false; |
|
355 objArg = obj; |
|
356 break; |
|
357 } |
|
358 |
|
359 JSAtom *name = ToAtom<NoGC>(cx, rref); |
|
360 if (name) { |
|
361 if (name->isIndex(&index)) { |
|
362 if (JSObject::getElementNoGC(cx, objArg, objArg, index, res.address())) |
|
363 break; |
|
364 } else { |
|
365 if (JSObject::getPropertyNoGC(cx, objArg, objArg, name->asPropertyName(), res.address())) |
|
366 break; |
|
367 } |
|
368 } |
|
369 |
|
370 RootedObject obj(cx, objArg); |
|
371 |
|
372 name = ToAtom<CanGC>(cx, rref); |
|
373 if (!name) |
|
374 return false; |
|
375 |
|
376 if (name->isIndex(&index)) { |
|
377 if (!JSObject::getElement(cx, obj, obj, index, res)) |
|
378 return false; |
|
379 } else { |
|
380 if (!JSObject::getProperty(cx, obj, obj, name->asPropertyName(), res)) |
|
381 return false; |
|
382 } |
|
383 |
|
384 objArg = obj; |
|
385 } while (0); |
|
386 |
|
387 #if JS_HAS_NO_SUCH_METHOD |
|
388 if (op == JSOP_CALLELEM && MOZ_UNLIKELY(res.isUndefined()) && wasObject) { |
|
389 RootedObject obj(cx, objArg); |
|
390 if (!OnUnknownMethod(cx, obj, rref, res)) |
|
391 return false; |
|
392 } |
|
393 #endif |
|
394 |
|
395 assertSameCompartmentDebugOnly(cx, res); |
|
396 return true; |
|
397 } |
|
398 |
|
399 static MOZ_ALWAYS_INLINE bool |
|
400 GetElemOptimizedArguments(JSContext *cx, AbstractFramePtr frame, MutableHandleValue lref, |
|
401 HandleValue rref, MutableHandleValue res, bool *done) |
|
402 { |
|
403 JS_ASSERT(!*done); |
|
404 |
|
405 if (IsOptimizedArguments(frame, lref.address())) { |
|
406 if (rref.isInt32()) { |
|
407 int32_t i = rref.toInt32(); |
|
408 if (i >= 0 && uint32_t(i) < frame.numActualArgs()) { |
|
409 res.set(frame.unaliasedActual(i)); |
|
410 *done = true; |
|
411 return true; |
|
412 } |
|
413 } |
|
414 |
|
415 RootedScript script(cx, frame.script()); |
|
416 if (!JSScript::argumentsOptimizationFailed(cx, script)) |
|
417 return false; |
|
418 |
|
419 lref.set(ObjectValue(frame.argsObj())); |
|
420 } |
|
421 |
|
422 return true; |
|
423 } |
|
424 |
|
425 static MOZ_ALWAYS_INLINE bool |
|
426 GetElementOperation(JSContext *cx, JSOp op, MutableHandleValue lref, HandleValue rref, |
|
427 MutableHandleValue res) |
|
428 { |
|
429 JS_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM); |
|
430 |
|
431 uint32_t index; |
|
432 if (lref.isString() && IsDefinitelyIndex(rref, &index)) { |
|
433 JSString *str = lref.toString(); |
|
434 if (index < str->length()) { |
|
435 str = cx->staticStrings().getUnitStringForElement(cx, str, index); |
|
436 if (!str) |
|
437 return false; |
|
438 res.setString(str); |
|
439 return true; |
|
440 } |
|
441 } |
|
442 |
|
443 bool isObject = lref.isObject(); |
|
444 JSObject *obj = ToObjectFromStack(cx, lref); |
|
445 if (!obj) |
|
446 return false; |
|
447 return GetObjectElementOperation(cx, op, obj, isObject, rref, res); |
|
448 } |
|
449 |
|
450 static MOZ_ALWAYS_INLINE JSString * |
|
451 TypeOfOperation(const Value &v, JSRuntime *rt) |
|
452 { |
|
453 JSType type = js::TypeOfValue(v); |
|
454 return TypeName(type, *rt->commonNames); |
|
455 } |
|
456 |
|
457 static inline JSString * |
|
458 TypeOfObjectOperation(JSObject *obj, JSRuntime *rt) |
|
459 { |
|
460 JSType type = js::TypeOfObject(obj); |
|
461 return TypeName(type, *rt->commonNames); |
|
462 } |
|
463 |
|
464 static MOZ_ALWAYS_INLINE bool |
|
465 InitElemOperation(JSContext *cx, HandleObject obj, HandleValue idval, HandleValue val) |
|
466 { |
|
467 JS_ASSERT(!val.isMagic(JS_ELEMENTS_HOLE)); |
|
468 |
|
469 RootedId id(cx); |
|
470 if (!ValueToId<CanGC>(cx, idval, &id)) |
|
471 return false; |
|
472 |
|
473 return JSObject::defineGeneric(cx, obj, id, val, nullptr, nullptr, JSPROP_ENUMERATE); |
|
474 } |
|
475 |
|
476 static MOZ_ALWAYS_INLINE bool |
|
477 InitArrayElemOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, uint32_t index, HandleValue val) |
|
478 { |
|
479 JSOp op = JSOp(*pc); |
|
480 JS_ASSERT(op == JSOP_INITELEM_ARRAY || op == JSOP_INITELEM_INC); |
|
481 |
|
482 JS_ASSERT(obj->is<ArrayObject>()); |
|
483 |
|
484 /* |
|
485 * If val is a hole, do not call JSObject::defineElement. In this case, |
|
486 * if the current op is the last element initialiser, set the array length |
|
487 * to one greater than id. |
|
488 */ |
|
489 if (val.isMagic(JS_ELEMENTS_HOLE)) { |
|
490 JSOp next = JSOp(*GetNextPc(pc)); |
|
491 |
|
492 if ((op == JSOP_INITELEM_ARRAY && next == JSOP_ENDINIT) || |
|
493 (op == JSOP_INITELEM_INC && next == JSOP_POP)) |
|
494 { |
|
495 if (!SetLengthProperty(cx, obj, index + 1)) |
|
496 return false; |
|
497 } |
|
498 } else { |
|
499 if (!JSObject::defineElement(cx, obj, index, val, nullptr, nullptr, JSPROP_ENUMERATE)) |
|
500 return false; |
|
501 } |
|
502 |
|
503 if (op == JSOP_INITELEM_INC && index == INT32_MAX) { |
|
504 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SPREAD_TOO_LARGE); |
|
505 return false; |
|
506 } |
|
507 |
|
508 return true; |
|
509 } |
|
510 |
|
511 #define RELATIONAL_OP(OP) \ |
|
512 JS_BEGIN_MACRO \ |
|
513 /* Optimize for two int-tagged operands (typical loop control). */ \ |
|
514 if (lhs.isInt32() && rhs.isInt32()) { \ |
|
515 *res = lhs.toInt32() OP rhs.toInt32(); \ |
|
516 } else { \ |
|
517 if (!ToPrimitive(cx, JSTYPE_NUMBER, lhs)) \ |
|
518 return false; \ |
|
519 if (!ToPrimitive(cx, JSTYPE_NUMBER, rhs)) \ |
|
520 return false; \ |
|
521 if (lhs.isString() && rhs.isString()) { \ |
|
522 JSString *l = lhs.toString(), *r = rhs.toString(); \ |
|
523 int32_t result; \ |
|
524 if (!CompareStrings(cx, l, r, &result)) \ |
|
525 return false; \ |
|
526 *res = result OP 0; \ |
|
527 } else { \ |
|
528 double l, r; \ |
|
529 if (!ToNumber(cx, lhs, &l) || !ToNumber(cx, rhs, &r)) \ |
|
530 return false;; \ |
|
531 *res = (l OP r); \ |
|
532 } \ |
|
533 } \ |
|
534 return true; \ |
|
535 JS_END_MACRO |
|
536 |
|
537 static MOZ_ALWAYS_INLINE bool |
|
538 LessThanOperation(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) { |
|
539 RELATIONAL_OP(<); |
|
540 } |
|
541 |
|
542 static MOZ_ALWAYS_INLINE bool |
|
543 LessThanOrEqualOperation(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) { |
|
544 RELATIONAL_OP(<=); |
|
545 } |
|
546 |
|
547 static MOZ_ALWAYS_INLINE bool |
|
548 GreaterThanOperation(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) { |
|
549 RELATIONAL_OP(>); |
|
550 } |
|
551 |
|
552 static MOZ_ALWAYS_INLINE bool |
|
553 GreaterThanOrEqualOperation(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) { |
|
554 RELATIONAL_OP(>=); |
|
555 } |
|
556 |
|
557 static MOZ_ALWAYS_INLINE bool |
|
558 BitNot(JSContext *cx, HandleValue in, int *out) |
|
559 { |
|
560 int i; |
|
561 if (!ToInt32(cx, in, &i)) |
|
562 return false; |
|
563 *out = ~i; |
|
564 return true; |
|
565 } |
|
566 |
|
567 static MOZ_ALWAYS_INLINE bool |
|
568 BitXor(JSContext *cx, HandleValue lhs, HandleValue rhs, int *out) |
|
569 { |
|
570 int left, right; |
|
571 if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right)) |
|
572 return false; |
|
573 *out = left ^ right; |
|
574 return true; |
|
575 } |
|
576 |
|
577 static MOZ_ALWAYS_INLINE bool |
|
578 BitOr(JSContext *cx, HandleValue lhs, HandleValue rhs, int *out) |
|
579 { |
|
580 int left, right; |
|
581 if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right)) |
|
582 return false; |
|
583 *out = left | right; |
|
584 return true; |
|
585 } |
|
586 |
|
587 static MOZ_ALWAYS_INLINE bool |
|
588 BitAnd(JSContext *cx, HandleValue lhs, HandleValue rhs, int *out) |
|
589 { |
|
590 int left, right; |
|
591 if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right)) |
|
592 return false; |
|
593 *out = left & right; |
|
594 return true; |
|
595 } |
|
596 |
|
597 static MOZ_ALWAYS_INLINE bool |
|
598 BitLsh(JSContext *cx, HandleValue lhs, HandleValue rhs, int *out) |
|
599 { |
|
600 int32_t left, right; |
|
601 if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right)) |
|
602 return false; |
|
603 *out = uint32_t(left) << (right & 31); |
|
604 return true; |
|
605 } |
|
606 |
|
607 static MOZ_ALWAYS_INLINE bool |
|
608 BitRsh(JSContext *cx, HandleValue lhs, HandleValue rhs, int *out) |
|
609 { |
|
610 int32_t left, right; |
|
611 if (!ToInt32(cx, lhs, &left) || !ToInt32(cx, rhs, &right)) |
|
612 return false; |
|
613 *out = left >> (right & 31); |
|
614 return true; |
|
615 } |
|
616 |
|
617 static MOZ_ALWAYS_INLINE bool |
|
618 UrshOperation(JSContext *cx, HandleValue lhs, HandleValue rhs, MutableHandleValue out) |
|
619 { |
|
620 uint32_t left; |
|
621 int32_t right; |
|
622 if (!ToUint32(cx, lhs, &left) || !ToInt32(cx, rhs, &right)) |
|
623 return false; |
|
624 left >>= right & 31; |
|
625 out.setNumber(uint32_t(left)); |
|
626 return true; |
|
627 } |
|
628 |
|
629 #undef RELATIONAL_OP |
|
630 |
|
631 inline JSFunction * |
|
632 ReportIfNotFunction(JSContext *cx, HandleValue v, MaybeConstruct construct = NO_CONSTRUCT) |
|
633 { |
|
634 if (v.isObject() && v.toObject().is<JSFunction>()) |
|
635 return &v.toObject().as<JSFunction>(); |
|
636 |
|
637 ReportIsNotFunction(cx, v, -1, construct); |
|
638 return nullptr; |
|
639 } |
|
640 |
|
641 /* |
|
642 * FastInvokeGuard is used to optimize calls to JS functions from natives written |
|
643 * in C++, for instance Array.map. If the callee is not Ion-compiled, this will |
|
644 * just call Invoke. If the callee has a valid IonScript, however, it will enter |
|
645 * Ion directly. |
|
646 */ |
|
647 class FastInvokeGuard |
|
648 { |
|
649 InvokeArgs args_; |
|
650 RootedFunction fun_; |
|
651 RootedScript script_; |
|
652 #ifdef JS_ION |
|
653 // Constructing an IonContext is pretty expensive due to the TLS access, |
|
654 // so only do this if we have to. |
|
655 bool useIon_; |
|
656 #endif |
|
657 |
|
658 public: |
|
659 FastInvokeGuard(JSContext *cx, const Value &fval) |
|
660 : args_(cx) |
|
661 , fun_(cx) |
|
662 , script_(cx) |
|
663 #ifdef JS_ION |
|
664 , useIon_(jit::IsIonEnabled(cx)) |
|
665 #endif |
|
666 { |
|
667 JS_ASSERT(!InParallelSection()); |
|
668 initFunction(fval); |
|
669 } |
|
670 |
|
671 void initFunction(const Value &fval) { |
|
672 if (fval.isObject() && fval.toObject().is<JSFunction>()) { |
|
673 JSFunction *fun = &fval.toObject().as<JSFunction>(); |
|
674 if (fun->isInterpreted()) |
|
675 fun_ = fun; |
|
676 } |
|
677 } |
|
678 |
|
679 InvokeArgs &args() { |
|
680 return args_; |
|
681 } |
|
682 |
|
683 bool invoke(JSContext *cx) { |
|
684 #ifdef JS_ION |
|
685 if (useIon_ && fun_) { |
|
686 if (!script_) { |
|
687 script_ = fun_->getOrCreateScript(cx); |
|
688 if (!script_) |
|
689 return false; |
|
690 } |
|
691 JS_ASSERT(fun_->nonLazyScript() == script_); |
|
692 |
|
693 jit::MethodStatus status = jit::CanEnterUsingFastInvoke(cx, script_, args_.length()); |
|
694 if (status == jit::Method_Error) |
|
695 return false; |
|
696 if (status == jit::Method_Compiled) { |
|
697 jit::IonExecStatus result = jit::FastInvoke(cx, fun_, args_); |
|
698 if (IsErrorStatus(result)) |
|
699 return false; |
|
700 |
|
701 JS_ASSERT(result == jit::IonExec_Ok); |
|
702 return true; |
|
703 } |
|
704 |
|
705 JS_ASSERT(status == jit::Method_Skipped); |
|
706 |
|
707 if (script_->canIonCompile()) { |
|
708 // This script is not yet hot. Since calling into Ion is much |
|
709 // faster here, bump the use count a bit to account for this. |
|
710 script_->incUseCount(5); |
|
711 } |
|
712 } |
|
713 #endif |
|
714 |
|
715 return Invoke(cx, args_); |
|
716 } |
|
717 |
|
718 private: |
|
719 FastInvokeGuard(const FastInvokeGuard& other) MOZ_DELETE; |
|
720 const FastInvokeGuard& operator=(const FastInvokeGuard& other) MOZ_DELETE; |
|
721 }; |
|
722 |
|
723 } /* namespace js */ |
|
724 |
|
725 #endif /* vm_Interpreter_inl_h */ |