Bug 877287 - Disable running JS code when ion compiling. r=bhackett a=akeybl
[gecko.git] / js / src / jsinterp.cpp
blob74a3819ef8beb63f8056929d4ca0aa3664722e5d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=99:
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 /*
9 * JavaScript bytecode interpreter.
12 #include "mozilla/DebugOnly.h"
13 #include "mozilla/FloatingPoint.h"
15 #include <stdio.h>
16 #include <string.h>
17 #include <math.h>
18 #include "jstypes.h"
19 #include "jsutil.h"
20 #include "jsprf.h"
21 #include "jsapi.h"
22 #include "jsarray.h"
23 #include "jsatom.h"
24 #include "jsbool.h"
25 #include "jscntxt.h"
26 #include "jsdate.h"
27 #include "jsversion.h"
28 #include "jsdbgapi.h"
29 #include "jsfun.h"
30 #include "jsgc.h"
31 #include "jsinterp.h"
32 #include "jsiter.h"
33 #include "jslibmath.h"
34 #include "jslock.h"
35 #include "jsnum.h"
36 #include "jsobj.h"
37 #include "jsopcode.h"
38 #include "jspropertycache.h"
39 #include "jsscript.h"
40 #include "jsstr.h"
42 #include "builtin/Eval.h"
43 #include "gc/Marking.h"
44 #include "ion/AsmJS.h"
45 #include "vm/Debugger.h"
46 #include "vm/Shape.h"
48 #ifdef JS_METHODJIT
49 #include "methodjit/MethodJIT.h"
50 #include "methodjit/Logging.h"
51 #endif
52 #include "ion/Ion.h"
54 #include "jsatominlines.h"
55 #include "jsboolinlines.h"
56 #include "jsinferinlines.h"
57 #include "jsinterpinlines.h"
58 #include "jsobjinlines.h"
59 #include "jsopcodeinlines.h"
60 #include "jsprobes.h"
61 #include "jspropertycacheinlines.h"
62 #include "jsscriptinlines.h"
63 #include "jstypedarrayinlines.h"
65 #include "builtin/Iterator-inl.h"
66 #include "vm/Shape-inl.h"
67 #include "vm/Stack-inl.h"
68 #include "vm/String-inl.h"
70 #include "jsautooplen.h"
72 #if defined(JS_METHODJIT) && defined(JS_MONOIC)
73 #include "methodjit/MonoIC.h"
74 #endif
76 #if JS_TRACE_LOGGING
77 #include "TraceLogging.h"
78 #endif
80 using namespace js;
81 using namespace js::gc;
82 using namespace js::types;
84 using mozilla::DebugOnly;
86 /* Some objects (e.g., With) delegate 'this' to another object. */
87 static inline JSObject *
88 CallThisObjectHook(JSContext *cx, HandleObject obj, Value *argv)
90 JSObject *thisp = JSObject::thisObject(cx, obj);
91 if (!thisp)
92 return NULL;
93 argv[-1].setObject(*thisp);
94 return thisp;
97 bool
98 js::BoxNonStrictThis(JSContext *cx, MutableHandleValue thisv, bool *modified)
101 * Check for SynthesizeFrame poisoning and fast constructors which
102 * didn't check their callee properly.
104 JS_ASSERT(!thisv.isMagic());
105 *modified = false;
107 if (thisv.isNullOrUndefined()) {
108 Rooted<GlobalObject*> global(cx, cx->global());
109 JSObject *thisp = JSObject::thisObject(cx, global);
110 if (!thisp)
111 return false;
112 thisv.set(ObjectValue(*thisp));
113 *modified = true;
114 return true;
117 if (!thisv.isObject()) {
118 if (!js_PrimitiveToObject(cx, thisv.address()))
119 return false;
120 *modified = true;
123 return true;
127 * ECMA requires "the global object", but in embeddings such as the browser,
128 * which have multiple top-level objects (windows, frames, etc. in the DOM),
129 * we prefer fun's parent. An example that causes this code to run:
131 * // in window w1
132 * function f() { return this }
133 * function g() { return f }
135 * // in window w2
136 * var h = w1.g()
137 * alert(h() == w1)
139 * The alert should display "true".
141 bool
142 js::BoxNonStrictThis(JSContext *cx, const CallReceiver &call)
145 * Check for SynthesizeFrame poisoning and fast constructors which
146 * didn't check their callee properly.
148 RootedValue thisv(cx, call.thisv());
149 JS_ASSERT(!thisv.isMagic());
151 #ifdef DEBUG
152 JSFunction *fun = call.callee().isFunction() ? call.callee().toFunction() : NULL;
153 JS_ASSERT_IF(fun && fun->isInterpreted(), !fun->strict());
154 #endif
156 bool modified;
157 if (!BoxNonStrictThis(cx, &thisv, &modified))
158 return false;
159 if (modified)
160 call.setThis(thisv);
162 return true;
165 #if JS_HAS_NO_SUCH_METHOD
167 const uint32_t JSSLOT_FOUND_FUNCTION = 0;
168 const uint32_t JSSLOT_SAVED_ID = 1;
170 Class js_NoSuchMethodClass = {
171 "NoSuchMethod",
172 JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS,
173 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
174 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,
178 * When JSOP_CALLPROP or JSOP_CALLELEM does not find the method property of
179 * the base object, we search for the __noSuchMethod__ method in the base.
180 * If it exists, we store the method and the property's id into an object of
181 * NoSuchMethod class and store this object into the callee's stack slot.
182 * Later, Invoke will recognise such an object and transfer control to
183 * NoSuchMethod that invokes the method like:
185 * this.__noSuchMethod__(id, args)
187 * where id is the name of the method that this invocation attempted to
188 * call by name, and args is an Array containing this invocation's actual
189 * parameters.
191 bool
192 js::OnUnknownMethod(JSContext *cx, HandleObject obj, Value idval_, MutableHandleValue vp)
194 RootedValue idval(cx, idval_);
196 RootedId id(cx, NameToId(cx->names().noSuchMethod));
197 RootedValue value(cx);
198 if (!GetMethod(cx, obj, id, 0, &value))
199 return false;
200 TypeScript::MonitorUnknown(cx);
202 if (value.get().isPrimitive()) {
203 vp.set(value);
204 } else {
205 JSObject *obj = NewObjectWithClassProto(cx, &js_NoSuchMethodClass, NULL, NULL);
206 if (!obj)
207 return false;
209 obj->setSlot(JSSLOT_FOUND_FUNCTION, value);
210 obj->setSlot(JSSLOT_SAVED_ID, idval);
211 vp.setObject(*obj);
213 return true;
216 static JSBool
217 NoSuchMethod(JSContext *cx, unsigned argc, Value *vp)
219 InvokeArgsGuard args;
220 if (!cx->stack.pushInvokeArgs(cx, 2, &args))
221 return JS_FALSE;
223 JS_ASSERT(vp[0].isObject());
224 JS_ASSERT(vp[1].isObject());
225 JSObject *obj = &vp[0].toObject();
226 JS_ASSERT(obj->getClass() == &js_NoSuchMethodClass);
228 args.setCallee(obj->getSlot(JSSLOT_FOUND_FUNCTION));
229 args.setThis(vp[1]);
230 args[0] = obj->getSlot(JSSLOT_SAVED_ID);
231 JSObject *argsobj = NewDenseCopiedArray(cx, argc, vp + 2);
232 if (!argsobj)
233 return JS_FALSE;
234 args[1].setObject(*argsobj);
235 JSBool ok = Invoke(cx, args);
236 vp[0] = args.rval();
237 return ok;
240 #endif /* JS_HAS_NO_SUCH_METHOD */
242 bool
243 js::ReportIsNotFunction(JSContext *cx, const Value &v, int numToSkip, MaybeConstruct construct)
245 unsigned error = construct ? JSMSG_NOT_CONSTRUCTOR : JSMSG_NOT_FUNCTION;
246 int spIndex = numToSkip >= 0 ? -(numToSkip + 1) : JSDVG_SEARCH_STACK;
248 RootedValue val(cx, v);
249 js_ReportValueError3(cx, error, spIndex, val, NullPtr(), NULL, NULL);
250 return false;
253 JSObject *
254 js::ValueToCallable(JSContext *cx, const Value &v, int numToSkip, MaybeConstruct construct)
256 if (v.isObject()) {
257 JSObject *callable = &v.toObject();
258 if (callable->isCallable())
259 return callable;
262 ReportIsNotFunction(cx, v, numToSkip, construct);
263 return NULL;
266 bool
267 js::RunScript(JSContext *cx, StackFrame *fp)
269 JS_ASSERT(fp == cx->fp());
270 RootedScript script(cx, fp->script());
272 JS_ASSERT_IF(!fp->isGeneratorFrame(), cx->regs().pc == script->code);
273 JS_ASSERT_IF(fp->isEvalFrame(), script->isActiveEval);
274 #ifdef JS_METHODJIT_SPEW
275 JMCheckLogging();
276 #endif
278 JS_CHECK_RECURSION(cx, return false);
280 // Check to see if useNewType flag should be set for this frame.
281 if (fp->isFunctionFrame() && fp->isConstructing() && !fp->isGeneratorFrame() &&
282 cx->typeInferenceEnabled())
284 StackIter iter(cx);
285 if (!iter.done()) {
286 ++iter;
287 if (iter.isScript()) {
288 RawScript script = iter.script();
289 jsbytecode *pc = iter.pc();
290 if (UseNewType(cx, script, pc))
291 fp->setUseNewType();
296 #ifdef DEBUG
297 struct CheckStackBalance {
298 JSContext *cx;
299 StackFrame *fp;
300 CheckStackBalance(JSContext *cx)
301 : cx(cx), fp(cx->fp())
303 ~CheckStackBalance() {
304 JS_ASSERT(fp == cx->fp());
306 } check(cx);
307 #endif
309 SPSEntryMarker marker(cx->runtime);
311 #ifdef JS_ION
312 if (ion::IsEnabled(cx)) {
313 ion::MethodStatus status = ion::CanEnter(cx, script, AbstractFramePtr(fp),
314 fp->isConstructing());
315 if (status == ion::Method_Error)
316 return false;
317 if (status == ion::Method_Compiled) {
318 ion::IonExecStatus status = ion::Cannon(cx, fp);
320 // Note that if we bailed out, new inline frames may have been
321 // pushed, so we interpret with the current fp.
322 if (status == ion::IonExec_Bailout)
323 return Interpret(cx, fp, JSINTERP_REJOIN);
325 return !IsErrorStatus(status);
328 #endif
330 #ifdef JS_METHODJIT
331 mjit::CompileStatus status;
332 status = mjit::CanMethodJIT(cx, script, script->code, fp->isConstructing(),
333 mjit::CompileRequest_Interpreter, fp);
334 if (status == mjit::Compile_Error)
335 return false;
337 if (status == mjit::Compile_Okay)
338 return mjit::JaegerStatusToSuccess(mjit::JaegerShot(cx, false));
339 #endif
341 return Interpret(cx, fp) != Interpret_Error;
345 * Find a function reference and its 'this' value implicit first parameter
346 * under argc arguments on cx's stack, and call the function. Push missing
347 * required arguments, allocate declared local variables, and pop everything
348 * when done. Then push the return value.
350 bool
351 js::InvokeKernel(JSContext *cx, CallArgs args, MaybeConstruct construct)
353 JS_ASSERT(args.length() <= StackSpace::ARGS_LENGTH_MAX);
354 JS_ASSERT(!cx->compartment->activeAnalysis);
356 // Never allow execution of JS code when compiling.
357 if (cx->compartment->activeAnalysis) {
358 JS_ReportError(cx, "Can't run scripts during analysis.");
359 return false;
362 /* We should never enter a new script while cx->iterValue is live. */
363 JS_ASSERT(cx->iterValue.isMagic(JS_NO_ITER_VALUE));
365 /* MaybeConstruct is a subset of InitialFrameFlags */
366 InitialFrameFlags initial = (InitialFrameFlags) construct;
368 if (args.calleev().isPrimitive())
369 return ReportIsNotFunction(cx, args.calleev().get(), args.length() + 1, construct);
371 JSObject &callee = args.callee();
372 Class *clasp = callee.getClass();
374 /* Invoke non-functions. */
375 if (JS_UNLIKELY(clasp != &FunctionClass)) {
376 #if JS_HAS_NO_SUCH_METHOD
377 if (JS_UNLIKELY(clasp == &js_NoSuchMethodClass))
378 return NoSuchMethod(cx, args.length(), args.base());
379 #endif
380 JS_ASSERT_IF(construct, !clasp->construct);
381 if (!clasp->call)
382 return ReportIsNotFunction(cx, args.calleev().get(), args.length() + 1, construct);
383 return CallJSNative(cx, clasp->call, args);
386 /* Invoke native functions. */
387 JSFunction *fun = callee.toFunction();
388 JS_ASSERT_IF(construct, !fun->isNativeConstructor());
389 if (fun->isNative())
390 return CallJSNative(cx, fun->native(), args);
392 if (!fun->getOrCreateScript(cx))
393 return false;
395 if (!TypeMonitorCall(cx, args, construct))
396 return false;
398 /* Get pointer to new frame/slots, prepare arguments. */
399 InvokeFrameGuard ifg;
400 if (!cx->stack.pushInvokeFrame(cx, args, initial, &ifg))
401 return false;
403 /* Run function until JSOP_STOP, JSOP_RETURN or error. */
404 JSBool ok = RunScript(cx, ifg.fp());
406 /* Propagate the return value out. */
407 args.rval().set(ifg.fp()->returnValue());
408 JS_ASSERT_IF(ok && construct, !args.rval().isPrimitive());
409 return ok;
412 bool
413 js::Invoke(JSContext *cx, const Value &thisv, const Value &fval, unsigned argc, Value *argv,
414 Value *rval)
416 InvokeArgsGuard args;
417 if (!cx->stack.pushInvokeArgs(cx, argc, &args))
418 return false;
420 args.setCallee(fval);
421 args.setThis(thisv);
422 PodCopy(args.array(), argv, argc);
424 if (args.thisv().isObject()) {
426 * We must call the thisObject hook in case we are not called from the
427 * interpreter, where a prior bytecode has computed an appropriate
428 * |this| already.
430 RootedObject thisObj(cx, &args.thisv().toObject());
431 JSObject *thisp = JSObject::thisObject(cx, thisObj);
432 if (!thisp)
433 return false;
434 args.setThis(ObjectValue(*thisp));
437 if (!Invoke(cx, args))
438 return false;
440 *rval = args.rval();
441 return true;
444 bool
445 js::InvokeConstructorKernel(JSContext *cx, CallArgs args)
447 JS_ASSERT(!FunctionClass.construct);
449 args.setThis(MagicValue(JS_IS_CONSTRUCTING));
451 if (!args.calleev().isObject())
452 return ReportIsNotFunction(cx, args.calleev().get(), args.length() + 1, CONSTRUCT);
454 JSObject &callee = args.callee();
455 if (callee.isFunction()) {
456 RootedFunction fun(cx, callee.toFunction());
458 if (fun->isNativeConstructor()) {
459 bool ok = CallJSNativeConstructor(cx, fun->native(), args);
460 return ok;
463 if (!fun->isInterpretedConstructor())
464 return ReportIsNotFunction(cx, args.calleev().get(), args.length() + 1, CONSTRUCT);
466 if (!InvokeKernel(cx, args, CONSTRUCT))
467 return false;
469 JS_ASSERT(args.rval().isObject());
470 return true;
473 Class *clasp = callee.getClass();
474 if (!clasp->construct)
475 return ReportIsNotFunction(cx, args.calleev().get(), args.length() + 1, CONSTRUCT);
477 return CallJSNativeConstructor(cx, clasp->construct, args);
480 bool
481 js::InvokeConstructor(JSContext *cx, const Value &fval, unsigned argc, Value *argv, Value *rval)
483 InvokeArgsGuard args;
484 if (!cx->stack.pushInvokeArgs(cx, argc, &args))
485 return false;
487 args.setCallee(fval);
488 args.setThis(MagicValue(JS_THIS_POISON));
489 PodCopy(args.array(), argv, argc);
491 if (!InvokeConstructor(cx, args))
492 return false;
494 *rval = args.rval();
495 return true;
498 bool
499 js::InvokeGetterOrSetter(JSContext *cx, JSObject *obj, const Value &fval, unsigned argc, Value *argv,
500 Value *rval)
503 * Invoke could result in another try to get or set the same id again, see
504 * bug 355497.
506 JS_CHECK_RECURSION(cx, return false);
508 return Invoke(cx, ObjectValue(*obj), fval, argc, argv, rval);
511 bool
512 js::ExecuteKernel(JSContext *cx, HandleScript script, JSObject &scopeChainArg, const Value &thisv,
513 ExecuteType type, AbstractFramePtr evalInFrame, Value *result)
515 RootedObject scopeChain(cx, &scopeChainArg);
517 JS_ASSERT_IF(evalInFrame, type == EXECUTE_DEBUG);
518 JS_ASSERT_IF(type == EXECUTE_GLOBAL, !scopeChain->isScope());
520 if (script->isEmpty()) {
521 if (result)
522 result->setUndefined();
523 return true;
526 ExecuteFrameGuard efg;
527 if (!cx->stack.pushExecuteFrame(cx, script, thisv, scopeChain, type, evalInFrame, &efg))
528 return false;
530 if (!script->ensureRanAnalysis(cx))
531 return false;
532 TypeScript::SetThis(cx, script, efg.fp()->thisValue());
534 Probes::startExecution(script);
535 bool ok = RunScript(cx, efg.fp());
536 Probes::stopExecution(script);
538 /* Propgate the return value out. */
539 if (result)
540 *result = efg.fp()->returnValue();
541 return ok;
544 bool
545 js::Execute(JSContext *cx, HandleScript script, JSObject &scopeChainArg, Value *rval)
547 /* The scope chain could be anything, so innerize just in case. */
548 RootedObject scopeChain(cx, &scopeChainArg);
549 scopeChain = GetInnerObject(cx, scopeChain);
550 if (!scopeChain)
551 return false;
553 /* Ensure the scope chain is all same-compartment and terminates in a global. */
554 #ifdef DEBUG
555 RawObject s = scopeChain;
556 do {
557 assertSameCompartment(cx, s);
558 JS_ASSERT_IF(!s->enclosingScope(), s->isGlobal());
559 } while ((s = s->enclosingScope()));
560 #endif
562 /* The VAROBJFIX option makes varObj == globalObj in global code. */
563 if (!cx->hasOption(JSOPTION_VAROBJFIX)) {
564 if (!scopeChain->setVarObj(cx))
565 return false;
568 /* Use the scope chain as 'this', modulo outerization. */
569 JSObject *thisObj = JSObject::thisObject(cx, scopeChain);
570 if (!thisObj)
571 return false;
572 Value thisv = ObjectValue(*thisObj);
574 return ExecuteKernel(cx, script, *scopeChain, thisv, EXECUTE_GLOBAL,
575 NullFramePtr() /* evalInFrame */, rval);
578 bool
579 js::HasInstance(JSContext *cx, HandleObject obj, HandleValue v, JSBool *bp)
581 Class *clasp = obj->getClass();
582 RootedValue local(cx, v);
583 if (clasp->hasInstance)
584 return clasp->hasInstance(cx, obj, &local, bp);
586 RootedValue val(cx, ObjectValue(*obj));
587 js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
588 JSDVG_SEARCH_STACK, val, NullPtr());
589 return JS_FALSE;
592 bool
593 js::LooselyEqual(JSContext *cx, const Value &lval, const Value &rval, bool *result)
595 if (SameType(lval, rval)) {
596 if (lval.isString()) {
597 JSString *l = lval.toString();
598 JSString *r = rval.toString();
599 return EqualStrings(cx, l, r, result);
602 if (lval.isDouble()) {
603 double l = lval.toDouble(), r = rval.toDouble();
604 *result = (l == r);
605 return true;
608 if (lval.isObject()) {
609 JSObject *l = &lval.toObject();
610 JSObject *r = &rval.toObject();
611 *result = l == r;
612 return true;
615 *result = lval.payloadAsRawUint32() == rval.payloadAsRawUint32();
616 return true;
619 if (lval.isNullOrUndefined()) {
620 *result = rval.isNullOrUndefined() ||
621 (rval.isObject() && EmulatesUndefined(&rval.toObject()));
622 return true;
625 if (rval.isNullOrUndefined()) {
626 *result = (lval.isObject() && EmulatesUndefined(&lval.toObject()));
627 return true;
630 RootedValue lvalue(cx, lval);
631 RootedValue rvalue(cx, rval);
633 if (!ToPrimitive(cx, &lvalue))
634 return false;
635 if (!ToPrimitive(cx, &rvalue))
636 return false;
638 if (lvalue.get().isString() && rvalue.get().isString()) {
639 JSString *l = lvalue.get().toString();
640 JSString *r = rvalue.get().toString();
641 return EqualStrings(cx, l, r, result);
644 double l, r;
645 if (!ToNumber(cx, lvalue, &l) || !ToNumber(cx, rvalue, &r))
646 return false;
647 *result = (l == r);
648 return true;
651 bool
652 js::StrictlyEqual(JSContext *cx, const Value &lref, const Value &rref, bool *equal)
654 Value lval = lref, rval = rref;
655 if (SameType(lval, rval)) {
656 if (lval.isString())
657 return EqualStrings(cx, lval.toString(), rval.toString(), equal);
658 if (lval.isDouble()) {
659 *equal = (lval.toDouble() == rval.toDouble());
660 return true;
662 if (lval.isObject()) {
663 *equal = lval.toObject() == rval.toObject();
664 return true;
666 if (lval.isUndefined()) {
667 *equal = true;
668 return true;
670 *equal = lval.payloadAsRawUint32() == rval.payloadAsRawUint32();
671 return true;
674 if (lval.isDouble() && rval.isInt32()) {
675 double ld = lval.toDouble();
676 double rd = rval.toInt32();
677 *equal = (ld == rd);
678 return true;
680 if (lval.isInt32() && rval.isDouble()) {
681 double ld = lval.toInt32();
682 double rd = rval.toDouble();
683 *equal = (ld == rd);
684 return true;
687 *equal = false;
688 return true;
691 static inline bool
692 IsNegativeZero(const Value &v)
694 return v.isDouble() && MOZ_DOUBLE_IS_NEGATIVE_ZERO(v.toDouble());
697 static inline bool
698 IsNaN(const Value &v)
700 return v.isDouble() && MOZ_DOUBLE_IS_NaN(v.toDouble());
703 bool
704 js::SameValue(JSContext *cx, const Value &v1, const Value &v2, bool *same)
706 if (IsNegativeZero(v1)) {
707 *same = IsNegativeZero(v2);
708 return true;
710 if (IsNegativeZero(v2)) {
711 *same = false;
712 return true;
714 if (IsNaN(v1) && IsNaN(v2)) {
715 *same = true;
716 return true;
718 return StrictlyEqual(cx, v1, v2, same);
721 JSType
722 js::TypeOfValue(JSContext *cx, const Value &vref)
724 Value v = vref;
725 if (v.isNumber())
726 return JSTYPE_NUMBER;
727 if (v.isString())
728 return JSTYPE_STRING;
729 if (v.isNull())
730 return JSTYPE_OBJECT;
731 if (v.isUndefined())
732 return JSTYPE_VOID;
733 if (v.isObject()) {
734 RootedObject obj(cx, &v.toObject());
735 return baseops::TypeOf(cx, obj);
737 JS_ASSERT(v.isBoolean());
738 return JSTYPE_BOOLEAN;
742 * Enter the new with scope using an object at sp[-1] and associate the depth
743 * of the with block with sp + stackIndex.
745 static bool
746 EnterWith(JSContext *cx, int stackIndex)
748 StackFrame *fp = cx->fp();
749 Value *sp = cx->regs().sp;
750 JS_ASSERT(stackIndex < 0);
751 JS_ASSERT(int(cx->regs().stackDepth()) + stackIndex >= 0);
753 RootedObject obj(cx);
754 if (sp[-1].isObject()) {
755 obj = &sp[-1].toObject();
756 } else {
757 obj = js_ValueToNonNullObject(cx, sp[-1]);
758 if (!obj)
759 return false;
760 sp[-1].setObject(*obj);
763 WithObject *withobj = WithObject::create(cx, obj, fp->scopeChain(),
764 cx->regs().stackDepth() + stackIndex);
765 if (!withobj)
766 return false;
768 fp->pushOnScopeChain(*withobj);
769 return true;
772 /* Unwind block and scope chains to match the given depth. */
773 void
774 js::UnwindScope(JSContext *cx, AbstractFramePtr frame, uint32_t stackDepth)
776 JS_ASSERT_IF(frame.isStackFrame(), cx->fp() == frame.asStackFrame());
777 JS_ASSERT_IF(frame.isStackFrame(), stackDepth <= cx->regs().stackDepth());
779 for (ScopeIter si(frame, cx); !si.done(); ++si) {
780 switch (si.type()) {
781 case ScopeIter::Block:
782 if (si.staticBlock().stackDepth() < stackDepth)
783 return;
784 frame.popBlock(cx);
785 break;
786 case ScopeIter::With:
787 if (si.scope().asWith().stackDepth() < stackDepth)
788 return;
789 frame.popWith(cx);
790 break;
791 case ScopeIter::Call:
792 case ScopeIter::StrictEvalScope:
793 break;
798 void
799 js::UnwindForUncatchableException(JSContext *cx, const FrameRegs &regs)
801 /* c.f. the regular (catchable) TryNoteIter loop in Interpret. */
802 for (TryNoteIter tni(cx, regs); !tni.done(); ++tni) {
803 JSTryNote *tn = *tni;
804 if (tn->kind == JSTRY_ITER) {
805 Value *sp = regs.spForStackDepth(tn->stackDepth);
806 UnwindIteratorForUncatchableException(cx, &sp[-1].toObject());
811 TryNoteIter::TryNoteIter(JSContext *cx, const FrameRegs &regs)
812 : regs(regs),
813 script(cx, regs.fp()->script()),
814 pcOffset(regs.pc - script->main())
816 if (script->hasTrynotes()) {
817 tn = script->trynotes()->vector;
818 tnEnd = tn + script->trynotes()->length;
819 } else {
820 tn = tnEnd = NULL;
822 settle();
825 void
826 TryNoteIter::operator++()
828 ++tn;
829 settle();
832 bool
833 TryNoteIter::done() const
835 return tn == tnEnd;
838 void
839 TryNoteIter::settle()
841 for (; tn != tnEnd; ++tn) {
842 /* If pc is out of range, try the next one. */
843 if (pcOffset - tn->start >= tn->length)
844 continue;
847 * We have a note that covers the exception pc but we must check
848 * whether the interpreter has already executed the corresponding
849 * handler. This is possible when the executed bytecode implements
850 * break or return from inside a for-in loop.
852 * In this case the emitter generates additional [enditer] and [gosub]
853 * opcodes to close all outstanding iterators and execute the finally
854 * blocks. If such an [enditer] throws an exception, its pc can still
855 * be inside several nested for-in loops and try-finally statements
856 * even if we have already closed the corresponding iterators and
857 * invoked the finally blocks.
859 * To address this, we make [enditer] always decrease the stack even
860 * when its implementation throws an exception. Thus already executed
861 * [enditer] and [gosub] opcodes will have try notes with the stack
862 * depth exceeding the current one and this condition is what we use to
863 * filter them out.
865 if (tn->stackDepth <= regs.stackDepth())
866 break;
870 #define PUSH_COPY(v) do { *regs.sp++ = v; assertSameCompartmentDebugOnly(cx, regs.sp[-1]); } while (0)
871 #define PUSH_COPY_SKIP_CHECK(v) *regs.sp++ = v
872 #define PUSH_NULL() regs.sp++->setNull()
873 #define PUSH_UNDEFINED() regs.sp++->setUndefined()
874 #define PUSH_BOOLEAN(b) regs.sp++->setBoolean(b)
875 #define PUSH_DOUBLE(d) regs.sp++->setDouble(d)
876 #define PUSH_INT32(i) regs.sp++->setInt32(i)
877 #define PUSH_STRING(s) do { regs.sp++->setString(s); assertSameCompartmentDebugOnly(cx, regs.sp[-1]); } while (0)
878 #define PUSH_OBJECT(obj) do { regs.sp++->setObject(obj); assertSameCompartmentDebugOnly(cx, regs.sp[-1]); } while (0)
879 #define PUSH_OBJECT_OR_NULL(obj) do { regs.sp++->setObjectOrNull(obj); assertSameCompartmentDebugOnly(cx, regs.sp[-1]); } while (0)
880 #define PUSH_HOLE() regs.sp++->setMagic(JS_ELEMENTS_HOLE)
881 #define POP_COPY_TO(v) v = *--regs.sp
882 #define POP_RETURN_VALUE() regs.fp()->setReturnValue(*--regs.sp)
884 #define FETCH_OBJECT(cx, n, obj) \
885 JS_BEGIN_MACRO \
886 HandleValue val = HandleValue::fromMarkedLocation(&regs.sp[n]); \
887 obj = ToObject(cx, (val)); \
888 if (!obj) \
889 goto error; \
890 JS_END_MACRO
892 template<typename T>
893 class GenericInterruptEnabler : public InterpreterFrames::InterruptEnablerBase {
894 public:
895 GenericInterruptEnabler(T *variable, T value) : variable(variable), value(value) { }
896 void enable() const { *variable = value; }
898 private:
899 T *variable;
900 T value;
903 inline InterpreterFrames::InterpreterFrames(JSContext *cx, FrameRegs *regs,
904 const InterruptEnablerBase &enabler)
905 : context(cx), regs(regs), enabler(enabler)
907 older = cx->runtime->interpreterFrames;
908 cx->runtime->interpreterFrames = this;
911 inline InterpreterFrames::~InterpreterFrames()
913 context->runtime->interpreterFrames = older;
916 #if defined(DEBUG) && !defined(JS_THREADSAFE) && !defined(JSGC_ROOT_ANALYSIS)
917 void
918 js::AssertValidPropertyCacheHit(JSContext *cx, JSObject *start,
919 JSObject *found, PropertyCacheEntry *entry)
921 jsbytecode *pc;
922 JSScript *script = cx->stack.currentScript(&pc);
924 uint64_t sample = cx->runtime->gcNumber;
926 PropertyName *name = GetNameFromBytecode(cx, script, pc, JSOp(*pc));
927 JSObject *pobj;
928 Shape *prop;
929 if (baseops::LookupProperty<NoGC>(cx, start, NameToId(name), &pobj, &prop)) {
930 JS_ASSERT(prop);
931 JS_ASSERT(pobj == found);
932 JS_ASSERT(entry->prop == prop);
935 JS_ASSERT(cx->runtime->gcNumber == sample);
937 #endif /* DEBUG && !JS_THREADSAFE */
940 * Ensure that the intrepreter switch can close call-bytecode cases in the
941 * same way as non-call bytecodes.
943 JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH);
944 JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH);
945 JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH);
948 * Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but
949 * remain distinct for the decompiler.
951 JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH);
953 /* See TRY_BRANCH_AFTER_COND. */
954 JS_STATIC_ASSERT(JSOP_IFNE_LENGTH == JSOP_IFEQ_LENGTH);
955 JS_STATIC_ASSERT(JSOP_IFNE == JSOP_IFEQ + 1);
957 /* For the fastest case inder JSOP_INCNAME, etc. */
958 JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_DECNAME_LENGTH);
959 JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEINC_LENGTH);
960 JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEDEC_LENGTH);
963 * Inline fast paths for iteration. js_IteratorMore and js_IteratorNext handle
964 * all cases, but we inline the most frequently taken paths here.
966 static inline bool
967 IteratorMore(JSContext *cx, JSObject *iterobj, bool *cond, MutableHandleValue rval)
969 if (iterobj->isPropertyIterator()) {
970 NativeIterator *ni = iterobj->asPropertyIterator().getNativeIterator();
971 if (ni->isKeyIter()) {
972 *cond = (ni->props_cursor < ni->props_end);
973 return true;
976 Rooted<JSObject*> iobj(cx, iterobj);
977 if (!js_IteratorMore(cx, iobj, rval))
978 return false;
979 *cond = rval.isTrue();
980 return true;
983 static inline bool
984 IteratorNext(JSContext *cx, HandleObject iterobj, MutableHandleValue rval)
986 if (iterobj->isPropertyIterator()) {
987 NativeIterator *ni = iterobj->asPropertyIterator().getNativeIterator();
988 if (ni->isKeyIter()) {
989 JS_ASSERT(ni->props_cursor < ni->props_end);
990 rval.setString(*ni->current());
991 ni->incCursor();
992 return true;
995 return js_IteratorNext(cx, iterobj, rval);
999 * For bytecodes which push values and then fall through, make sure the
1000 * types of the pushed values are consistent with type inference information.
1002 static inline void
1003 TypeCheckNextBytecode(JSContext *cx, HandleScript script, unsigned n, const FrameRegs &regs)
1005 #ifdef DEBUG
1006 if (cx->typeInferenceEnabled() && n == GetBytecodeLength(regs.pc))
1007 TypeScript::CheckBytecode(cx, script, regs.pc, regs.sp);
1008 #endif
1011 JS_NEVER_INLINE InterpretStatus
1012 js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
1014 JSAutoResolveFlags rf(cx, RESOLVE_INFER);
1016 if (interpMode == JSINTERP_NORMAL)
1017 gc::MaybeVerifyBarriers(cx, true);
1019 JS_ASSERT(!cx->compartment->activeAnalysis);
1021 // Never allow execution of JS code when compiling.
1022 if (cx->compartment->activeAnalysis) {
1023 JS_ReportError(cx, "Can't run scripts during analysis.");
1024 return Interpret_Error;
1027 #define CHECK_PCCOUNT_INTERRUPTS() JS_ASSERT_IF(script->hasScriptCounts, switchMask == -1)
1029 register int switchMask = 0;
1030 int switchOp;
1031 typedef GenericInterruptEnabler<int> InterruptEnabler;
1032 InterruptEnabler interrupts(&switchMask, -1);
1034 # define DO_OP() goto do_op
1035 # define DO_NEXT_OP(n) JS_BEGIN_MACRO \
1036 JS_ASSERT((n) == len); \
1037 goto advance_pc; \
1038 JS_END_MACRO
1040 # define BEGIN_CASE(OP) case OP:
1041 # define END_CASE(OP) END_CASE_LEN(OP##_LENGTH)
1042 # define END_CASE_LEN(n) END_CASE_LENX(n)
1043 # define END_CASE_LENX(n) END_CASE_LEN##n
1046 * To share the code for all len == 1 cases we use the specialized label with
1047 * code that falls through to advance_pc: .
1049 # define END_CASE_LEN1 goto advance_pc_by_one;
1050 # define END_CASE_LEN2 len = 2; goto advance_pc;
1051 # define END_CASE_LEN3 len = 3; goto advance_pc;
1052 # define END_CASE_LEN4 len = 4; goto advance_pc;
1053 # define END_CASE_LEN5 len = 5; goto advance_pc;
1054 # define END_CASE_LEN6 len = 6; goto advance_pc;
1055 # define END_CASE_LEN7 len = 7; goto advance_pc;
1056 # define END_CASE_LEN8 len = 8; goto advance_pc;
1057 # define END_CASE_LEN9 len = 9; goto advance_pc;
1058 # define END_CASE_LEN10 len = 10; goto advance_pc;
1059 # define END_CASE_LEN11 len = 11; goto advance_pc;
1060 # define END_CASE_LEN12 len = 12; goto advance_pc;
1061 # define END_VARLEN_CASE goto advance_pc;
1062 # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP)
1063 # define END_EMPTY_CASES goto advance_pc_by_one;
1065 #define LOAD_DOUBLE(PCOFF, dbl) \
1066 (dbl = script->getConst(GET_UINT32_INDEX(regs.pc + (PCOFF))).toDouble())
1068 #ifdef JS_METHODJIT
1070 #define CHECK_PARTIAL_METHODJIT(status) \
1071 JS_BEGIN_MACRO \
1072 switch (status) { \
1073 case mjit::Jaeger_UnfinishedAtTrap: \
1074 interpMode = JSINTERP_SKIP_TRAP; \
1075 /* FALLTHROUGH */ \
1076 case mjit::Jaeger_Unfinished: \
1077 op = (JSOp) *regs.pc; \
1078 SET_SCRIPT(regs.fp()->script()); \
1079 if (cx->isExceptionPending()) \
1080 goto error; \
1081 DO_OP(); \
1082 default:; \
1084 JS_END_MACRO
1085 #endif
1088 * Prepare to call a user-supplied branch handler, and abort the script
1089 * if it returns false.
1091 #define CHECK_BRANCH() \
1092 JS_BEGIN_MACRO \
1093 if (cx->runtime->interrupt && !js_HandleExecutionInterrupt(cx)) \
1094 goto error; \
1095 JS_END_MACRO
1097 #define BRANCH(n) \
1098 JS_BEGIN_MACRO \
1099 regs.pc += (n); \
1100 op = (JSOp) *regs.pc; \
1101 if ((n) <= 0) \
1102 goto check_backedge; \
1103 DO_OP(); \
1104 JS_END_MACRO
1106 #define SET_SCRIPT(s) \
1107 JS_BEGIN_MACRO \
1108 script = (s); \
1109 if (script->hasAnyBreakpointsOrStepMode() || script->hasScriptCounts) \
1110 interrupts.enable(); \
1111 JS_ASSERT_IF(interpMode == JSINTERP_SKIP_TRAP, \
1112 script->hasAnyBreakpointsOrStepMode()); \
1113 JS_END_MACRO
1115 /* Repoint cx->regs to a local variable for faster access. */
1116 FrameRegs regs = cx->regs();
1117 PreserveRegsGuard interpGuard(cx, regs);
1120 * Help Debugger find frames running scripts that it has put in
1121 * single-step mode.
1123 InterpreterFrames interpreterFrame(cx, &regs, interrupts);
1125 /* Copy in hot values that change infrequently. */
1126 JSRuntime *const rt = cx->runtime;
1127 RootedScript script(cx);
1128 SET_SCRIPT(regs.fp()->script());
1130 #ifdef JS_METHODJIT
1131 /* Reset the loop count on the script we're entering. */
1132 script->resetLoopCount();
1133 #endif
1135 #if JS_TRACE_LOGGING
1136 AutoTraceLog logger(TraceLogging::defaultLogger(),
1137 TraceLogging::INTERPRETER_START,
1138 TraceLogging::INTERPRETER_STOP,
1139 script);
1140 #endif
1143 * Pool of rooters for use in this interpreter frame. References to these
1144 * are used for local variables within interpreter cases. This avoids
1145 * creating new rooters each time an interpreter case is entered, and also
1146 * correctness pitfalls due to incorrect compilation of destructor calls
1147 * around computed gotos.
1149 RootedValue rootValue0(cx), rootValue1(cx);
1150 RootedString rootString0(cx), rootString1(cx);
1151 RootedObject rootObject0(cx), rootObject1(cx), rootObject2(cx);
1152 RootedFunction rootFunction0(cx);
1153 RootedTypeObject rootType0(cx);
1154 RootedPropertyName rootName0(cx);
1155 RootedId rootId0(cx);
1156 RootedShape rootShape0(cx);
1157 RootedScript rootScript0(cx);
1158 DebugOnly<uint32_t> blockDepth;
1160 if (!entryFrame)
1161 entryFrame = regs.fp();
1163 #if JS_HAS_GENERATORS
1164 if (JS_UNLIKELY(regs.fp()->isGeneratorFrame())) {
1165 JS_ASSERT(size_t(regs.pc - script->code) <= script->length);
1166 JS_ASSERT(regs.stackDepth() <= script->nslots);
1169 * To support generator_throw and to catch ignored exceptions,
1170 * fail if cx->isExceptionPending() is true.
1172 if (cx->isExceptionPending()) {
1173 Probes::enterScript(cx, script, script->function(), regs.fp());
1174 goto error;
1177 #endif
1179 /* State communicated between non-local jumps: */
1180 bool interpReturnOK;
1182 /* Don't call the script prologue if executing between Method and Trace JIT. */
1183 if (interpMode == JSINTERP_NORMAL) {
1184 StackFrame *fp = regs.fp();
1185 if (!fp->isGeneratorFrame()) {
1186 if (!fp->prologue(cx))
1187 goto error;
1188 } else {
1189 Probes::enterScript(cx, script, script->function(), fp);
1191 if (cx->compartment->debugMode()) {
1192 JSTrapStatus status = ScriptDebugPrologue(cx, fp);
1193 switch (status) {
1194 case JSTRAP_CONTINUE:
1195 break;
1196 case JSTRAP_RETURN:
1197 interpReturnOK = true;
1198 goto forced_return;
1199 case JSTRAP_THROW:
1200 case JSTRAP_ERROR:
1201 goto error;
1202 default:
1203 JS_NOT_REACHED("bad ScriptDebugPrologue status");
1208 /* The REJOIN mode acts like the normal mode, except the prologue is skipped. */
1209 if (interpMode == JSINTERP_REJOIN)
1210 interpMode = JSINTERP_NORMAL;
1213 * The RETHROW mode acts like a bailout mode, except that it resume an
1214 * exception instead of resuming the script.
1216 if (interpMode == JSINTERP_RETHROW)
1217 goto error;
1220 * It is important that "op" be initialized before calling DO_OP because
1221 * it is possible for "op" to be specially assigned during the normal
1222 * processing of an opcode while looping. We rely on DO_NEXT_OP to manage
1223 * "op" correctly in all other cases.
1225 JSOp op;
1226 int32_t len;
1227 len = 0;
1229 if (rt->profilingScripts || cx->runtime->debugHooks.interruptHook)
1230 interrupts.enable();
1232 DO_NEXT_OP(len);
1234 for (;;) {
1235 advance_pc_by_one:
1236 JS_ASSERT(js_CodeSpec[op].length == 1);
1237 len = 1;
1238 advance_pc:
1239 TypeCheckNextBytecode(cx, script, len, regs);
1240 js::gc::MaybeVerifyBarriers(cx);
1241 regs.pc += len;
1242 op = (JSOp) *regs.pc;
1244 do_op:
1245 CHECK_PCCOUNT_INTERRUPTS();
1246 switchOp = int(op) | switchMask;
1247 do_switch:
1248 switch (switchOp) {
1250 case -1:
1251 JS_ASSERT(switchMask == -1);
1253 bool moreInterrupts = false;
1255 if (cx->runtime->profilingScripts) {
1256 if (!script->hasScriptCounts)
1257 script->initScriptCounts(cx);
1258 moreInterrupts = true;
1261 if (script->hasScriptCounts) {
1262 PCCounts counts = script->getPCCounts(regs.pc);
1263 counts.get(PCCounts::BASE_INTERP)++;
1264 moreInterrupts = true;
1267 JSInterruptHook hook = cx->runtime->debugHooks.interruptHook;
1268 if (hook || script->stepModeEnabled()) {
1269 RootedValue rval(cx);
1270 JSTrapStatus status = JSTRAP_CONTINUE;
1271 if (hook)
1272 status = hook(cx, script, regs.pc, rval.address(), cx->runtime->debugHooks.interruptHookData);
1273 if (status == JSTRAP_CONTINUE && script->stepModeEnabled())
1274 status = Debugger::onSingleStep(cx, &rval);
1275 switch (status) {
1276 case JSTRAP_ERROR:
1277 goto error;
1278 case JSTRAP_CONTINUE:
1279 break;
1280 case JSTRAP_RETURN:
1281 regs.fp()->setReturnValue(rval);
1282 interpReturnOK = true;
1283 goto forced_return;
1284 case JSTRAP_THROW:
1285 cx->setPendingException(rval);
1286 goto error;
1287 default:;
1289 moreInterrupts = true;
1292 if (script->hasAnyBreakpointsOrStepMode())
1293 moreInterrupts = true;
1295 if (script->hasBreakpointsAt(regs.pc) && interpMode != JSINTERP_SKIP_TRAP) {
1296 RootedValue rval(cx);
1297 JSTrapStatus status = Debugger::onTrap(cx, &rval);
1298 switch (status) {
1299 case JSTRAP_ERROR:
1300 goto error;
1301 case JSTRAP_RETURN:
1302 regs.fp()->setReturnValue(rval);
1303 interpReturnOK = true;
1304 goto forced_return;
1305 case JSTRAP_THROW:
1306 cx->setPendingException(rval);
1307 goto error;
1308 default:
1309 break;
1311 JS_ASSERT(status == JSTRAP_CONTINUE);
1312 JS_ASSERT(rval.isInt32() && rval.toInt32() == op);
1315 interpMode = JSINTERP_NORMAL;
1317 switchMask = moreInterrupts ? -1 : 0;
1318 switchOp = int(op);
1319 goto do_switch;
1322 /* No-ops for ease of decompilation. */
1323 ADD_EMPTY_CASE(JSOP_NOP)
1324 ADD_EMPTY_CASE(JSOP_UNUSED71)
1325 ADD_EMPTY_CASE(JSOP_UNUSED132)
1326 ADD_EMPTY_CASE(JSOP_UNUSED148)
1327 ADD_EMPTY_CASE(JSOP_UNUSED161)
1328 ADD_EMPTY_CASE(JSOP_UNUSED162)
1329 ADD_EMPTY_CASE(JSOP_UNUSED163)
1330 ADD_EMPTY_CASE(JSOP_UNUSED164)
1331 ADD_EMPTY_CASE(JSOP_UNUSED165)
1332 ADD_EMPTY_CASE(JSOP_UNUSED166)
1333 ADD_EMPTY_CASE(JSOP_UNUSED167)
1334 ADD_EMPTY_CASE(JSOP_UNUSED168)
1335 ADD_EMPTY_CASE(JSOP_UNUSED169)
1336 ADD_EMPTY_CASE(JSOP_UNUSED170)
1337 ADD_EMPTY_CASE(JSOP_UNUSED171)
1338 ADD_EMPTY_CASE(JSOP_UNUSED172)
1339 ADD_EMPTY_CASE(JSOP_UNUSED173)
1340 ADD_EMPTY_CASE(JSOP_UNUSED174)
1341 ADD_EMPTY_CASE(JSOP_UNUSED175)
1342 ADD_EMPTY_CASE(JSOP_UNUSED176)
1343 ADD_EMPTY_CASE(JSOP_UNUSED177)
1344 ADD_EMPTY_CASE(JSOP_UNUSED178)
1345 ADD_EMPTY_CASE(JSOP_UNUSED179)
1346 ADD_EMPTY_CASE(JSOP_UNUSED180)
1347 ADD_EMPTY_CASE(JSOP_UNUSED181)
1348 ADD_EMPTY_CASE(JSOP_UNUSED182)
1349 ADD_EMPTY_CASE(JSOP_UNUSED183)
1350 ADD_EMPTY_CASE(JSOP_UNUSED188)
1351 ADD_EMPTY_CASE(JSOP_UNUSED189)
1352 ADD_EMPTY_CASE(JSOP_UNUSED190)
1353 ADD_EMPTY_CASE(JSOP_UNUSED200)
1354 ADD_EMPTY_CASE(JSOP_UNUSED201)
1355 ADD_EMPTY_CASE(JSOP_UNUSED208)
1356 ADD_EMPTY_CASE(JSOP_UNUSED209)
1357 ADD_EMPTY_CASE(JSOP_UNUSED210)
1358 ADD_EMPTY_CASE(JSOP_UNUSED219)
1359 ADD_EMPTY_CASE(JSOP_UNUSED220)
1360 ADD_EMPTY_CASE(JSOP_UNUSED221)
1361 ADD_EMPTY_CASE(JSOP_UNUSED222)
1362 ADD_EMPTY_CASE(JSOP_UNUSED223)
1363 ADD_EMPTY_CASE(JSOP_CONDSWITCH)
1364 ADD_EMPTY_CASE(JSOP_TRY)
1365 END_EMPTY_CASES
1367 BEGIN_CASE(JSOP_LOOPHEAD)
1369 #ifdef JS_METHODJIT
1370 script->incrLoopCount();
1371 #endif
1372 END_CASE(JSOP_LOOPHEAD)
1374 BEGIN_CASE(JSOP_LABEL)
1375 END_CASE(JSOP_LABEL)
1377 check_backedge:
1379 CHECK_BRANCH();
1380 if (op != JSOP_LOOPHEAD)
1381 DO_OP();
1383 #ifdef JS_METHODJIT
1384 // Attempt on-stack replacement with JaegerMonkey code, which is keyed to
1385 // the interpreter state at the JSOP_LOOPHEAD at the start of the loop.
1386 // Unlike IonMonkey, this requires two different code fragments to perform
1387 // hoisting.
1388 mjit::CompileStatus status =
1389 mjit::CanMethodJIT(cx, script, regs.pc, regs.fp()->isConstructing(),
1390 mjit::CompileRequest_Interpreter, regs.fp());
1391 if (status == mjit::Compile_Error)
1392 goto error;
1393 if (status == mjit::Compile_Okay) {
1394 void *ncode =
1395 script->nativeCodeForPC(regs.fp()->isConstructing(), regs.pc);
1396 JS_ASSERT(ncode);
1397 mjit::JaegerStatus status = mjit::JaegerShotAtSafePoint(cx, ncode, true);
1398 if (status == mjit::Jaeger_ThrowBeforeEnter)
1399 goto error;
1400 CHECK_PARTIAL_METHODJIT(status);
1401 interpReturnOK = (status == mjit::Jaeger_Returned);
1402 if (entryFrame != regs.fp())
1403 goto jit_return;
1404 regs.fp()->setFinishedInInterpreter();
1405 goto leave_on_safe_point;
1407 #endif /* JS_METHODJIT */
1409 DO_OP();
1412 BEGIN_CASE(JSOP_LOOPENTRY)
1414 #ifdef JS_ION
1415 // Attempt on-stack replacement with Ion code. IonMonkey OSR takes place at
1416 // the point of the initial loop entry, to consolidate hoisted code between
1417 // entry points.
1418 if (ion::IsEnabled(cx)) {
1419 ion::MethodStatus status =
1420 ion::CanEnterAtBranch(cx, script, AbstractFramePtr(regs.fp()), regs.pc,
1421 regs.fp()->isConstructing());
1422 if (status == ion::Method_Error)
1423 goto error;
1424 if (status == ion::Method_Compiled) {
1425 ion::IonExecStatus maybeOsr = ion::SideCannon(cx, regs.fp(), regs.pc);
1426 if (maybeOsr == ion::IonExec_Bailout) {
1427 // We hit a deoptimization path in the first Ion frame, so now
1428 // we've just replaced the entire Ion activation.
1429 SET_SCRIPT(regs.fp()->script());
1430 op = JSOp(*regs.pc);
1431 DO_OP();
1434 // We failed to call into Ion at all, so treat as an error.
1435 if (maybeOsr == ion::IonExec_Aborted)
1436 goto error;
1438 interpReturnOK = (maybeOsr == ion::IonExec_Ok);
1440 if (entryFrame != regs.fp())
1441 goto jit_return;
1443 regs.fp()->setFinishedInInterpreter();
1444 goto leave_on_safe_point;
1447 #endif /* JS_ION */
1449 END_CASE(JSOP_LOOPENTRY)
1451 BEGIN_CASE(JSOP_NOTEARG)
1452 END_CASE(JSOP_NOTEARG)
1454 /* ADD_EMPTY_CASE is not used here as JSOP_LINENO_LENGTH == 3. */
1455 BEGIN_CASE(JSOP_LINENO)
1456 END_CASE(JSOP_LINENO)
1458 BEGIN_CASE(JSOP_UNDEFINED)
1459 PUSH_UNDEFINED();
1460 END_CASE(JSOP_UNDEFINED)
1462 BEGIN_CASE(JSOP_POP)
1463 regs.sp--;
1464 END_CASE(JSOP_POP)
1466 BEGIN_CASE(JSOP_POPN)
1467 JS_ASSERT(GET_UINT16(regs.pc) <= regs.stackDepth());
1468 regs.sp -= GET_UINT16(regs.pc);
1469 #ifdef DEBUG
1470 if (StaticBlockObject *block = regs.fp()->maybeBlockChain())
1471 JS_ASSERT(regs.stackDepth() >= block->stackDepth() + block->slotCount());
1472 #endif
1473 END_CASE(JSOP_POPN)
1475 BEGIN_CASE(JSOP_SETRVAL)
1476 BEGIN_CASE(JSOP_POPV)
1477 POP_RETURN_VALUE();
1478 END_CASE(JSOP_POPV)
1480 BEGIN_CASE(JSOP_ENTERWITH)
1481 if (!EnterWith(cx, -1))
1482 goto error;
1485 * We must ensure that different "with" blocks have different stack depth
1486 * associated with them. This allows the try handler search to properly
1487 * recover the scope chain. Thus we must keep the stack at least at the
1488 * current level.
1490 * We set sp[-1] to the current "with" object to help asserting the
1491 * enter/leave balance in [leavewith].
1493 regs.sp[-1].setObject(*regs.fp()->scopeChain());
1494 END_CASE(JSOP_ENTERWITH)
1496 BEGIN_CASE(JSOP_LEAVEWITH)
1497 JS_ASSERT(regs.sp[-1].toObject() == *regs.fp()->scopeChain());
1498 regs.fp()->popWith(cx);
1499 regs.sp--;
1500 END_CASE(JSOP_LEAVEWITH)
1502 BEGIN_CASE(JSOP_RETURN)
1503 POP_RETURN_VALUE();
1504 /* FALL THROUGH */
1506 BEGIN_CASE(JSOP_RETRVAL) /* fp return value already set */
1507 BEGIN_CASE(JSOP_STOP)
1510 * When the inlined frame exits with an exception or an error, ok will be
1511 * false after the inline_return label.
1513 CHECK_BRANCH();
1515 interpReturnOK = true;
1516 if (entryFrame != regs.fp())
1517 inline_return:
1519 if (cx->compartment->debugMode())
1520 interpReturnOK = ScriptDebugEpilogue(cx, regs.fp(), interpReturnOK);
1522 if (!regs.fp()->isYielding())
1523 regs.fp()->epilogue(cx);
1524 else
1525 Probes::exitScript(cx, script, script->function(), regs.fp());
1527 /* The JIT inlines the epilogue. */
1528 #ifdef JS_METHODJIT
1529 jit_return:
1530 #endif
1532 /* The results of lowered call/apply frames need to be shifted. */
1533 bool shiftResult = regs.fp()->loweredCallOrApply();
1535 cx->stack.popInlineFrame(regs);
1536 SET_SCRIPT(regs.fp()->script());
1538 JS_ASSERT(js_CodeSpec[*regs.pc].format & JOF_INVOKE);
1540 /* Resume execution in the calling frame. */
1541 if (JS_LIKELY(interpReturnOK)) {
1542 TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
1544 if (shiftResult) {
1545 regs.sp[-2] = regs.sp[-1];
1546 regs.sp--;
1549 len = JSOP_CALL_LENGTH;
1550 DO_NEXT_OP(len);
1553 /* Increment pc so that |sp - fp->slots == ReconstructStackDepth(pc)|. */
1554 regs.pc += JSOP_CALL_LENGTH;
1555 goto error;
1556 } else {
1557 JS_ASSERT(regs.stackDepth() == 0);
1559 interpReturnOK = true;
1560 goto exit;
1563 BEGIN_CASE(JSOP_DEFAULT)
1564 regs.sp--;
1565 /* FALL THROUGH */
1566 BEGIN_CASE(JSOP_GOTO)
1568 len = GET_JUMP_OFFSET(regs.pc);
1569 BRANCH(len);
1571 END_CASE(JSOP_GOTO)
1573 BEGIN_CASE(JSOP_IFEQ)
1575 bool cond = ToBoolean(regs.sp[-1]);
1576 regs.sp--;
1577 if (cond == false) {
1578 len = GET_JUMP_OFFSET(regs.pc);
1579 BRANCH(len);
1582 END_CASE(JSOP_IFEQ)
1584 BEGIN_CASE(JSOP_IFNE)
1586 bool cond = ToBoolean(regs.sp[-1]);
1587 regs.sp--;
1588 if (cond != false) {
1589 len = GET_JUMP_OFFSET(regs.pc);
1590 BRANCH(len);
1593 END_CASE(JSOP_IFNE)
1595 BEGIN_CASE(JSOP_OR)
1597 bool cond = ToBoolean(regs.sp[-1]);
1598 if (cond == true) {
1599 len = GET_JUMP_OFFSET(regs.pc);
1600 DO_NEXT_OP(len);
1603 END_CASE(JSOP_OR)
1605 BEGIN_CASE(JSOP_AND)
1607 bool cond = ToBoolean(regs.sp[-1]);
1608 if (cond == false) {
1609 len = GET_JUMP_OFFSET(regs.pc);
1610 DO_NEXT_OP(len);
1613 END_CASE(JSOP_AND)
1615 #define FETCH_ELEMENT_ID(obj, n, id) \
1616 JS_BEGIN_MACRO \
1617 const Value &idval_ = regs.sp[n]; \
1618 if (!ValueToId<CanGC>(cx, obj, idval_, &id)) \
1619 goto error; \
1620 JS_END_MACRO
1622 #define TRY_BRANCH_AFTER_COND(cond,spdec) \
1623 JS_BEGIN_MACRO \
1624 JS_ASSERT(js_CodeSpec[op].length == 1); \
1625 unsigned diff_ = (unsigned) GET_UINT8(regs.pc) - (unsigned) JSOP_IFEQ; \
1626 if (diff_ <= 1) { \
1627 regs.sp -= spdec; \
1628 if (cond == (diff_ != 0)) { \
1629 ++regs.pc; \
1630 len = GET_JUMP_OFFSET(regs.pc); \
1631 BRANCH(len); \
1633 len = 1 + JSOP_IFEQ_LENGTH; \
1634 DO_NEXT_OP(len); \
1636 JS_END_MACRO
1638 BEGIN_CASE(JSOP_IN)
1640 HandleValue rref = HandleValue::fromMarkedLocation(&regs.sp[-1]);
1641 if (!rref.isObject()) {
1642 js_ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, rref, NullPtr());
1643 goto error;
1645 RootedObject &obj = rootObject0;
1646 obj = &rref.toObject();
1647 RootedId &id = rootId0;
1648 FETCH_ELEMENT_ID(obj, -2, id);
1649 RootedObject &obj2 = rootObject1;
1650 RootedShape &prop = rootShape0;
1651 if (!JSObject::lookupGeneric(cx, obj, id, &obj2, &prop))
1652 goto error;
1653 bool cond = prop != NULL;
1654 prop = NULL;
1655 TRY_BRANCH_AFTER_COND(cond, 2);
1656 regs.sp--;
1657 regs.sp[-1].setBoolean(cond);
1659 END_CASE(JSOP_IN)
1661 BEGIN_CASE(JSOP_ITER)
1663 JS_ASSERT(regs.stackDepth() >= 1);
1664 uint8_t flags = GET_UINT8(regs.pc);
1665 MutableHandleValue res = MutableHandleValue::fromMarkedLocation(&regs.sp[-1]);
1666 if (!ValueToIterator(cx, flags, res))
1667 goto error;
1668 JS_ASSERT(!res.isPrimitive());
1670 END_CASE(JSOP_ITER)
1672 BEGIN_CASE(JSOP_MOREITER)
1674 JS_ASSERT(regs.stackDepth() >= 1);
1675 JS_ASSERT(regs.sp[-1].isObject());
1676 PUSH_NULL();
1677 bool cond;
1678 MutableHandleValue res = MutableHandleValue::fromMarkedLocation(&regs.sp[-1]);
1679 if (!IteratorMore(cx, &regs.sp[-2].toObject(), &cond, res))
1680 goto error;
1681 regs.sp[-1].setBoolean(cond);
1683 END_CASE(JSOP_MOREITER)
1685 BEGIN_CASE(JSOP_ITERNEXT)
1687 JS_ASSERT(regs.sp[-1].isObject());
1688 PUSH_NULL();
1689 MutableHandleValue res = MutableHandleValue::fromMarkedLocation(&regs.sp[-1]);
1690 RootedObject &obj = rootObject0;
1691 obj = &regs.sp[-2].toObject();
1692 if (!IteratorNext(cx, obj, res))
1693 goto error;
1695 END_CASE(JSOP_ITERNEXT)
1697 BEGIN_CASE(JSOP_ENDITER)
1699 JS_ASSERT(regs.stackDepth() >= 1);
1700 RootedObject &obj = rootObject0;
1701 obj = &regs.sp[-1].toObject();
1702 bool ok = CloseIterator(cx, obj);
1703 regs.sp--;
1704 if (!ok)
1705 goto error;
1707 END_CASE(JSOP_ENDITER)
1709 BEGIN_CASE(JSOP_DUP)
1711 JS_ASSERT(regs.stackDepth() >= 1);
1712 const Value &rref = regs.sp[-1];
1713 PUSH_COPY(rref);
1715 END_CASE(JSOP_DUP)
1717 BEGIN_CASE(JSOP_DUP2)
1719 JS_ASSERT(regs.stackDepth() >= 2);
1720 const Value &lref = regs.sp[-2];
1721 const Value &rref = regs.sp[-1];
1722 PUSH_COPY(lref);
1723 PUSH_COPY(rref);
1725 END_CASE(JSOP_DUP2)
1727 BEGIN_CASE(JSOP_SWAP)
1729 JS_ASSERT(regs.stackDepth() >= 2);
1730 Value &lref = regs.sp[-2];
1731 Value &rref = regs.sp[-1];
1732 lref.swap(rref);
1734 END_CASE(JSOP_SWAP)
1736 BEGIN_CASE(JSOP_PICK)
1738 unsigned i = GET_UINT8(regs.pc);
1739 JS_ASSERT(regs.stackDepth() >= i + 1);
1740 Value lval = regs.sp[-int(i + 1)];
1741 memmove(regs.sp - (i + 1), regs.sp - i, sizeof(Value) * i);
1742 regs.sp[-1] = lval;
1744 END_CASE(JSOP_PICK)
1746 BEGIN_CASE(JSOP_SETCONST)
1748 RootedPropertyName &name = rootName0;
1749 name = script->getName(regs.pc);
1751 RootedValue &rval = rootValue0;
1752 rval = regs.sp[-1];
1754 RootedObject &obj = rootObject0;
1755 obj = &regs.fp()->varObj();
1756 if (!JSObject::defineProperty(cx, obj, name, rval,
1757 JS_PropertyStub, JS_StrictPropertyStub,
1758 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY)) {
1759 goto error;
1762 END_CASE(JSOP_SETCONST);
1764 #if JS_HAS_DESTRUCTURING
1765 BEGIN_CASE(JSOP_ENUMCONSTELEM)
1767 RootedValue &rval = rootValue0;
1768 rval = regs.sp[-3];
1770 RootedObject &obj = rootObject0;
1771 FETCH_OBJECT(cx, -2, obj);
1772 RootedId &id = rootId0;
1773 FETCH_ELEMENT_ID(obj, -1, id);
1774 if (!JSObject::defineGeneric(cx, obj, id, rval,
1775 JS_PropertyStub, JS_StrictPropertyStub,
1776 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY)) {
1777 goto error;
1779 regs.sp -= 3;
1781 END_CASE(JSOP_ENUMCONSTELEM)
1782 #endif
1784 BEGIN_CASE(JSOP_BINDGNAME)
1785 PUSH_OBJECT(regs.fp()->global());
1786 END_CASE(JSOP_BINDGNAME)
1788 BEGIN_CASE(JSOP_BINDINTRINSIC)
1789 PUSH_OBJECT(*cx->global()->intrinsicsHolder());
1790 END_CASE(JSOP_BINDGNAME)
1792 BEGIN_CASE(JSOP_BINDNAME)
1794 RootedObject &scopeChain = rootObject0;
1795 scopeChain = regs.fp()->scopeChain();
1797 RootedPropertyName &name = rootName0;
1798 name = script->getName(regs.pc);
1800 /* Assigning to an undeclared name adds a property to the global object. */
1801 RootedObject &scope = rootObject1;
1802 if (!LookupNameWithGlobalDefault(cx, name, scopeChain, &scope))
1803 goto error;
1805 PUSH_OBJECT(*scope);
1807 END_CASE(JSOP_BINDNAME)
1809 #define BITWISE_OP(OP) \
1810 JS_BEGIN_MACRO \
1811 int32_t i, j; \
1812 if (!ToInt32(cx, regs.sp[-2], &i)) \
1813 goto error; \
1814 if (!ToInt32(cx, regs.sp[-1], &j)) \
1815 goto error; \
1816 i = i OP j; \
1817 regs.sp--; \
1818 regs.sp[-1].setInt32(i); \
1819 JS_END_MACRO
1821 BEGIN_CASE(JSOP_BITOR)
1822 BITWISE_OP(|);
1823 END_CASE(JSOP_BITOR)
1825 BEGIN_CASE(JSOP_BITXOR)
1826 BITWISE_OP(^);
1827 END_CASE(JSOP_BITXOR)
1829 BEGIN_CASE(JSOP_BITAND)
1830 BITWISE_OP(&);
1831 END_CASE(JSOP_BITAND)
1833 #undef BITWISE_OP
1835 #define EQUALITY_OP(OP) \
1836 JS_BEGIN_MACRO \
1837 Value rval = regs.sp[-1]; \
1838 Value lval = regs.sp[-2]; \
1839 bool cond; \
1840 if (!LooselyEqual(cx, lval, rval, &cond)) \
1841 goto error; \
1842 cond = cond OP JS_TRUE; \
1843 TRY_BRANCH_AFTER_COND(cond, 2); \
1844 regs.sp--; \
1845 regs.sp[-1].setBoolean(cond); \
1846 JS_END_MACRO
1848 BEGIN_CASE(JSOP_EQ)
1849 EQUALITY_OP(==);
1850 END_CASE(JSOP_EQ)
1852 BEGIN_CASE(JSOP_NE)
1853 EQUALITY_OP(!=);
1854 END_CASE(JSOP_NE)
1856 #undef EQUALITY_OP
1858 #define STRICT_EQUALITY_OP(OP, COND) \
1859 JS_BEGIN_MACRO \
1860 const Value &rref = regs.sp[-1]; \
1861 const Value &lref = regs.sp[-2]; \
1862 bool equal; \
1863 if (!StrictlyEqual(cx, lref, rref, &equal)) \
1864 goto error; \
1865 COND = equal OP JS_TRUE; \
1866 regs.sp--; \
1867 JS_END_MACRO
1869 BEGIN_CASE(JSOP_STRICTEQ)
1871 bool cond;
1872 STRICT_EQUALITY_OP(==, cond);
1873 regs.sp[-1].setBoolean(cond);
1875 END_CASE(JSOP_STRICTEQ)
1877 BEGIN_CASE(JSOP_STRICTNE)
1879 bool cond;
1880 STRICT_EQUALITY_OP(!=, cond);
1881 regs.sp[-1].setBoolean(cond);
1883 END_CASE(JSOP_STRICTNE)
1885 BEGIN_CASE(JSOP_CASE)
1887 bool cond;
1888 STRICT_EQUALITY_OP(==, cond);
1889 if (cond) {
1890 regs.sp--;
1891 len = GET_JUMP_OFFSET(regs.pc);
1892 BRANCH(len);
1895 END_CASE(JSOP_CASE)
1897 #undef STRICT_EQUALITY_OP
1899 BEGIN_CASE(JSOP_LT)
1901 bool cond;
1902 MutableHandleValue lval = MutableHandleValue::fromMarkedLocation(&regs.sp[-2]);
1903 MutableHandleValue rval = MutableHandleValue::fromMarkedLocation(&regs.sp[-1]);
1904 if (!LessThanOperation(cx, lval, rval, &cond))
1905 goto error;
1906 TRY_BRANCH_AFTER_COND(cond, 2);
1907 regs.sp[-2].setBoolean(cond);
1908 regs.sp--;
1910 END_CASE(JSOP_LT)
1912 BEGIN_CASE(JSOP_LE)
1914 bool cond;
1915 MutableHandleValue lval = MutableHandleValue::fromMarkedLocation(&regs.sp[-2]);
1916 MutableHandleValue rval = MutableHandleValue::fromMarkedLocation(&regs.sp[-1]);
1917 if (!LessThanOrEqualOperation(cx, lval, rval, &cond))
1918 goto error;
1919 TRY_BRANCH_AFTER_COND(cond, 2);
1920 regs.sp[-2].setBoolean(cond);
1921 regs.sp--;
1923 END_CASE(JSOP_LE)
1925 BEGIN_CASE(JSOP_GT)
1927 bool cond;
1928 MutableHandleValue lval = MutableHandleValue::fromMarkedLocation(&regs.sp[-2]);
1929 MutableHandleValue rval = MutableHandleValue::fromMarkedLocation(&regs.sp[-1]);
1930 if (!GreaterThanOperation(cx, lval, rval, &cond))
1931 goto error;
1932 TRY_BRANCH_AFTER_COND(cond, 2);
1933 regs.sp[-2].setBoolean(cond);
1934 regs.sp--;
1936 END_CASE(JSOP_GT)
1938 BEGIN_CASE(JSOP_GE)
1940 bool cond;
1941 MutableHandleValue lval = MutableHandleValue::fromMarkedLocation(&regs.sp[-2]);
1942 MutableHandleValue rval = MutableHandleValue::fromMarkedLocation(&regs.sp[-1]);
1943 if (!GreaterThanOrEqualOperation(cx, lval, rval, &cond))
1944 goto error;
1945 TRY_BRANCH_AFTER_COND(cond, 2);
1946 regs.sp[-2].setBoolean(cond);
1947 regs.sp--;
1949 END_CASE(JSOP_GE)
1951 #define SIGNED_SHIFT_OP(OP) \
1952 JS_BEGIN_MACRO \
1953 int32_t i, j; \
1954 if (!ToInt32(cx, regs.sp[-2], &i)) \
1955 goto error; \
1956 if (!ToInt32(cx, regs.sp[-1], &j)) \
1957 goto error; \
1958 i = i OP (j & 31); \
1959 regs.sp--; \
1960 regs.sp[-1].setInt32(i); \
1961 JS_END_MACRO
1963 BEGIN_CASE(JSOP_LSH)
1964 SIGNED_SHIFT_OP(<<);
1965 END_CASE(JSOP_LSH)
1967 BEGIN_CASE(JSOP_RSH)
1968 SIGNED_SHIFT_OP(>>);
1969 END_CASE(JSOP_RSH)
1971 #undef SIGNED_SHIFT_OP
1973 BEGIN_CASE(JSOP_URSH)
1975 HandleValue lval = HandleValue::fromMarkedLocation(&regs.sp[-2]);
1976 HandleValue rval = HandleValue::fromMarkedLocation(&regs.sp[-1]);
1977 if (!UrshOperation(cx, script, regs.pc, lval, rval, &regs.sp[-2]))
1978 goto error;
1979 regs.sp--;
1981 END_CASE(JSOP_URSH)
1983 BEGIN_CASE(JSOP_ADD)
1985 MutableHandleValue lval = MutableHandleValue::fromMarkedLocation(&regs.sp[-2]);
1986 MutableHandleValue rval = MutableHandleValue::fromMarkedLocation(&regs.sp[-1]);
1987 if (!AddOperation(cx, script, regs.pc, lval, rval, &regs.sp[-2]))
1988 goto error;
1989 regs.sp--;
1991 END_CASE(JSOP_ADD)
1993 BEGIN_CASE(JSOP_SUB)
1995 RootedValue &lval = rootValue0, &rval = rootValue1;
1996 lval = regs.sp[-2];
1997 rval = regs.sp[-1];
1998 if (!SubOperation(cx, script, regs.pc, lval, rval, &regs.sp[-2]))
1999 goto error;
2000 regs.sp--;
2002 END_CASE(JSOP_SUB)
2004 BEGIN_CASE(JSOP_MUL)
2006 RootedValue &lval = rootValue0, &rval = rootValue1;
2007 lval = regs.sp[-2];
2008 rval = regs.sp[-1];
2009 if (!MulOperation(cx, script, regs.pc, lval, rval, &regs.sp[-2]))
2010 goto error;
2011 regs.sp--;
2013 END_CASE(JSOP_MUL)
2015 BEGIN_CASE(JSOP_DIV)
2017 RootedValue &lval = rootValue0, &rval = rootValue1;
2018 lval = regs.sp[-2];
2019 rval = regs.sp[-1];
2020 if (!DivOperation(cx, script, regs.pc, lval, rval, &regs.sp[-2]))
2021 goto error;
2022 regs.sp--;
2024 END_CASE(JSOP_DIV)
2026 BEGIN_CASE(JSOP_MOD)
2028 RootedValue &lval = rootValue0, &rval = rootValue1;
2029 lval = regs.sp[-2];
2030 rval = regs.sp[-1];
2031 if (!ModOperation(cx, script, regs.pc, lval, rval, &regs.sp[-2]))
2032 goto error;
2033 regs.sp--;
2035 END_CASE(JSOP_MOD)
2037 BEGIN_CASE(JSOP_NOT)
2039 bool cond = ToBoolean(regs.sp[-1]);
2040 regs.sp--;
2041 PUSH_BOOLEAN(!cond);
2043 END_CASE(JSOP_NOT)
2045 BEGIN_CASE(JSOP_BITNOT)
2047 int32_t i;
2048 HandleValue value = HandleValue::fromMarkedLocation(&regs.sp[-1]);
2049 if (!BitNot(cx, value, &i))
2050 goto error;
2051 regs.sp[-1].setInt32(i);
2053 END_CASE(JSOP_BITNOT)
2055 BEGIN_CASE(JSOP_NEG)
2057 RootedValue &val = rootValue0;
2058 val = regs.sp[-1];
2059 MutableHandleValue res = MutableHandleValue::fromMarkedLocation(&regs.sp[-1]);
2060 if (!NegOperation(cx, script, regs.pc, val, res))
2061 goto error;
2063 END_CASE(JSOP_NEG)
2065 BEGIN_CASE(JSOP_POS)
2066 if (!ToNumber(cx, &regs.sp[-1]))
2067 goto error;
2068 if (!regs.sp[-1].isInt32())
2069 TypeScript::MonitorOverflow(cx, script, regs.pc);
2070 END_CASE(JSOP_POS)
2072 BEGIN_CASE(JSOP_DELNAME)
2074 RootedPropertyName &name = rootName0;
2075 name = script->getName(regs.pc);
2077 RootedObject &scopeObj = rootObject0;
2078 scopeObj = cx->stack.currentScriptedScopeChain();
2080 RootedObject &scope = rootObject1;
2081 RootedObject &pobj = rootObject2;
2082 RootedShape &prop = rootShape0;
2083 if (!LookupName(cx, name, scopeObj, &scope, &pobj, &prop))
2084 goto error;
2086 /* Strict mode code should never contain JSOP_DELNAME opcodes. */
2087 JS_ASSERT(!script->strict);
2089 /* ECMA says to return true if name is undefined or inherited. */
2090 PUSH_BOOLEAN(true);
2091 if (prop) {
2092 MutableHandleValue res = MutableHandleValue::fromMarkedLocation(&regs.sp[-1]);
2093 if (!JSObject::deleteProperty(cx, scope, name, res, false))
2094 goto error;
2096 prop = NULL;
2098 END_CASE(JSOP_DELNAME)
2100 BEGIN_CASE(JSOP_DELPROP)
2102 RootedPropertyName &name = rootName0;
2103 name = script->getName(regs.pc);
2105 RootedObject &obj = rootObject0;
2106 FETCH_OBJECT(cx, -1, obj);
2108 MutableHandleValue res = MutableHandleValue::fromMarkedLocation(&regs.sp[-1]);
2109 if (!JSObject::deleteProperty(cx, obj, name, res, script->strict))
2110 goto error;
2112 END_CASE(JSOP_DELPROP)
2114 BEGIN_CASE(JSOP_DELELEM)
2116 /* Fetch the left part and resolve it to a non-null object. */
2117 RootedObject &obj = rootObject0;
2118 FETCH_OBJECT(cx, -2, obj);
2120 RootedValue &propval = rootValue0;
2121 propval = regs.sp[-1];
2123 MutableHandleValue res = MutableHandleValue::fromMarkedLocation(&regs.sp[-2]);
2124 if (!JSObject::deleteByValue(cx, obj, propval, res, script->strict))
2125 goto error;
2127 regs.sp--;
2129 END_CASE(JSOP_DELELEM)
2131 BEGIN_CASE(JSOP_TOID)
2134 * Increment or decrement requires use to lookup the same property twice,
2135 * but we need to avoid the observable stringification the second time.
2136 * There must be an object value below the id, which will not be popped.
2138 RootedValue &objval = rootValue0, &idval = rootValue1;
2139 objval = regs.sp[-2];
2140 idval = regs.sp[-1];
2142 MutableHandleValue res = MutableHandleValue::fromMarkedLocation(&regs.sp[-1]);
2143 if (!ToIdOperation(cx, script, regs.pc, objval, idval, res))
2144 goto error;
2146 END_CASE(JSOP_TOID)
2148 BEGIN_CASE(JSOP_TYPEOFEXPR)
2149 BEGIN_CASE(JSOP_TYPEOF)
2151 HandleValue ref = HandleValue::fromMarkedLocation(&regs.sp[-1]);
2152 regs.sp[-1].setString(TypeOfOperation(cx, ref));
2154 END_CASE(JSOP_TYPEOF)
2156 BEGIN_CASE(JSOP_VOID)
2157 regs.sp[-1].setUndefined();
2158 END_CASE(JSOP_VOID)
2160 BEGIN_CASE(JSOP_INCELEM)
2161 BEGIN_CASE(JSOP_DECELEM)
2162 BEGIN_CASE(JSOP_ELEMINC)
2163 BEGIN_CASE(JSOP_ELEMDEC)
2164 /* No-op */
2165 END_CASE(JSOP_INCELEM)
2167 BEGIN_CASE(JSOP_INCPROP)
2168 BEGIN_CASE(JSOP_DECPROP)
2169 BEGIN_CASE(JSOP_PROPINC)
2170 BEGIN_CASE(JSOP_PROPDEC)
2171 BEGIN_CASE(JSOP_INCNAME)
2172 BEGIN_CASE(JSOP_DECNAME)
2173 BEGIN_CASE(JSOP_NAMEINC)
2174 BEGIN_CASE(JSOP_NAMEDEC)
2175 BEGIN_CASE(JSOP_INCGNAME)
2176 BEGIN_CASE(JSOP_DECGNAME)
2177 BEGIN_CASE(JSOP_GNAMEINC)
2178 BEGIN_CASE(JSOP_GNAMEDEC)
2179 /* No-op */
2180 END_CASE(JSOP_INCPROP)
2182 BEGIN_CASE(JSOP_DECALIASEDVAR)
2183 BEGIN_CASE(JSOP_ALIASEDVARDEC)
2184 BEGIN_CASE(JSOP_INCALIASEDVAR)
2185 BEGIN_CASE(JSOP_ALIASEDVARINC)
2186 /* No-op */
2187 END_CASE(JSOP_ALIASEDVARINC)
2189 BEGIN_CASE(JSOP_DECARG)
2190 BEGIN_CASE(JSOP_ARGDEC)
2191 BEGIN_CASE(JSOP_INCARG)
2192 BEGIN_CASE(JSOP_ARGINC)
2194 /* No-op */
2196 END_CASE(JSOP_ARGINC);
2198 BEGIN_CASE(JSOP_DECLOCAL)
2199 BEGIN_CASE(JSOP_LOCALDEC)
2200 BEGIN_CASE(JSOP_INCLOCAL)
2201 BEGIN_CASE(JSOP_LOCALINC)
2203 /* No-op */
2205 END_CASE(JSOP_LOCALINC)
2207 BEGIN_CASE(JSOP_THIS)
2208 if (!ComputeThis(cx, regs.fp()))
2209 goto error;
2210 PUSH_COPY(regs.fp()->thisValue());
2211 END_CASE(JSOP_THIS)
2213 BEGIN_CASE(JSOP_GETPROP)
2214 BEGIN_CASE(JSOP_GETXPROP)
2215 BEGIN_CASE(JSOP_LENGTH)
2216 BEGIN_CASE(JSOP_CALLPROP)
2218 MutableHandleValue lval = MutableHandleValue::fromMarkedLocation(&regs.sp[-1]);
2219 if (!GetPropertyOperation(cx, script, regs.pc, lval, lval))
2220 goto error;
2222 TypeScript::Monitor(cx, script, regs.pc, lval);
2223 assertSameCompartmentDebugOnly(cx, lval);
2225 END_CASE(JSOP_GETPROP)
2227 BEGIN_CASE(JSOP_SETINTRINSIC)
2229 HandleValue value = HandleValue::fromMarkedLocation(&regs.sp[-1]);
2231 if (!SetIntrinsicOperation(cx, script, regs.pc, value))
2232 goto error;
2234 regs.sp[-2] = regs.sp[-1];
2235 regs.sp--;
2237 END_CASE(JSOP_SETINTRINSIC)
2239 BEGIN_CASE(JSOP_SETGNAME)
2240 BEGIN_CASE(JSOP_SETNAME)
2242 RootedObject &scope = rootObject0;
2243 scope = &regs.sp[-2].toObject();
2245 HandleValue value = HandleValue::fromMarkedLocation(&regs.sp[-1]);
2247 if (!SetNameOperation(cx, script, regs.pc, scope, value))
2248 goto error;
2250 regs.sp[-2] = regs.sp[-1];
2251 regs.sp--;
2253 END_CASE(JSOP_SETNAME)
2255 BEGIN_CASE(JSOP_SETPROP)
2257 HandleValue lval = HandleValue::fromMarkedLocation(&regs.sp[-2]);
2258 HandleValue rval = HandleValue::fromMarkedLocation(&regs.sp[-1]);
2260 if (!SetPropertyOperation(cx, regs.pc, lval, rval))
2261 goto error;
2263 regs.sp[-2] = regs.sp[-1];
2264 regs.sp--;
2266 END_CASE(JSOP_SETPROP)
2268 BEGIN_CASE(JSOP_GETELEM)
2269 BEGIN_CASE(JSOP_CALLELEM)
2271 MutableHandleValue lval = MutableHandleValue::fromMarkedLocation(&regs.sp[-2]);
2272 HandleValue rval = HandleValue::fromMarkedLocation(&regs.sp[-1]);
2274 MutableHandleValue res = MutableHandleValue::fromMarkedLocation(&regs.sp[-2]);
2275 if (!GetElementOperation(cx, op, lval, rval, res))
2276 goto error;
2277 TypeScript::Monitor(cx, script, regs.pc, res);
2278 regs.sp--;
2280 END_CASE(JSOP_GETELEM)
2282 BEGIN_CASE(JSOP_SETELEM)
2284 RootedObject &obj = rootObject0;
2285 FETCH_OBJECT(cx, -3, obj);
2286 RootedId &id = rootId0;
2287 FETCH_ELEMENT_ID(obj, -2, id);
2288 Value &value = regs.sp[-1];
2289 if (!SetObjectElementOperation(cx, obj, id, value, script->strict))
2290 goto error;
2291 regs.sp[-3] = value;
2292 regs.sp -= 2;
2294 END_CASE(JSOP_SETELEM)
2296 BEGIN_CASE(JSOP_ENUMELEM)
2298 RootedObject &obj = rootObject0;
2299 RootedValue &rval = rootValue0;
2301 /* Funky: the value to set is under the [obj, id] pair. */
2302 FETCH_OBJECT(cx, -2, obj);
2303 RootedId &id = rootId0;
2304 FETCH_ELEMENT_ID(obj, -1, id);
2305 rval = regs.sp[-3];
2306 if (!JSObject::setGeneric(cx, obj, obj, id, &rval, script->strict))
2307 goto error;
2308 regs.sp -= 3;
2310 END_CASE(JSOP_ENUMELEM)
2312 BEGIN_CASE(JSOP_EVAL)
2314 CallArgs args = CallArgsFromSp(GET_ARGC(regs.pc), regs.sp);
2315 if (IsBuiltinEvalForScope(regs.fp()->scopeChain(), args.calleev())) {
2316 if (!DirectEval(cx, args))
2317 goto error;
2318 } else {
2319 if (!InvokeKernel(cx, args))
2320 goto error;
2322 regs.sp = args.spAfterCall();
2323 TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
2325 END_CASE(JSOP_EVAL)
2327 BEGIN_CASE(JSOP_FUNAPPLY)
2328 if (!GuardFunApplyArgumentsOptimization(cx))
2329 goto error;
2330 /* FALL THROUGH */
2332 BEGIN_CASE(JSOP_NEW)
2333 BEGIN_CASE(JSOP_CALL)
2334 BEGIN_CASE(JSOP_FUNCALL)
2336 if (regs.fp()->hasPushedSPSFrame())
2337 cx->runtime->spsProfiler.updatePC(script, regs.pc);
2338 JS_ASSERT(regs.stackDepth() >= 2 + GET_ARGC(regs.pc));
2339 CallArgs args = CallArgsFromSp(GET_ARGC(regs.pc), regs.sp);
2341 bool construct = (*regs.pc == JSOP_NEW);
2343 RootedFunction &fun = rootFunction0;
2344 RootedScript &funScript = rootScript0;
2345 bool isFunction = IsFunctionObject(args.calleev(), fun.address());
2348 * Some builtins are marked as clone-at-callsite to increase precision of
2349 * TI and JITs.
2351 if (isFunction && fun->isInterpreted()) {
2352 funScript = fun->getOrCreateScript(cx);
2353 if (!funScript)
2354 goto error;
2355 if (cx->typeInferenceEnabled() && funScript->shouldCloneAtCallsite) {
2356 fun = CloneFunctionAtCallsite(cx, fun, script, regs.pc);
2357 if (!fun)
2358 goto error;
2359 args.setCallee(ObjectValue(*fun));
2363 /* Don't bother trying to fast-path calls to scripted non-constructors. */
2364 if (!isFunction || !fun->isInterpretedConstructor()) {
2365 if (construct) {
2366 if (!InvokeConstructorKernel(cx, args))
2367 goto error;
2368 } else {
2369 if (!InvokeKernel(cx, args))
2370 goto error;
2372 Value *newsp = args.spAfterCall();
2373 TypeScript::Monitor(cx, script, regs.pc, newsp[-1]);
2374 regs.sp = newsp;
2375 len = JSOP_CALL_LENGTH;
2376 DO_NEXT_OP(len);
2379 if (!TypeMonitorCall(cx, args, construct))
2380 goto error;
2382 InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE;
2383 bool newType = cx->typeInferenceEnabled() && UseNewType(cx, script, regs.pc);
2384 funScript = fun->nonLazyScript();
2385 if (!cx->stack.pushInlineFrame(cx, regs, args, fun, funScript, initial))
2386 goto error;
2388 if (newType)
2389 regs.fp()->setUseNewType();
2391 SET_SCRIPT(regs.fp()->script());
2392 #ifdef JS_METHODJIT
2393 script->resetLoopCount();
2394 #endif
2396 #ifdef JS_ION
2397 if (!newType && ion::IsEnabled(cx)) {
2398 ion::MethodStatus status = ion::CanEnter(cx, script, AbstractFramePtr(regs.fp()),
2399 regs.fp()->isConstructing());
2400 if (status == ion::Method_Error)
2401 goto error;
2402 if (status == ion::Method_Compiled) {
2403 ion::IonExecStatus exec = ion::Cannon(cx, regs.fp());
2404 CHECK_BRANCH();
2405 if (exec == ion::IonExec_Bailout) {
2406 SET_SCRIPT(regs.fp()->script());
2407 op = JSOp(*regs.pc);
2408 DO_OP();
2410 interpReturnOK = !IsErrorStatus(exec);
2411 goto jit_return;
2414 #endif
2416 #ifdef JS_METHODJIT
2417 if (!newType && cx->methodJitEnabled) {
2418 /* Try to ensure methods are method JIT'd. */
2419 mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, script->code,
2420 construct,
2421 mjit::CompileRequest_Interpreter,
2422 regs.fp());
2423 if (status == mjit::Compile_Error)
2424 goto error;
2425 if (status == mjit::Compile_Okay) {
2426 mjit::JaegerStatus status = mjit::JaegerShot(cx, true);
2427 CHECK_PARTIAL_METHODJIT(status);
2428 interpReturnOK = mjit::JaegerStatusToSuccess(status);
2429 goto jit_return;
2432 #endif
2434 if (!regs.fp()->prologue(cx))
2435 goto error;
2436 if (cx->compartment->debugMode()) {
2437 switch (ScriptDebugPrologue(cx, regs.fp())) {
2438 case JSTRAP_CONTINUE:
2439 break;
2440 case JSTRAP_RETURN:
2441 interpReturnOK = true;
2442 goto forced_return;
2443 case JSTRAP_THROW:
2444 case JSTRAP_ERROR:
2445 goto error;
2446 default:
2447 JS_NOT_REACHED("bad ScriptDebugPrologue status");
2451 /* Load first op and dispatch it (safe since JSOP_STOP). */
2452 op = (JSOp) *regs.pc;
2453 DO_OP();
2456 BEGIN_CASE(JSOP_SETCALL)
2458 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_LEFTSIDE_OF_ASS);
2459 goto error;
2461 END_CASE(JSOP_SETCALL)
2463 BEGIN_CASE(JSOP_IMPLICITTHIS)
2465 RootedPropertyName &name = rootName0;
2466 name = script->getName(regs.pc);
2468 RootedObject &scopeObj = rootObject0;
2469 scopeObj = cx->stack.currentScriptedScopeChain();
2471 RootedObject &scope = rootObject1;
2472 if (!LookupNameWithGlobalDefault(cx, name, scopeObj, &scope))
2473 goto error;
2475 RootedValue &v = rootValue0;
2476 if (!ComputeImplicitThis(cx, scope, &v))
2477 goto error;
2478 PUSH_COPY(v);
2480 END_CASE(JSOP_IMPLICITTHIS)
2482 BEGIN_CASE(JSOP_GETGNAME)
2483 BEGIN_CASE(JSOP_CALLGNAME)
2484 BEGIN_CASE(JSOP_NAME)
2485 BEGIN_CASE(JSOP_CALLNAME)
2487 RootedValue &rval = rootValue0;
2489 if (!NameOperation(cx, regs.pc, &rval))
2490 goto error;
2492 PUSH_COPY(rval);
2493 TypeScript::Monitor(cx, script, regs.pc, rval);
2495 END_CASE(JSOP_NAME)
2497 BEGIN_CASE(JSOP_GETINTRINSIC)
2498 BEGIN_CASE(JSOP_CALLINTRINSIC)
2500 RootedValue &rval = rootValue0;
2502 if (!GetIntrinsicOperation(cx, script, regs.pc, &rval))
2503 goto error;
2505 PUSH_COPY(rval);
2506 TypeScript::Monitor(cx, script, regs.pc, rval);
2508 END_CASE(JSOP_GETINTRINSIC)
2510 BEGIN_CASE(JSOP_UINT16)
2511 PUSH_INT32((int32_t) GET_UINT16(regs.pc));
2512 END_CASE(JSOP_UINT16)
2514 BEGIN_CASE(JSOP_UINT24)
2515 PUSH_INT32((int32_t) GET_UINT24(regs.pc));
2516 END_CASE(JSOP_UINT24)
2518 BEGIN_CASE(JSOP_INT8)
2519 PUSH_INT32(GET_INT8(regs.pc));
2520 END_CASE(JSOP_INT8)
2522 BEGIN_CASE(JSOP_INT32)
2523 PUSH_INT32(GET_INT32(regs.pc));
2524 END_CASE(JSOP_INT32)
2526 BEGIN_CASE(JSOP_DOUBLE)
2528 double dbl;
2529 LOAD_DOUBLE(0, dbl);
2530 PUSH_DOUBLE(dbl);
2532 END_CASE(JSOP_DOUBLE)
2534 BEGIN_CASE(JSOP_STRING)
2535 PUSH_STRING(script->getAtom(regs.pc));
2536 END_CASE(JSOP_STRING)
2538 BEGIN_CASE(JSOP_OBJECT)
2539 PUSH_OBJECT(*script->getObject(regs.pc));
2540 END_CASE(JSOP_OBJECT)
2542 BEGIN_CASE(JSOP_REGEXP)
2545 * Push a regexp object cloned from the regexp literal object mapped by the
2546 * bytecode at pc.
2548 uint32_t index = GET_UINT32_INDEX(regs.pc);
2549 JSObject *proto = regs.fp()->global().getOrCreateRegExpPrototype(cx);
2550 if (!proto)
2551 goto error;
2552 JSObject *obj = CloneRegExpObject(cx, script->getRegExp(index), proto);
2553 if (!obj)
2554 goto error;
2555 PUSH_OBJECT(*obj);
2557 END_CASE(JSOP_REGEXP)
2559 BEGIN_CASE(JSOP_ZERO)
2560 PUSH_INT32(0);
2561 END_CASE(JSOP_ZERO)
2563 BEGIN_CASE(JSOP_ONE)
2564 PUSH_INT32(1);
2565 END_CASE(JSOP_ONE)
2567 BEGIN_CASE(JSOP_NULL)
2568 PUSH_NULL();
2569 END_CASE(JSOP_NULL)
2571 BEGIN_CASE(JSOP_FALSE)
2572 PUSH_BOOLEAN(false);
2573 END_CASE(JSOP_FALSE)
2575 BEGIN_CASE(JSOP_TRUE)
2576 PUSH_BOOLEAN(true);
2577 END_CASE(JSOP_TRUE)
2580 BEGIN_CASE(JSOP_TABLESWITCH)
2582 jsbytecode *pc2 = regs.pc;
2583 len = GET_JUMP_OFFSET(pc2);
2586 * ECMAv2+ forbids conversion of discriminant, so we will skip to the
2587 * default case if the discriminant isn't already an int jsval. (This
2588 * opcode is emitted only for dense int-domain switches.)
2590 const Value &rref = *--regs.sp;
2591 int32_t i;
2592 if (rref.isInt32()) {
2593 i = rref.toInt32();
2594 } else {
2595 double d;
2596 /* Don't use MOZ_DOUBLE_IS_INT32; treat -0 (double) as 0. */
2597 if (!rref.isDouble() || (d = rref.toDouble()) != (i = int32_t(rref.toDouble())))
2598 DO_NEXT_OP(len);
2601 pc2 += JUMP_OFFSET_LEN;
2602 int32_t low = GET_JUMP_OFFSET(pc2);
2603 pc2 += JUMP_OFFSET_LEN;
2604 int32_t high = GET_JUMP_OFFSET(pc2);
2606 i -= low;
2607 if ((uint32_t)i < (uint32_t)(high - low + 1)) {
2608 pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i;
2609 int32_t off = (int32_t) GET_JUMP_OFFSET(pc2);
2610 if (off)
2611 len = off;
2614 END_VARLEN_CASE
2617 BEGIN_CASE(JSOP_ARGUMENTS)
2618 JS_ASSERT(!regs.fp()->fun()->hasRest());
2619 if (script->needsArgsObj()) {
2620 ArgumentsObject *obj = ArgumentsObject::createExpected(cx, regs.fp());
2621 if (!obj)
2622 goto error;
2623 PUSH_COPY(ObjectValue(*obj));
2624 } else {
2625 PUSH_COPY(MagicValue(JS_OPTIMIZED_ARGUMENTS));
2627 END_CASE(JSOP_ARGUMENTS)
2629 BEGIN_CASE(JSOP_REST)
2631 RootedObject &rest = rootObject0;
2632 rest = regs.fp()->createRestParameter(cx);
2633 if (!rest)
2634 goto error;
2635 PUSH_COPY(ObjectValue(*rest));
2636 if (!SetInitializerObjectType(cx, script, regs.pc, rest, GenericObject))
2637 goto error;
2638 rootType0 = GetTypeCallerInitObject(cx, JSProto_Array);
2639 if (!rootType0)
2640 goto error;
2641 rest->setType(rootType0);
2643 END_CASE(JSOP_REST)
2645 BEGIN_CASE(JSOP_CALLALIASEDVAR)
2646 BEGIN_CASE(JSOP_GETALIASEDVAR)
2648 ScopeCoordinate sc = ScopeCoordinate(regs.pc);
2649 PUSH_COPY(regs.fp()->aliasedVarScope(sc).aliasedVar(sc));
2650 TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
2652 END_CASE(JSOP_GETALIASEDVAR)
2654 BEGIN_CASE(JSOP_SETALIASEDVAR)
2656 ScopeCoordinate sc = ScopeCoordinate(regs.pc);
2657 regs.fp()->aliasedVarScope(sc).setAliasedVar(sc, regs.sp[-1]);
2659 END_CASE(JSOP_SETALIASEDVAR)
2661 BEGIN_CASE(JSOP_GETARG)
2662 BEGIN_CASE(JSOP_CALLARG)
2664 unsigned i = GET_ARGNO(regs.pc);
2665 if (script->argsObjAliasesFormals())
2666 PUSH_COPY(regs.fp()->argsObj().arg(i));
2667 else
2668 PUSH_COPY(regs.fp()->unaliasedFormal(i));
2670 END_CASE(JSOP_GETARG)
2672 BEGIN_CASE(JSOP_SETARG)
2674 unsigned i = GET_ARGNO(regs.pc);
2675 if (script->argsObjAliasesFormals())
2676 regs.fp()->argsObj().setArg(i, regs.sp[-1]);
2677 else
2678 regs.fp()->unaliasedFormal(i) = regs.sp[-1];
2680 END_CASE(JSOP_SETARG)
2682 BEGIN_CASE(JSOP_GETLOCAL)
2683 BEGIN_CASE(JSOP_CALLLOCAL)
2685 unsigned i = GET_SLOTNO(regs.pc);
2686 PUSH_COPY_SKIP_CHECK(regs.fp()->unaliasedLocal(i));
2689 * Skip the same-compartment assertion if the local will be immediately
2690 * popped. We do not guarantee sync for dead locals when coming in from the
2691 * method JIT, and a GETLOCAL followed by POP is not considered to be
2692 * a use of the variable.
2694 if (regs.pc[JSOP_GETLOCAL_LENGTH] != JSOP_POP)
2695 assertSameCompartmentDebugOnly(cx, regs.sp[-1]);
2697 END_CASE(JSOP_GETLOCAL)
2699 BEGIN_CASE(JSOP_SETLOCAL)
2701 unsigned i = GET_SLOTNO(regs.pc);
2702 regs.fp()->unaliasedLocal(i) = regs.sp[-1];
2704 END_CASE(JSOP_SETLOCAL)
2706 BEGIN_CASE(JSOP_DEFCONST)
2707 BEGIN_CASE(JSOP_DEFVAR)
2709 /* ES5 10.5 step 8 (with subsequent errata). */
2710 unsigned attrs = JSPROP_ENUMERATE;
2711 if (!regs.fp()->isEvalFrame())
2712 attrs |= JSPROP_PERMANENT;
2713 if (op == JSOP_DEFCONST)
2714 attrs |= JSPROP_READONLY;
2716 /* Step 8b. */
2717 RootedObject &obj = rootObject0;
2718 obj = &regs.fp()->varObj();
2720 RootedPropertyName &name = rootName0;
2721 name = script->getName(regs.pc);
2723 if (!DefVarOrConstOperation(cx, obj, name, attrs))
2724 goto error;
2726 END_CASE(JSOP_DEFVAR)
2728 BEGIN_CASE(JSOP_DEFFUN)
2731 * A top-level function defined in Global or Eval code (see ECMA-262
2732 * Ed. 3), or else a SpiderMonkey extension: a named function statement in
2733 * a compound statement (not at the top statement level of global code, or
2734 * at the top level of a function body).
2736 RootedFunction &fun = rootFunction0;
2737 fun = script->getFunction(GET_UINT32_INDEX(regs.pc));
2739 if (!DefFunOperation(cx, script, regs.fp()->scopeChain(), fun))
2740 goto error;
2742 END_CASE(JSOP_DEFFUN)
2744 BEGIN_CASE(JSOP_LAMBDA)
2746 /* Load the specified function object literal. */
2747 RootedFunction &fun = rootFunction0;
2748 fun = script->getFunction(GET_UINT32_INDEX(regs.pc));
2750 JSObject *obj = Lambda(cx, fun, regs.fp()->scopeChain());
2751 if (!obj)
2752 goto error;
2753 JS_ASSERT(obj->getProto());
2754 PUSH_OBJECT(*obj);
2756 END_CASE(JSOP_LAMBDA)
2758 BEGIN_CASE(JSOP_CALLEE)
2759 JS_ASSERT(regs.fp()->isNonEvalFunctionFrame());
2760 PUSH_COPY(regs.fp()->calleev());
2761 END_CASE(JSOP_CALLEE)
2763 BEGIN_CASE(JSOP_GETTER)
2764 BEGIN_CASE(JSOP_SETTER)
2766 JSOp op2 = JSOp(*++regs.pc);
2767 RootedId &id = rootId0;
2768 RootedValue &rval = rootValue0;
2769 RootedValue &scratch = rootValue1;
2770 int i;
2772 RootedObject &obj = rootObject0;
2773 switch (op2) {
2774 case JSOP_SETNAME:
2775 case JSOP_SETPROP:
2776 id = NameToId(script->getName(regs.pc));
2777 rval = regs.sp[-1];
2778 i = -1;
2779 goto gs_pop_lval;
2780 case JSOP_SETELEM:
2781 rval = regs.sp[-1];
2782 id = JSID_VOID;
2783 i = -2;
2784 gs_pop_lval:
2785 FETCH_OBJECT(cx, i - 1, obj);
2786 break;
2788 case JSOP_INITPROP:
2789 JS_ASSERT(regs.stackDepth() >= 2);
2790 rval = regs.sp[-1];
2791 i = -1;
2792 id = NameToId(script->getName(regs.pc));
2793 goto gs_get_lval;
2794 default:
2795 JS_ASSERT(op2 == JSOP_INITELEM);
2796 JS_ASSERT(regs.stackDepth() >= 3);
2797 rval = regs.sp[-1];
2798 id = JSID_VOID;
2799 i = -2;
2800 gs_get_lval:
2802 const Value &lref = regs.sp[i-1];
2803 JS_ASSERT(lref.isObject());
2804 obj = &lref.toObject();
2805 break;
2809 /* Ensure that id has a type suitable for use with obj. */
2810 if (JSID_IS_VOID(id))
2811 FETCH_ELEMENT_ID(obj, i, id);
2813 if (!js_IsCallable(rval)) {
2814 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_GETTER_OR_SETTER,
2815 (op == JSOP_GETTER) ? js_getter_str : js_setter_str);
2816 goto error;
2820 * Getters and setters are just like watchpoints from an access control
2821 * point of view.
2823 scratch.setUndefined();
2824 unsigned attrs;
2825 if (!CheckAccess(cx, obj, id, JSACC_WATCH, &scratch, &attrs))
2826 goto error;
2828 PropertyOp getter;
2829 StrictPropertyOp setter;
2830 if (op == JSOP_GETTER) {
2831 getter = CastAsPropertyOp(&rval.toObject());
2832 setter = JS_StrictPropertyStub;
2833 attrs = JSPROP_GETTER;
2834 } else {
2835 getter = JS_PropertyStub;
2836 setter = CastAsStrictPropertyOp(&rval.toObject());
2837 attrs = JSPROP_SETTER;
2839 attrs |= JSPROP_ENUMERATE | JSPROP_SHARED;
2841 scratch.setUndefined();
2842 if (!JSObject::defineGeneric(cx, obj, id, scratch, getter, setter, attrs))
2843 goto error;
2845 regs.sp += i;
2846 if (js_CodeSpec[op2].ndefs > js_CodeSpec[op2].nuses) {
2847 JS_ASSERT(js_CodeSpec[op2].ndefs == js_CodeSpec[op2].nuses + 1);
2848 regs.sp[-1] = rval;
2849 assertSameCompartmentDebugOnly(cx, regs.sp[-1]);
2851 len = js_CodeSpec[op2].length;
2852 DO_NEXT_OP(len);
2855 BEGIN_CASE(JSOP_HOLE)
2856 PUSH_HOLE();
2857 END_CASE(JSOP_HOLE)
2859 BEGIN_CASE(JSOP_NEWINIT)
2861 uint8_t i = GET_UINT8(regs.pc);
2862 JS_ASSERT(i == JSProto_Array || i == JSProto_Object);
2864 RootedObject &obj = rootObject0;
2865 NewObjectKind newKind;
2866 if (i == JSProto_Array) {
2867 newKind = UseNewTypeForInitializer(cx, script, regs.pc, &ArrayClass);
2868 obj = NewDenseEmptyArray(cx, NULL, newKind);
2869 } else {
2870 gc::AllocKind allocKind = GuessObjectGCKind(0);
2871 newKind = UseNewTypeForInitializer(cx, script, regs.pc, &ObjectClass);
2872 obj = NewBuiltinClassInstance(cx, &ObjectClass, allocKind, newKind);
2874 if (!obj || !SetInitializerObjectType(cx, script, regs.pc, obj, newKind))
2875 goto error;
2877 PUSH_OBJECT(*obj);
2878 TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
2880 END_CASE(JSOP_NEWINIT)
2882 BEGIN_CASE(JSOP_NEWARRAY)
2884 unsigned count = GET_UINT24(regs.pc);
2885 RootedObject &obj = rootObject0;
2886 NewObjectKind newKind = UseNewTypeForInitializer(cx, script, regs.pc, &ArrayClass);
2887 obj = NewDenseAllocatedArray(cx, count, NULL, newKind);
2888 if (!obj || !SetInitializerObjectType(cx, script, regs.pc, obj, newKind))
2889 goto error;
2891 PUSH_OBJECT(*obj);
2892 TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
2894 END_CASE(JSOP_NEWARRAY)
2896 BEGIN_CASE(JSOP_NEWOBJECT)
2898 RootedObject &baseobj = rootObject0;
2899 baseobj = script->getObject(regs.pc);
2901 RootedObject &obj = rootObject1;
2902 NewObjectKind newKind = UseNewTypeForInitializer(cx, script, regs.pc, baseobj->getClass());
2903 obj = CopyInitializerObject(cx, baseobj, newKind);
2904 if (!obj || !SetInitializerObjectType(cx, script, regs.pc, obj, newKind))
2905 goto error;
2907 PUSH_OBJECT(*obj);
2908 TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
2910 END_CASE(JSOP_NEWOBJECT)
2912 BEGIN_CASE(JSOP_ENDINIT)
2914 /* FIXME remove JSOP_ENDINIT bug 588522 */
2915 JS_ASSERT(regs.stackDepth() >= 1);
2916 JS_ASSERT(regs.sp[-1].isObject() || regs.sp[-1].isUndefined());
2918 END_CASE(JSOP_ENDINIT)
2920 BEGIN_CASE(JSOP_INITPROP)
2922 /* Load the property's initial value into rval. */
2923 JS_ASSERT(regs.stackDepth() >= 2);
2924 RootedValue &rval = rootValue0;
2925 rval = regs.sp[-1];
2927 /* Load the object being initialized into lval/obj. */
2928 RootedObject &obj = rootObject0;
2929 obj = &regs.sp[-2].toObject();
2930 JS_ASSERT(obj->isObject());
2932 PropertyName *name = script->getName(regs.pc);
2934 RootedId &id = rootId0;
2935 id = NameToId(name);
2937 if (JS_UNLIKELY(name == cx->names().proto)
2938 ? !baseops::SetPropertyHelper(cx, obj, obj, id, 0, &rval, script->strict)
2939 : !DefineNativeProperty(cx, obj, id, rval, NULL, NULL,
2940 JSPROP_ENUMERATE, 0, 0, 0)) {
2941 goto error;
2944 regs.sp--;
2946 END_CASE(JSOP_INITPROP);
2948 BEGIN_CASE(JSOP_INITELEM)
2950 JS_ASSERT(regs.stackDepth() >= 3);
2951 HandleValue val = HandleValue::fromMarkedLocation(&regs.sp[-1]);
2952 MutableHandleValue id = MutableHandleValue::fromMarkedLocation(&regs.sp[-2]);
2954 RootedObject &obj = rootObject0;
2955 obj = &regs.sp[-3].toObject();
2957 if (!InitElemOperation(cx, obj, id, val))
2958 goto error;
2960 regs.sp -= 2;
2962 END_CASE(JSOP_INITELEM)
2964 BEGIN_CASE(JSOP_INITELEM_ARRAY)
2966 JS_ASSERT(regs.stackDepth() >= 2);
2967 HandleValue val = HandleValue::fromMarkedLocation(&regs.sp[-1]);
2969 RootedObject &obj = rootObject0;
2970 obj = &regs.sp[-2].toObject();
2972 JS_ASSERT(obj->isArray());
2974 uint32_t index = GET_UINT24(regs.pc);
2975 if (!InitArrayElemOperation(cx, regs.pc, obj, index, val))
2976 goto error;
2978 regs.sp--;
2980 END_CASE(JSOP_INITELEM_ARRAY)
2982 BEGIN_CASE(JSOP_INITELEM_INC)
2984 JS_ASSERT(regs.stackDepth() >= 3);
2985 HandleValue val = HandleValue::fromMarkedLocation(&regs.sp[-1]);
2987 RootedObject &obj = rootObject0;
2988 obj = &regs.sp[-3].toObject();
2990 uint32_t index = regs.sp[-2].toInt32();
2991 if (!InitArrayElemOperation(cx, regs.pc, obj, index, val))
2992 goto error;
2994 regs.sp[-2].setInt32(index + 1);
2995 regs.sp--;
2997 END_CASE(JSOP_INITELEM_INC)
2999 BEGIN_CASE(JSOP_SPREAD)
3001 int32_t count = regs.sp[-2].toInt32();
3002 RootedObject &arr = rootObject0;
3003 arr = &regs.sp[-3].toObject();
3004 const Value iterable = regs.sp[-1];
3005 ForOfIterator iter(cx, iterable);
3006 RootedValue &iterVal = rootValue0;
3007 while (iter.next()) {
3008 if (count == INT32_MAX) {
3009 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3010 JSMSG_SPREAD_TOO_LARGE);
3011 goto error;
3013 iterVal = iter.value();
3014 if (!JSObject::defineElement(cx, arr, count++, iterVal, NULL, NULL, JSPROP_ENUMERATE))
3015 goto error;
3017 if (!iter.close())
3018 goto error;
3019 regs.sp[-2].setInt32(count);
3020 regs.sp--;
3022 END_CASE(JSOP_SPREAD)
3025 BEGIN_CASE(JSOP_GOSUB)
3026 PUSH_BOOLEAN(false);
3027 int32_t i = (regs.pc - script->code) + JSOP_GOSUB_LENGTH;
3028 len = GET_JUMP_OFFSET(regs.pc);
3029 PUSH_INT32(i);
3030 END_VARLEN_CASE
3034 BEGIN_CASE(JSOP_RETSUB)
3035 /* Pop [exception or hole, retsub pc-index]. */
3036 Value rval, lval;
3037 POP_COPY_TO(rval);
3038 POP_COPY_TO(lval);
3039 JS_ASSERT(lval.isBoolean());
3040 if (lval.toBoolean()) {
3042 * Exception was pending during finally, throw it *before* we adjust
3043 * pc, because pc indexes into script->trynotes. This turns out not to
3044 * be necessary, but it seems clearer. And it points out a FIXME:
3045 * 350509, due to Igor Bukanov.
3047 cx->setPendingException(rval);
3048 goto error;
3050 JS_ASSERT(rval.isInt32());
3052 /* Increment the PC by this much. */
3053 len = rval.toInt32() - int32_t(regs.pc - script->code);
3054 END_VARLEN_CASE
3057 BEGIN_CASE(JSOP_EXCEPTION)
3059 PUSH_NULL();
3060 MutableHandleValue res = MutableHandleValue::fromMarkedLocation(&regs.sp[-1]);
3061 if (!GetAndClearException(cx, res))
3062 goto error;
3064 END_CASE(JSOP_EXCEPTION)
3066 BEGIN_CASE(JSOP_FINALLY)
3067 CHECK_BRANCH();
3068 END_CASE(JSOP_FINALLY)
3070 BEGIN_CASE(JSOP_THROWING)
3072 JS_ASSERT(!cx->isExceptionPending());
3073 Value v;
3074 POP_COPY_TO(v);
3075 cx->setPendingException(v);
3077 END_CASE(JSOP_THROWING)
3079 BEGIN_CASE(JSOP_THROW)
3081 CHECK_BRANCH();
3082 RootedValue &v = rootValue0;
3083 POP_COPY_TO(v);
3084 JS_ALWAYS_FALSE(Throw(cx, v));
3085 /* let the code at error try to catch the exception. */
3086 goto error;
3089 BEGIN_CASE(JSOP_INSTANCEOF)
3091 RootedValue &rref = rootValue0;
3092 rref = regs.sp[-1];
3093 if (rref.isPrimitive()) {
3094 js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, -1, rref, NullPtr());
3095 goto error;
3097 RootedObject &obj = rootObject0;
3098 obj = &rref.toObject();
3099 JSBool cond = JS_FALSE;
3100 if (!HasInstance(cx, obj, HandleValue::fromMarkedLocation(&regs.sp[-2]), &cond))
3101 goto error;
3102 regs.sp--;
3103 regs.sp[-1].setBoolean(cond);
3105 END_CASE(JSOP_INSTANCEOF)
3107 BEGIN_CASE(JSOP_DEBUGGER)
3109 JSTrapStatus st = JSTRAP_CONTINUE;
3110 RootedValue rval(cx);
3111 if (JSDebuggerHandler handler = cx->runtime->debugHooks.debuggerHandler)
3112 st = handler(cx, script, regs.pc, rval.address(), cx->runtime->debugHooks.debuggerHandlerData);
3113 if (st == JSTRAP_CONTINUE)
3114 st = Debugger::onDebuggerStatement(cx, &rval);
3115 switch (st) {
3116 case JSTRAP_ERROR:
3117 goto error;
3118 case JSTRAP_CONTINUE:
3119 break;
3120 case JSTRAP_RETURN:
3121 regs.fp()->setReturnValue(rval);
3122 interpReturnOK = true;
3123 goto forced_return;
3124 case JSTRAP_THROW:
3125 cx->setPendingException(rval);
3126 goto error;
3127 default:;
3130 END_CASE(JSOP_DEBUGGER)
3132 BEGIN_CASE(JSOP_ENTERBLOCK)
3133 BEGIN_CASE(JSOP_ENTERLET0)
3134 BEGIN_CASE(JSOP_ENTERLET1)
3136 StaticBlockObject &blockObj = script->getObject(regs.pc)->asStaticBlock();
3138 if (op == JSOP_ENTERBLOCK) {
3139 JS_ASSERT(regs.stackDepth() == blockObj.stackDepth());
3140 JS_ASSERT(regs.stackDepth() + blockObj.slotCount() <= script->nslots);
3141 Value *vp = regs.sp + blockObj.slotCount();
3142 SetValueRangeToUndefined(regs.sp, vp);
3143 regs.sp = vp;
3146 /* Clone block iff there are any closed-over variables. */
3147 if (!regs.fp()->pushBlock(cx, blockObj))
3148 goto error;
3150 END_CASE(JSOP_ENTERBLOCK)
3152 BEGIN_CASE(JSOP_LEAVEBLOCK)
3153 BEGIN_CASE(JSOP_LEAVEFORLETIN)
3154 BEGIN_CASE(JSOP_LEAVEBLOCKEXPR)
3156 blockDepth = regs.fp()->blockChain().stackDepth();
3158 regs.fp()->popBlock(cx);
3160 if (op == JSOP_LEAVEBLOCK) {
3161 /* Pop the block's slots. */
3162 regs.sp -= GET_UINT16(regs.pc);
3163 JS_ASSERT(regs.stackDepth() == blockDepth);
3164 } else if (op == JSOP_LEAVEBLOCKEXPR) {
3165 /* Pop the block's slots maintaining the topmost expr. */
3166 Value *vp = &regs.sp[-1];
3167 regs.sp -= GET_UINT16(regs.pc);
3168 JS_ASSERT(regs.stackDepth() == blockDepth + 1);
3169 regs.sp[-1] = *vp;
3170 } else {
3171 /* Another op will pop; nothing to do here. */
3172 len = JSOP_LEAVEFORLETIN_LENGTH;
3173 DO_NEXT_OP(len);
3176 END_CASE(JSOP_LEAVEBLOCK)
3178 #if JS_HAS_GENERATORS
3179 BEGIN_CASE(JSOP_GENERATOR)
3181 JS_ASSERT(!cx->isExceptionPending());
3182 regs.fp()->initGeneratorFrame();
3183 regs.pc += JSOP_GENERATOR_LENGTH;
3184 JSObject *obj = js_NewGenerator(cx);
3185 if (!obj)
3186 goto error;
3187 regs.fp()->setReturnValue(ObjectValue(*obj));
3188 regs.fp()->setYielding();
3189 interpReturnOK = true;
3190 if (entryFrame != regs.fp())
3191 goto inline_return;
3192 goto exit;
3195 BEGIN_CASE(JSOP_YIELD)
3196 JS_ASSERT(!cx->isExceptionPending());
3197 JS_ASSERT(regs.fp()->isNonEvalFunctionFrame());
3198 if (cx->innermostGenerator()->state == JSGEN_CLOSING) {
3199 RootedValue &val = rootValue0;
3200 val.setObject(regs.fp()->callee());
3201 js_ReportValueError(cx, JSMSG_BAD_GENERATOR_YIELD, JSDVG_SEARCH_STACK, val, NullPtr());
3202 goto error;
3204 regs.fp()->setReturnValue(regs.sp[-1]);
3205 regs.fp()->setYielding();
3206 regs.pc += JSOP_YIELD_LENGTH;
3207 interpReturnOK = true;
3208 goto exit;
3210 BEGIN_CASE(JSOP_ARRAYPUSH)
3212 uint32_t slot = GET_UINT16(regs.pc);
3213 JS_ASSERT(script->nfixed <= slot);
3214 JS_ASSERT(slot < script->nslots);
3215 RootedObject &obj = rootObject0;
3216 obj = &regs.fp()->unaliasedLocal(slot).toObject();
3217 if (!js_NewbornArrayPush(cx, obj, regs.sp[-1]))
3218 goto error;
3219 regs.sp--;
3221 END_CASE(JSOP_ARRAYPUSH)
3222 #endif /* JS_HAS_GENERATORS */
3224 default:
3226 char numBuf[12];
3227 JS_snprintf(numBuf, sizeof numBuf, "%d", op);
3228 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3229 JSMSG_BAD_BYTECODE, numBuf);
3230 goto error;
3233 } /* switch (op) */
3234 } /* for (;;) */
3236 error:
3237 JS_ASSERT(&cx->regs() == &regs);
3238 JS_ASSERT(uint32_t(regs.pc - script->code) < script->length);
3240 /* When rejoining, we must not err before finishing Interpret's prologue. */
3241 JS_ASSERT(interpMode != JSINTERP_REJOIN);
3243 if (cx->isExceptionPending()) {
3244 /* Call debugger throw hooks. */
3245 if (cx->compartment->debugMode()) {
3246 JSTrapStatus status = DebugExceptionUnwind(cx, regs.fp(), regs.pc);
3247 switch (status) {
3248 case JSTRAP_ERROR:
3249 goto error;
3251 case JSTRAP_CONTINUE:
3252 case JSTRAP_THROW:
3253 break;
3255 case JSTRAP_RETURN:
3256 interpReturnOK = true;
3257 goto forced_return;
3259 default:
3260 JS_NOT_REACHED("Invalid trap status");
3264 for (TryNoteIter tni(cx, regs); !tni.done(); ++tni) {
3265 JSTryNote *tn = *tni;
3267 UnwindScope(cx, cx->fp(), tn->stackDepth);
3270 * Set pc to the first bytecode after the the try note to point
3271 * to the beginning of catch or finally or to [enditer] closing
3272 * the for-in loop.
3274 regs.pc = (script)->main() + tn->start + tn->length;
3275 regs.sp = regs.spForStackDepth(tn->stackDepth);
3277 switch (tn->kind) {
3278 case JSTRY_CATCH:
3279 JS_ASSERT(*regs.pc == JSOP_ENTERBLOCK);
3281 #if JS_HAS_GENERATORS
3282 /* Catch cannot intercept the closing of a generator. */
3283 if (JS_UNLIKELY(cx->getPendingException().isMagic(JS_GENERATOR_CLOSING)))
3284 break;
3285 #endif
3288 * Don't clear exceptions to save cx->exception from GC
3289 * until it is pushed to the stack via [exception] in the
3290 * catch block.
3292 len = 0;
3293 DO_NEXT_OP(len);
3295 case JSTRY_FINALLY:
3297 * Push (true, exception) pair for finally to indicate that
3298 * [retsub] should rethrow the exception.
3300 PUSH_BOOLEAN(true);
3301 PUSH_COPY(cx->getPendingException());
3302 cx->clearPendingException();
3303 len = 0;
3304 DO_NEXT_OP(len);
3306 case JSTRY_ITER: {
3307 /* This is similar to JSOP_ENDITER in the interpreter loop. */
3308 JS_ASSERT(JSOp(*regs.pc) == JSOP_ENDITER);
3309 RootedObject &obj = rootObject0;
3310 obj = &regs.sp[-1].toObject();
3311 bool ok = UnwindIteratorForException(cx, obj);
3312 regs.sp -= 1;
3313 if (!ok)
3314 goto error;
3320 * Propagate the exception or error to the caller unless the exception
3321 * is an asynchronous return from a generator.
3323 interpReturnOK = false;
3324 #if JS_HAS_GENERATORS
3325 if (JS_UNLIKELY(cx->isExceptionPending() &&
3326 cx->getPendingException().isMagic(JS_GENERATOR_CLOSING))) {
3327 cx->clearPendingException();
3328 interpReturnOK = true;
3329 regs.fp()->clearReturnValue();
3331 #endif
3332 } else {
3333 UnwindForUncatchableException(cx, regs);
3334 interpReturnOK = false;
3337 forced_return:
3338 UnwindScope(cx, cx->fp(), 0);
3339 regs.setToEndOfScript();
3341 if (entryFrame != regs.fp())
3342 goto inline_return;
3344 exit:
3345 if (cx->compartment->debugMode())
3346 interpReturnOK = ScriptDebugEpilogue(cx, regs.fp(), interpReturnOK);
3347 if (!regs.fp()->isYielding())
3348 regs.fp()->epilogue(cx);
3349 else
3350 Probes::exitScript(cx, script, script->function(), regs.fp());
3351 regs.fp()->setFinishedInInterpreter();
3353 gc::MaybeVerifyBarriers(cx, true);
3355 #ifdef JS_METHODJIT
3357 * This path is used when it's guaranteed the method can be finished
3358 * inside the JIT.
3360 leave_on_safe_point:
3361 #endif
3363 return interpReturnOK ? Interpret_Ok : Interpret_Error;
3366 bool
3367 js::Throw(JSContext *cx, HandleValue v)
3369 JS_ASSERT(!cx->isExceptionPending());
3370 cx->setPendingException(v);
3371 return false;
3374 bool
3375 js::GetProperty(JSContext *cx, HandleValue v, HandlePropertyName name, MutableHandleValue vp)
3377 if (name == cx->names().length) {
3378 // Fast path for strings, arrays and arguments.
3379 if (GetLengthProperty(v, vp))
3380 return true;
3383 RootedObject obj(cx, ToObjectFromStack(cx, v));
3384 if (!obj)
3385 return false;
3386 return JSObject::getProperty(cx, obj, obj, name, vp);
3389 bool
3390 js::GetScopeName(JSContext *cx, HandleObject scopeChain, HandlePropertyName name, MutableHandleValue vp)
3392 RootedShape shape(cx);
3393 RootedObject obj(cx), pobj(cx);
3394 if (!LookupName(cx, name, scopeChain, &obj, &pobj, &shape))
3395 return false;
3397 if (!shape) {
3398 JSAutoByteString printable;
3399 if (js_AtomToPrintableString(cx, name, &printable))
3400 js_ReportIsNotDefined(cx, printable.ptr());
3401 return false;
3404 return JSObject::getProperty(cx, obj, obj, name, vp);
3408 * Alternate form for NAME opcodes followed immediately by a TYPEOF,
3409 * which do not report an exception on (typeof foo == "undefined") tests.
3411 bool
3412 js::GetScopeNameForTypeOf(JSContext *cx, HandleObject scopeChain, HandlePropertyName name,
3413 MutableHandleValue vp)
3415 RootedShape shape(cx);
3416 RootedObject obj(cx), pobj(cx);
3417 if (!LookupName(cx, name, scopeChain, &obj, &pobj, &shape))
3418 return false;
3420 if (!shape) {
3421 vp.set(UndefinedValue());
3422 return true;
3425 return JSObject::getProperty(cx, obj, obj, name, vp);
3428 JSObject *
3429 js::Lambda(JSContext *cx, HandleFunction fun, HandleObject parent)
3431 RootedObject clone(cx, CloneFunctionObjectIfNotSingleton(cx, fun, parent));
3432 if (!clone)
3433 return NULL;
3435 if (fun->isArrow()) {
3436 StackFrame *fp = cx->fp();
3438 // Note that this will assert if called from Ion code. Ion can't yet
3439 // emit code for a bound arrow function (bug 851913).
3440 if (!ComputeThis(cx, fp))
3441 return NULL;
3443 RootedValue thisval(cx, fp->thisValue());
3444 clone = js_fun_bind(cx, clone, thisval, NULL, 0);
3445 if (!clone)
3446 return NULL;
3447 clone->toFunction()->flags |= JSFunction::ARROW;
3450 JS_ASSERT(clone->global() == clone->global());
3451 return clone;
3454 bool
3455 js::DefFunOperation(JSContext *cx, HandleScript script, HandleObject scopeChain,
3456 HandleFunction funArg)
3459 * If static link is not current scope, clone fun's object to link to the
3460 * current scope via parent. We do this to enable sharing of compiled
3461 * functions among multiple equivalent scopes, amortizing the cost of
3462 * compilation over a number of executions. Examples include XUL scripts
3463 * and event handlers shared among Firefox or other Mozilla app chrome
3464 * windows, and user-defined JS functions precompiled and then shared among
3465 * requests in server-side JS.
3467 RootedFunction fun(cx, funArg);
3468 if (fun->isNative() || fun->environment() != scopeChain) {
3469 fun = CloneFunctionObjectIfNotSingleton(cx, fun, scopeChain);
3470 if (!fun)
3471 return false;
3472 } else {
3473 JS_ASSERT(script->compileAndGo);
3474 JS_ASSERT_IF(!cx->fp()->beginsIonActivation(),
3475 cx->fp()->isGlobalFrame() || cx->fp()->isEvalInFunction());
3479 * We define the function as a property of the variable object and not the
3480 * current scope chain even for the case of function expression statements
3481 * and functions defined by eval inside let or with blocks.
3483 RootedObject parent(cx, scopeChain);
3484 while (!parent->isVarObj())
3485 parent = parent->enclosingScope();
3487 /* ES5 10.5 (NB: with subsequent errata). */
3488 RootedPropertyName name(cx, fun->atom()->asPropertyName());
3490 RootedShape shape(cx);
3491 RootedObject pobj(cx);
3492 if (!JSObject::lookupProperty(cx, parent, name, &pobj, &shape))
3493 return false;
3495 RootedValue rval(cx, ObjectValue(*fun));
3498 * ECMA requires functions defined when entering Eval code to be
3499 * impermanent.
3501 unsigned attrs = script->isActiveEval
3502 ? JSPROP_ENUMERATE
3503 : JSPROP_ENUMERATE | JSPROP_PERMANENT;
3505 /* Steps 5d, 5f. */
3506 if (!shape || pobj != parent) {
3507 return JSObject::defineProperty(cx, parent, name, rval, JS_PropertyStub,
3508 JS_StrictPropertyStub, attrs);
3511 /* Step 5e. */
3512 JS_ASSERT(parent->isNative());
3513 if (parent->isGlobal()) {
3514 if (shape->configurable()) {
3515 return JSObject::defineProperty(cx, parent, name, rval, JS_PropertyStub,
3516 JS_StrictPropertyStub, attrs);
3519 if (shape->isAccessorDescriptor() || !shape->writable() || !shape->enumerable()) {
3520 JSAutoByteString bytes;
3521 if (js_AtomToPrintableString(cx, name, &bytes)) {
3522 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_REDEFINE_PROP,
3523 bytes.ptr());
3526 return false;
3531 * Non-global properties, and global properties which we aren't simply
3532 * redefining, must be set. First, this preserves their attributes.
3533 * Second, this will produce warnings and/or errors as necessary if the
3534 * specified Call object property is not writable (const).
3537 /* Step 5f. */
3538 return JSObject::setProperty(cx, parent, parent, name, &rval, script->strict);
3541 bool
3542 js::GetAndClearException(JSContext *cx, MutableHandleValue res)
3544 // Check the interrupt flag to allow interrupting deeply nested exception
3545 // handling.
3546 if (cx->runtime->interrupt && !js_HandleExecutionInterrupt(cx))
3547 return false;
3549 res.set(cx->getPendingException());
3550 cx->clearPendingException();
3551 return true;
3554 template <bool strict>
3555 bool
3556 js::SetProperty(JSContext *cx, HandleObject obj, HandleId id, const Value &value)
3558 RootedValue v(cx, value);
3559 return JSObject::setGeneric(cx, obj, obj, id, &v, strict);
3562 template bool js::SetProperty<true> (JSContext *cx, HandleObject obj, HandleId id, const Value &value);
3563 template bool js::SetProperty<false>(JSContext *cx, HandleObject obj, HandleId id, const Value &value);
3565 template <bool strict>
3566 bool
3567 js::DeleteProperty(JSContext *cx, HandleValue v, HandlePropertyName name, JSBool *bp)
3569 // default op result is false (failure)
3570 *bp = true;
3572 // convert value to JSObject pointer
3573 RootedObject obj(cx, ToObjectFromStack(cx, v));
3574 if (!obj)
3575 return false;
3577 // Call deleteProperty on obj
3578 RootedValue result(cx, NullValue());
3579 bool delprop_ok = JSObject::deleteProperty(cx, obj, name, &result, strict);
3580 if (!delprop_ok)
3581 return false;
3583 // convert result into *bp and return
3584 *bp = result.toBoolean();
3585 return true;
3588 template bool js::DeleteProperty<true> (JSContext *cx, HandleValue val, HandlePropertyName name, JSBool *bp);
3589 template bool js::DeleteProperty<false>(JSContext *cx, HandleValue val, HandlePropertyName name, JSBool *bp);
3591 bool
3592 js::GetElement(JSContext *cx, MutableHandleValue lref, HandleValue rref, MutableHandleValue vp)
3594 return GetElementOperation(cx, JSOP_GETELEM, lref, rref, vp);
3597 bool
3598 js::GetElementMonitored(JSContext *cx, MutableHandleValue lref, HandleValue rref,
3599 MutableHandleValue vp)
3601 if (!GetElement(cx, lref, rref, vp))
3602 return false;
3604 TypeScript::Monitor(cx, vp);
3605 return true;
3608 bool
3609 js::CallElement(JSContext *cx, MutableHandleValue lref, HandleValue rref, MutableHandleValue res)
3611 return GetElementOperation(cx, JSOP_CALLELEM, lref, rref, res);
3614 bool
3615 js::SetObjectElement(JSContext *cx, HandleObject obj, HandleValue index, HandleValue value,
3616 JSBool strict)
3618 RootedId id(cx);
3619 RootedValue indexval(cx, index);
3620 if (!FetchElementId(cx, obj, indexval, &id, &indexval))
3621 return false;
3622 return SetObjectElementOperation(cx, obj, id, value, strict);
3625 bool
3626 js::SetObjectElement(JSContext *cx, HandleObject obj, HandleValue index, HandleValue value,
3627 JSBool strict, HandleScript script, jsbytecode *pc)
3629 JS_ASSERT(pc);
3630 RootedId id(cx);
3631 RootedValue indexval(cx, index);
3632 if (!FetchElementId(cx, obj, indexval, &id, &indexval))
3633 return false;
3634 return SetObjectElementOperation(cx, obj, id, value, strict, script, pc);
3637 bool
3638 js::AddValues(JSContext *cx, HandleScript script, jsbytecode *pc,
3639 MutableHandleValue lhs, MutableHandleValue rhs,
3640 Value *res)
3642 return AddOperation(cx, script, pc, lhs, rhs, res);
3645 bool
3646 js::SubValues(JSContext *cx, HandleScript script, jsbytecode *pc,
3647 MutableHandleValue lhs, MutableHandleValue rhs,
3648 Value *res)
3650 return SubOperation(cx, script, pc, lhs, rhs, res);
3653 bool
3654 js::MulValues(JSContext *cx, HandleScript script, jsbytecode *pc,
3655 MutableHandleValue lhs, MutableHandleValue rhs,
3656 Value *res)
3658 return MulOperation(cx, script, pc, lhs, rhs, res);
3661 bool
3662 js::DivValues(JSContext *cx, HandleScript script, jsbytecode *pc,
3663 MutableHandleValue lhs, MutableHandleValue rhs,
3664 Value *res)
3666 return DivOperation(cx, script, pc, lhs, rhs, res);
3669 bool
3670 js::ModValues(JSContext *cx, HandleScript script, jsbytecode *pc,
3671 MutableHandleValue lhs, MutableHandleValue rhs,
3672 Value *res)
3674 return ModOperation(cx, script, pc, lhs, rhs, res);
3677 bool
3678 js::UrshValues(JSContext *cx, HandleScript script, jsbytecode *pc,
3679 MutableHandleValue lhs, MutableHandleValue rhs,
3680 Value *res)
3682 return UrshOperation(cx, script, pc, lhs, rhs, res);