js/src/vm/Interpreter-inl.h

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:f428f3cf2bb3
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 */

mercurial