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/. */
9 * JavaScript bytecode interpreter.
12 #include "mozilla/DebugOnly.h"
13 #include "mozilla/FloatingPoint.h"
27 #include "jsversion.h"
33 #include "jslibmath.h"
38 #include "jspropertycache.h"
42 #include "builtin/Eval.h"
43 #include "gc/Marking.h"
44 #include "ion/AsmJS.h"
45 #include "vm/Debugger.h"
49 #include "methodjit/MethodJIT.h"
50 #include "methodjit/Logging.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"
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"
77 #include "TraceLogging.h"
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
);
93 argv
[-1].setObject(*thisp
);
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());
107 if (thisv
.isNullOrUndefined()) {
108 Rooted
<GlobalObject
*> global(cx
, cx
->global());
109 JSObject
*thisp
= JSObject::thisObject(cx
, global
);
112 thisv
.set(ObjectValue(*thisp
));
117 if (!thisv
.isObject()) {
118 if (!js_PrimitiveToObject(cx
, thisv
.address()))
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:
132 * function f() { return this }
133 * function g() { return f }
139 * The alert should display "true".
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());
152 JSFunction
*fun
= call
.callee().isFunction() ? call
.callee().toFunction() : NULL
;
153 JS_ASSERT_IF(fun
&& fun
->isInterpreted(), !fun
->strict());
157 if (!BoxNonStrictThis(cx
, &thisv
, &modified
))
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
= {
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
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
))
200 TypeScript::MonitorUnknown(cx
);
202 if (value
.get().isPrimitive()) {
205 JSObject
*obj
= NewObjectWithClassProto(cx
, &js_NoSuchMethodClass
, NULL
, NULL
);
209 obj
->setSlot(JSSLOT_FOUND_FUNCTION
, value
);
210 obj
->setSlot(JSSLOT_SAVED_ID
, idval
);
217 NoSuchMethod(JSContext
*cx
, unsigned argc
, Value
*vp
)
219 InvokeArgsGuard args
;
220 if (!cx
->stack
.pushInvokeArgs(cx
, 2, &args
))
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
));
230 args
[0] = obj
->getSlot(JSSLOT_SAVED_ID
);
231 JSObject
*argsobj
= NewDenseCopiedArray(cx
, argc
, vp
+ 2);
234 args
[1].setObject(*argsobj
);
235 JSBool ok
= Invoke(cx
, args
);
240 #endif /* JS_HAS_NO_SUCH_METHOD */
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
);
254 js::ValueToCallable(JSContext
*cx
, const Value
&v
, int numToSkip
, MaybeConstruct construct
)
257 JSObject
*callable
= &v
.toObject();
258 if (callable
->isCallable())
262 ReportIsNotFunction(cx
, v
, numToSkip
, construct
);
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
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())
287 if (iter
.isScript()) {
288 RawScript script
= iter
.script();
289 jsbytecode
*pc
= iter
.pc();
290 if (UseNewType(cx
, script
, pc
))
297 struct CheckStackBalance
{
300 CheckStackBalance(JSContext
*cx
)
301 : cx(cx
), fp(cx
->fp())
303 ~CheckStackBalance() {
304 JS_ASSERT(fp
== cx
->fp());
309 SPSEntryMarker
marker(cx
->runtime
);
312 if (ion::IsEnabled(cx
)) {
313 ion::MethodStatus status
= ion::CanEnter(cx
, script
, AbstractFramePtr(fp
),
314 fp
->isConstructing());
315 if (status
== ion::Method_Error
)
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
);
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
)
337 if (status
== mjit::Compile_Okay
)
338 return mjit::JaegerStatusToSuccess(mjit::JaegerShot(cx
, false));
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.
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.");
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());
380 JS_ASSERT_IF(construct
, !clasp
->construct
);
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());
390 return CallJSNative(cx
, fun
->native(), args
);
392 if (!fun
->getOrCreateScript(cx
))
395 if (!TypeMonitorCall(cx
, args
, construct
))
398 /* Get pointer to new frame/slots, prepare arguments. */
399 InvokeFrameGuard ifg
;
400 if (!cx
->stack
.pushInvokeFrame(cx
, args
, initial
, &ifg
))
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());
413 js::Invoke(JSContext
*cx
, const Value
&thisv
, const Value
&fval
, unsigned argc
, Value
*argv
,
416 InvokeArgsGuard args
;
417 if (!cx
->stack
.pushInvokeArgs(cx
, argc
, &args
))
420 args
.setCallee(fval
);
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
430 RootedObject
thisObj(cx
, &args
.thisv().toObject());
431 JSObject
*thisp
= JSObject::thisObject(cx
, thisObj
);
434 args
.setThis(ObjectValue(*thisp
));
437 if (!Invoke(cx
, args
))
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
);
463 if (!fun
->isInterpretedConstructor())
464 return ReportIsNotFunction(cx
, args
.calleev().get(), args
.length() + 1, CONSTRUCT
);
466 if (!InvokeKernel(cx
, args
, CONSTRUCT
))
469 JS_ASSERT(args
.rval().isObject());
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
);
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
))
487 args
.setCallee(fval
);
488 args
.setThis(MagicValue(JS_THIS_POISON
));
489 PodCopy(args
.array(), argv
, argc
);
491 if (!InvokeConstructor(cx
, args
))
499 js::InvokeGetterOrSetter(JSContext
*cx
, JSObject
*obj
, const Value
&fval
, unsigned argc
, Value
*argv
,
503 * Invoke could result in another try to get or set the same id again, see
506 JS_CHECK_RECURSION(cx
, return false);
508 return Invoke(cx
, ObjectValue(*obj
), fval
, argc
, argv
, rval
);
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()) {
522 result
->setUndefined();
526 ExecuteFrameGuard efg
;
527 if (!cx
->stack
.pushExecuteFrame(cx
, script
, thisv
, scopeChain
, type
, evalInFrame
, &efg
))
530 if (!script
->ensureRanAnalysis(cx
))
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. */
540 *result
= efg
.fp()->returnValue();
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
);
553 /* Ensure the scope chain is all same-compartment and terminates in a global. */
555 RawObject s
= scopeChain
;
557 assertSameCompartment(cx
, s
);
558 JS_ASSERT_IF(!s
->enclosingScope(), s
->isGlobal());
559 } while ((s
= s
->enclosingScope()));
562 /* The VAROBJFIX option makes varObj == globalObj in global code. */
563 if (!cx
->hasOption(JSOPTION_VAROBJFIX
)) {
564 if (!scopeChain
->setVarObj(cx
))
568 /* Use the scope chain as 'this', modulo outerization. */
569 JSObject
*thisObj
= JSObject::thisObject(cx
, scopeChain
);
572 Value thisv
= ObjectValue(*thisObj
);
574 return ExecuteKernel(cx
, script
, *scopeChain
, thisv
, EXECUTE_GLOBAL
,
575 NullFramePtr() /* evalInFrame */, rval
);
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());
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();
608 if (lval
.isObject()) {
609 JSObject
*l
= &lval
.toObject();
610 JSObject
*r
= &rval
.toObject();
615 *result
= lval
.payloadAsRawUint32() == rval
.payloadAsRawUint32();
619 if (lval
.isNullOrUndefined()) {
620 *result
= rval
.isNullOrUndefined() ||
621 (rval
.isObject() && EmulatesUndefined(&rval
.toObject()));
625 if (rval
.isNullOrUndefined()) {
626 *result
= (lval
.isObject() && EmulatesUndefined(&lval
.toObject()));
630 RootedValue
lvalue(cx
, lval
);
631 RootedValue
rvalue(cx
, rval
);
633 if (!ToPrimitive(cx
, &lvalue
))
635 if (!ToPrimitive(cx
, &rvalue
))
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
);
645 if (!ToNumber(cx
, lvalue
, &l
) || !ToNumber(cx
, rvalue
, &r
))
652 js::StrictlyEqual(JSContext
*cx
, const Value
&lref
, const Value
&rref
, bool *equal
)
654 Value lval
= lref
, rval
= rref
;
655 if (SameType(lval
, rval
)) {
657 return EqualStrings(cx
, lval
.toString(), rval
.toString(), equal
);
658 if (lval
.isDouble()) {
659 *equal
= (lval
.toDouble() == rval
.toDouble());
662 if (lval
.isObject()) {
663 *equal
= lval
.toObject() == rval
.toObject();
666 if (lval
.isUndefined()) {
670 *equal
= lval
.payloadAsRawUint32() == rval
.payloadAsRawUint32();
674 if (lval
.isDouble() && rval
.isInt32()) {
675 double ld
= lval
.toDouble();
676 double rd
= rval
.toInt32();
680 if (lval
.isInt32() && rval
.isDouble()) {
681 double ld
= lval
.toInt32();
682 double rd
= rval
.toDouble();
692 IsNegativeZero(const Value
&v
)
694 return v
.isDouble() && MOZ_DOUBLE_IS_NEGATIVE_ZERO(v
.toDouble());
698 IsNaN(const Value
&v
)
700 return v
.isDouble() && MOZ_DOUBLE_IS_NaN(v
.toDouble());
704 js::SameValue(JSContext
*cx
, const Value
&v1
, const Value
&v2
, bool *same
)
706 if (IsNegativeZero(v1
)) {
707 *same
= IsNegativeZero(v2
);
710 if (IsNegativeZero(v2
)) {
714 if (IsNaN(v1
) && IsNaN(v2
)) {
718 return StrictlyEqual(cx
, v1
, v2
, same
);
722 js::TypeOfValue(JSContext
*cx
, const Value
&vref
)
726 return JSTYPE_NUMBER
;
728 return JSTYPE_STRING
;
730 return JSTYPE_OBJECT
;
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.
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();
757 obj
= js_ValueToNonNullObject(cx
, sp
[-1]);
760 sp
[-1].setObject(*obj
);
763 WithObject
*withobj
= WithObject::create(cx
, obj
, fp
->scopeChain(),
764 cx
->regs().stackDepth() + stackIndex
);
768 fp
->pushOnScopeChain(*withobj
);
772 /* Unwind block and scope chains to match the given depth. */
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
) {
781 case ScopeIter::Block
:
782 if (si
.staticBlock().stackDepth() < stackDepth
)
786 case ScopeIter::With
:
787 if (si
.scope().asWith().stackDepth() < stackDepth
)
791 case ScopeIter::Call
:
792 case ScopeIter::StrictEvalScope
:
799 js::UnwindForUncatchableException(JSContext
*cx
, const FrameRegs
®s
)
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
®s
)
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
;
826 TryNoteIter::operator++()
833 TryNoteIter::done() const
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
)
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
865 if (tn
->stackDepth
<= regs
.stackDepth())
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) \
886 HandleValue val = HandleValue::fromMarkedLocation(®s.sp[n]); \
887 obj = ToObject(cx, (val)); \
893 class GenericInterruptEnabler
: public InterpreterFrames::InterruptEnablerBase
{
895 GenericInterruptEnabler(T
*variable
, T value
) : variable(variable
), value(value
) { }
896 void enable() const { *variable
= 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)
918 js::AssertValidPropertyCacheHit(JSContext
*cx
, JSObject
*start
,
919 JSObject
*found
, PropertyCacheEntry
*entry
)
922 JSScript
*script
= cx
->stack
.currentScript(&pc
);
924 uint64_t sample
= cx
->runtime
->gcNumber
;
926 PropertyName
*name
= GetNameFromBytecode(cx
, script
, pc
, JSOp(*pc
));
929 if (baseops::LookupProperty
<NoGC
>(cx
, start
, NameToId(name
), &pobj
, &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.
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
);
976 Rooted
<JSObject
*> iobj(cx
, iterobj
);
977 if (!js_IteratorMore(cx
, iobj
, rval
))
979 *cond
= rval
.isTrue();
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());
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.
1003 TypeCheckNextBytecode(JSContext
*cx
, HandleScript script
, unsigned n
, const FrameRegs
®s
)
1006 if (cx
->typeInferenceEnabled() && n
== GetBytecodeLength(regs
.pc
))
1007 TypeScript::CheckBytecode(cx
, script
, regs
.pc
, regs
.sp
);
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;
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); \
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())
1070 #define CHECK_PARTIAL_METHODJIT(status) \
1073 case mjit::Jaeger_UnfinishedAtTrap: \
1074 interpMode = JSINTERP_SKIP_TRAP; \
1076 case mjit::Jaeger_Unfinished: \
1077 op = (JSOp) *regs.pc; \
1078 SET_SCRIPT(regs.fp()->script()); \
1079 if (cx->isExceptionPending()) \
1088 * Prepare to call a user-supplied branch handler, and abort the script
1089 * if it returns false.
1091 #define CHECK_BRANCH() \
1093 if (cx->runtime->interrupt && !js_HandleExecutionInterrupt(cx)) \
1100 op = (JSOp) *regs.pc; \
1102 goto check_backedge; \
1106 #define SET_SCRIPT(s) \
1109 if (script->hasAnyBreakpointsOrStepMode() || script->hasScriptCounts) \
1110 interrupts.enable(); \
1111 JS_ASSERT_IF(interpMode == JSINTERP_SKIP_TRAP, \
1112 script->hasAnyBreakpointsOrStepMode()); \
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
1123 InterpreterFrames
interpreterFrame(cx
, ®s
, 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());
1131 /* Reset the loop count on the script we're entering. */
1132 script
->resetLoopCount();
1135 #if JS_TRACE_LOGGING
1136 AutoTraceLog
logger(TraceLogging::defaultLogger(),
1137 TraceLogging::INTERPRETER_START
,
1138 TraceLogging::INTERPRETER_STOP
,
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
;
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());
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
))
1189 Probes::enterScript(cx
, script
, script
->function(), fp
);
1191 if (cx
->compartment
->debugMode()) {
1192 JSTrapStatus status
= ScriptDebugPrologue(cx
, fp
);
1194 case JSTRAP_CONTINUE
:
1197 interpReturnOK
= true;
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
)
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.
1229 if (rt
->profilingScripts
|| cx
->runtime
->debugHooks
.interruptHook
)
1230 interrupts
.enable();
1236 JS_ASSERT(js_CodeSpec
[op
].length
== 1);
1239 TypeCheckNextBytecode(cx
, script
, len
, regs
);
1240 js::gc::MaybeVerifyBarriers(cx
);
1242 op
= (JSOp
) *regs
.pc
;
1245 CHECK_PCCOUNT_INTERRUPTS();
1246 switchOp
= int(op
) | switchMask
;
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
;
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
);
1278 case JSTRAP_CONTINUE
:
1281 regs
.fp()->setReturnValue(rval
);
1282 interpReturnOK
= true;
1285 cx
->setPendingException(rval
);
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
);
1302 regs
.fp()->setReturnValue(rval
);
1303 interpReturnOK
= true;
1306 cx
->setPendingException(rval
);
1311 JS_ASSERT(status
== JSTRAP_CONTINUE
);
1312 JS_ASSERT(rval
.isInt32() && rval
.toInt32() == op
);
1315 interpMode
= JSINTERP_NORMAL
;
1317 switchMask
= moreInterrupts
? -1 : 0;
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
)
1367 BEGIN_CASE(JSOP_LOOPHEAD
)
1370 script
->incrLoopCount();
1372 END_CASE(JSOP_LOOPHEAD
)
1374 BEGIN_CASE(JSOP_LABEL
)
1375 END_CASE(JSOP_LABEL
)
1380 if (op
!= JSOP_LOOPHEAD
)
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
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
)
1393 if (status
== mjit::Compile_Okay
) {
1395 script
->nativeCodeForPC(regs
.fp()->isConstructing(), regs
.pc
);
1397 mjit::JaegerStatus status
= mjit::JaegerShotAtSafePoint(cx
, ncode
, true);
1398 if (status
== mjit::Jaeger_ThrowBeforeEnter
)
1400 CHECK_PARTIAL_METHODJIT(status
);
1401 interpReturnOK
= (status
== mjit::Jaeger_Returned
);
1402 if (entryFrame
!= regs
.fp())
1404 regs
.fp()->setFinishedInInterpreter();
1405 goto leave_on_safe_point
;
1407 #endif /* JS_METHODJIT */
1412 BEGIN_CASE(JSOP_LOOPENTRY
)
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
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
)
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
);
1434 // We failed to call into Ion at all, so treat as an error.
1435 if (maybeOsr
== ion::IonExec_Aborted
)
1438 interpReturnOK
= (maybeOsr
== ion::IonExec_Ok
);
1440 if (entryFrame
!= regs
.fp())
1443 regs
.fp()->setFinishedInInterpreter();
1444 goto leave_on_safe_point
;
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
)
1460 END_CASE(JSOP_UNDEFINED
)
1462 BEGIN_CASE(JSOP_POP
)
1466 BEGIN_CASE(JSOP_POPN
)
1467 JS_ASSERT(GET_UINT16(regs
.pc
) <= regs
.stackDepth());
1468 regs
.sp
-= GET_UINT16(regs
.pc
);
1470 if (StaticBlockObject
*block
= regs
.fp()->maybeBlockChain())
1471 JS_ASSERT(regs
.stackDepth() >= block
->stackDepth() + block
->slotCount());
1475 BEGIN_CASE(JSOP_SETRVAL
)
1476 BEGIN_CASE(JSOP_POPV
)
1480 BEGIN_CASE(JSOP_ENTERWITH
)
1481 if (!EnterWith(cx
, -1))
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
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
);
1500 END_CASE(JSOP_LEAVEWITH
)
1502 BEGIN_CASE(JSOP_RETURN
)
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.
1515 interpReturnOK
= true;
1516 if (entryFrame
!= regs
.fp())
1519 if (cx
->compartment
->debugMode())
1520 interpReturnOK
= ScriptDebugEpilogue(cx
, regs
.fp(), interpReturnOK
);
1522 if (!regs
.fp()->isYielding())
1523 regs
.fp()->epilogue(cx
);
1525 Probes::exitScript(cx
, script
, script
->function(), regs
.fp());
1527 /* The JIT inlines the epilogue. */
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]);
1545 regs
.sp
[-2] = regs
.sp
[-1];
1549 len
= JSOP_CALL_LENGTH
;
1553 /* Increment pc so that |sp - fp->slots == ReconstructStackDepth(pc)|. */
1554 regs
.pc
+= JSOP_CALL_LENGTH
;
1557 JS_ASSERT(regs
.stackDepth() == 0);
1559 interpReturnOK
= true;
1563 BEGIN_CASE(JSOP_DEFAULT
)
1566 BEGIN_CASE(JSOP_GOTO
)
1568 len
= GET_JUMP_OFFSET(regs
.pc
);
1573 BEGIN_CASE(JSOP_IFEQ
)
1575 bool cond
= ToBoolean(regs
.sp
[-1]);
1577 if (cond
== false) {
1578 len
= GET_JUMP_OFFSET(regs
.pc
);
1584 BEGIN_CASE(JSOP_IFNE
)
1586 bool cond
= ToBoolean(regs
.sp
[-1]);
1588 if (cond
!= false) {
1589 len
= GET_JUMP_OFFSET(regs
.pc
);
1597 bool cond
= ToBoolean(regs
.sp
[-1]);
1599 len
= GET_JUMP_OFFSET(regs
.pc
);
1605 BEGIN_CASE(JSOP_AND
)
1607 bool cond
= ToBoolean(regs
.sp
[-1]);
1608 if (cond
== false) {
1609 len
= GET_JUMP_OFFSET(regs
.pc
);
1615 #define FETCH_ELEMENT_ID(obj, n, id) \
1617 const Value &idval_ = regs.sp[n]; \
1618 if (!ValueToId<CanGC>(cx, obj, idval_, &id)) \
1622 #define TRY_BRANCH_AFTER_COND(cond,spdec) \
1624 JS_ASSERT(js_CodeSpec[op].length == 1); \
1625 unsigned diff_ = (unsigned) GET_UINT8(regs.pc) - (unsigned) JSOP_IFEQ; \
1628 if (cond == (diff_ != 0)) { \
1630 len = GET_JUMP_OFFSET(regs.pc); \
1633 len = 1 + JSOP_IFEQ_LENGTH; \
1640 HandleValue rref
= HandleValue::fromMarkedLocation(®s
.sp
[-1]);
1641 if (!rref
.isObject()) {
1642 js_ReportValueError(cx
, JSMSG_IN_NOT_OBJECT
, -1, rref
, NullPtr());
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
))
1653 bool cond
= prop
!= NULL
;
1655 TRY_BRANCH_AFTER_COND(cond
, 2);
1657 regs
.sp
[-1].setBoolean(cond
);
1661 BEGIN_CASE(JSOP_ITER
)
1663 JS_ASSERT(regs
.stackDepth() >= 1);
1664 uint8_t flags
= GET_UINT8(regs
.pc
);
1665 MutableHandleValue res
= MutableHandleValue::fromMarkedLocation(®s
.sp
[-1]);
1666 if (!ValueToIterator(cx
, flags
, res
))
1668 JS_ASSERT(!res
.isPrimitive());
1672 BEGIN_CASE(JSOP_MOREITER
)
1674 JS_ASSERT(regs
.stackDepth() >= 1);
1675 JS_ASSERT(regs
.sp
[-1].isObject());
1678 MutableHandleValue res
= MutableHandleValue::fromMarkedLocation(®s
.sp
[-1]);
1679 if (!IteratorMore(cx
, ®s
.sp
[-2].toObject(), &cond
, res
))
1681 regs
.sp
[-1].setBoolean(cond
);
1683 END_CASE(JSOP_MOREITER
)
1685 BEGIN_CASE(JSOP_ITERNEXT
)
1687 JS_ASSERT(regs
.sp
[-1].isObject());
1689 MutableHandleValue res
= MutableHandleValue::fromMarkedLocation(®s
.sp
[-1]);
1690 RootedObject
&obj
= rootObject0
;
1691 obj
= ®s
.sp
[-2].toObject();
1692 if (!IteratorNext(cx
, obj
, res
))
1695 END_CASE(JSOP_ITERNEXT
)
1697 BEGIN_CASE(JSOP_ENDITER
)
1699 JS_ASSERT(regs
.stackDepth() >= 1);
1700 RootedObject
&obj
= rootObject0
;
1701 obj
= ®s
.sp
[-1].toObject();
1702 bool ok
= CloseIterator(cx
, obj
);
1707 END_CASE(JSOP_ENDITER
)
1709 BEGIN_CASE(JSOP_DUP
)
1711 JS_ASSERT(regs
.stackDepth() >= 1);
1712 const Value
&rref
= regs
.sp
[-1];
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];
1727 BEGIN_CASE(JSOP_SWAP
)
1729 JS_ASSERT(regs
.stackDepth() >= 2);
1730 Value
&lref
= regs
.sp
[-2];
1731 Value
&rref
= regs
.sp
[-1];
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
);
1746 BEGIN_CASE(JSOP_SETCONST
)
1748 RootedPropertyName
&name
= rootName0
;
1749 name
= script
->getName(regs
.pc
);
1751 RootedValue
&rval
= rootValue0
;
1754 RootedObject
&obj
= rootObject0
;
1755 obj
= ®s
.fp()->varObj();
1756 if (!JSObject::defineProperty(cx
, obj
, name
, rval
,
1757 JS_PropertyStub
, JS_StrictPropertyStub
,
1758 JSPROP_ENUMERATE
| JSPROP_PERMANENT
| JSPROP_READONLY
)) {
1762 END_CASE(JSOP_SETCONST
);
1764 #if JS_HAS_DESTRUCTURING
1765 BEGIN_CASE(JSOP_ENUMCONSTELEM
)
1767 RootedValue
&rval
= rootValue0
;
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
)) {
1781 END_CASE(JSOP_ENUMCONSTELEM
)
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
))
1805 PUSH_OBJECT(*scope
);
1807 END_CASE(JSOP_BINDNAME
)
1809 #define BITWISE_OP(OP) \
1812 if (!ToInt32(cx, regs.sp[-2], &i)) \
1814 if (!ToInt32(cx, regs.sp[-1], &j)) \
1818 regs.sp[-1].setInt32(i); \
1821 BEGIN_CASE(JSOP_BITOR
)
1823 END_CASE(JSOP_BITOR
)
1825 BEGIN_CASE(JSOP_BITXOR
)
1827 END_CASE(JSOP_BITXOR
)
1829 BEGIN_CASE(JSOP_BITAND
)
1831 END_CASE(JSOP_BITAND
)
1835 #define EQUALITY_OP(OP) \
1837 Value rval = regs.sp[-1]; \
1838 Value lval = regs.sp[-2]; \
1840 if (!LooselyEqual(cx, lval, rval, &cond)) \
1842 cond = cond OP JS_TRUE; \
1843 TRY_BRANCH_AFTER_COND(cond, 2); \
1845 regs.sp[-1].setBoolean(cond); \
1858 #define STRICT_EQUALITY_OP(OP, COND) \
1860 const Value &rref = regs.sp[-1]; \
1861 const Value &lref = regs.sp[-2]; \
1863 if (!StrictlyEqual(cx, lref, rref, &equal)) \
1865 COND = equal OP JS_TRUE; \
1869 BEGIN_CASE(JSOP_STRICTEQ
)
1872 STRICT_EQUALITY_OP(==, cond
);
1873 regs
.sp
[-1].setBoolean(cond
);
1875 END_CASE(JSOP_STRICTEQ
)
1877 BEGIN_CASE(JSOP_STRICTNE
)
1880 STRICT_EQUALITY_OP(!=, cond
);
1881 regs
.sp
[-1].setBoolean(cond
);
1883 END_CASE(JSOP_STRICTNE
)
1885 BEGIN_CASE(JSOP_CASE
)
1888 STRICT_EQUALITY_OP(==, cond
);
1891 len
= GET_JUMP_OFFSET(regs
.pc
);
1897 #undef STRICT_EQUALITY_OP
1902 MutableHandleValue lval
= MutableHandleValue::fromMarkedLocation(®s
.sp
[-2]);
1903 MutableHandleValue rval
= MutableHandleValue::fromMarkedLocation(®s
.sp
[-1]);
1904 if (!LessThanOperation(cx
, lval
, rval
, &cond
))
1906 TRY_BRANCH_AFTER_COND(cond
, 2);
1907 regs
.sp
[-2].setBoolean(cond
);
1915 MutableHandleValue lval
= MutableHandleValue::fromMarkedLocation(®s
.sp
[-2]);
1916 MutableHandleValue rval
= MutableHandleValue::fromMarkedLocation(®s
.sp
[-1]);
1917 if (!LessThanOrEqualOperation(cx
, lval
, rval
, &cond
))
1919 TRY_BRANCH_AFTER_COND(cond
, 2);
1920 regs
.sp
[-2].setBoolean(cond
);
1928 MutableHandleValue lval
= MutableHandleValue::fromMarkedLocation(®s
.sp
[-2]);
1929 MutableHandleValue rval
= MutableHandleValue::fromMarkedLocation(®s
.sp
[-1]);
1930 if (!GreaterThanOperation(cx
, lval
, rval
, &cond
))
1932 TRY_BRANCH_AFTER_COND(cond
, 2);
1933 regs
.sp
[-2].setBoolean(cond
);
1941 MutableHandleValue lval
= MutableHandleValue::fromMarkedLocation(®s
.sp
[-2]);
1942 MutableHandleValue rval
= MutableHandleValue::fromMarkedLocation(®s
.sp
[-1]);
1943 if (!GreaterThanOrEqualOperation(cx
, lval
, rval
, &cond
))
1945 TRY_BRANCH_AFTER_COND(cond
, 2);
1946 regs
.sp
[-2].setBoolean(cond
);
1951 #define SIGNED_SHIFT_OP(OP) \
1954 if (!ToInt32(cx, regs.sp[-2], &i)) \
1956 if (!ToInt32(cx, regs.sp[-1], &j)) \
1958 i = i OP (j & 31); \
1960 regs.sp[-1].setInt32(i); \
1963 BEGIN_CASE(JSOP_LSH
)
1964 SIGNED_SHIFT_OP(<<);
1967 BEGIN_CASE(JSOP_RSH
)
1968 SIGNED_SHIFT_OP(>>);
1971 #undef SIGNED_SHIFT_OP
1973 BEGIN_CASE(JSOP_URSH
)
1975 HandleValue lval
= HandleValue::fromMarkedLocation(®s
.sp
[-2]);
1976 HandleValue rval
= HandleValue::fromMarkedLocation(®s
.sp
[-1]);
1977 if (!UrshOperation(cx
, script
, regs
.pc
, lval
, rval
, ®s
.sp
[-2]))
1983 BEGIN_CASE(JSOP_ADD
)
1985 MutableHandleValue lval
= MutableHandleValue::fromMarkedLocation(®s
.sp
[-2]);
1986 MutableHandleValue rval
= MutableHandleValue::fromMarkedLocation(®s
.sp
[-1]);
1987 if (!AddOperation(cx
, script
, regs
.pc
, lval
, rval
, ®s
.sp
[-2]))
1993 BEGIN_CASE(JSOP_SUB
)
1995 RootedValue
&lval
= rootValue0
, &rval
= rootValue1
;
1998 if (!SubOperation(cx
, script
, regs
.pc
, lval
, rval
, ®s
.sp
[-2]))
2004 BEGIN_CASE(JSOP_MUL
)
2006 RootedValue
&lval
= rootValue0
, &rval
= rootValue1
;
2009 if (!MulOperation(cx
, script
, regs
.pc
, lval
, rval
, ®s
.sp
[-2]))
2015 BEGIN_CASE(JSOP_DIV
)
2017 RootedValue
&lval
= rootValue0
, &rval
= rootValue1
;
2020 if (!DivOperation(cx
, script
, regs
.pc
, lval
, rval
, ®s
.sp
[-2]))
2026 BEGIN_CASE(JSOP_MOD
)
2028 RootedValue
&lval
= rootValue0
, &rval
= rootValue1
;
2031 if (!ModOperation(cx
, script
, regs
.pc
, lval
, rval
, ®s
.sp
[-2]))
2037 BEGIN_CASE(JSOP_NOT
)
2039 bool cond
= ToBoolean(regs
.sp
[-1]);
2041 PUSH_BOOLEAN(!cond
);
2045 BEGIN_CASE(JSOP_BITNOT
)
2048 HandleValue value
= HandleValue::fromMarkedLocation(®s
.sp
[-1]);
2049 if (!BitNot(cx
, value
, &i
))
2051 regs
.sp
[-1].setInt32(i
);
2053 END_CASE(JSOP_BITNOT
)
2055 BEGIN_CASE(JSOP_NEG
)
2057 RootedValue
&val
= rootValue0
;
2059 MutableHandleValue res
= MutableHandleValue::fromMarkedLocation(®s
.sp
[-1]);
2060 if (!NegOperation(cx
, script
, regs
.pc
, val
, res
))
2065 BEGIN_CASE(JSOP_POS
)
2066 if (!ToNumber(cx
, ®s
.sp
[-1]))
2068 if (!regs
.sp
[-1].isInt32())
2069 TypeScript::MonitorOverflow(cx
, script
, regs
.pc
);
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
))
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. */
2092 MutableHandleValue res
= MutableHandleValue::fromMarkedLocation(®s
.sp
[-1]);
2093 if (!JSObject::deleteProperty(cx
, scope
, name
, res
, false))
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(®s
.sp
[-1]);
2109 if (!JSObject::deleteProperty(cx
, obj
, name
, res
, script
->strict
))
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(®s
.sp
[-2]);
2124 if (!JSObject::deleteByValue(cx
, obj
, propval
, res
, script
->strict
))
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(®s
.sp
[-1]);
2143 if (!ToIdOperation(cx
, script
, regs
.pc
, objval
, idval
, res
))
2148 BEGIN_CASE(JSOP_TYPEOFEXPR
)
2149 BEGIN_CASE(JSOP_TYPEOF
)
2151 HandleValue ref
= HandleValue::fromMarkedLocation(®s
.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();
2160 BEGIN_CASE(JSOP_INCELEM
)
2161 BEGIN_CASE(JSOP_DECELEM
)
2162 BEGIN_CASE(JSOP_ELEMINC
)
2163 BEGIN_CASE(JSOP_ELEMDEC
)
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
)
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
)
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
)
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
)
2205 END_CASE(JSOP_LOCALINC
)
2207 BEGIN_CASE(JSOP_THIS
)
2208 if (!ComputeThis(cx
, regs
.fp()))
2210 PUSH_COPY(regs
.fp()->thisValue());
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(®s
.sp
[-1]);
2219 if (!GetPropertyOperation(cx
, script
, regs
.pc
, lval
, lval
))
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(®s
.sp
[-1]);
2231 if (!SetIntrinsicOperation(cx
, script
, regs
.pc
, value
))
2234 regs
.sp
[-2] = regs
.sp
[-1];
2237 END_CASE(JSOP_SETINTRINSIC
)
2239 BEGIN_CASE(JSOP_SETGNAME
)
2240 BEGIN_CASE(JSOP_SETNAME
)
2242 RootedObject
&scope
= rootObject0
;
2243 scope
= ®s
.sp
[-2].toObject();
2245 HandleValue value
= HandleValue::fromMarkedLocation(®s
.sp
[-1]);
2247 if (!SetNameOperation(cx
, script
, regs
.pc
, scope
, value
))
2250 regs
.sp
[-2] = regs
.sp
[-1];
2253 END_CASE(JSOP_SETNAME
)
2255 BEGIN_CASE(JSOP_SETPROP
)
2257 HandleValue lval
= HandleValue::fromMarkedLocation(®s
.sp
[-2]);
2258 HandleValue rval
= HandleValue::fromMarkedLocation(®s
.sp
[-1]);
2260 if (!SetPropertyOperation(cx
, regs
.pc
, lval
, rval
))
2263 regs
.sp
[-2] = regs
.sp
[-1];
2266 END_CASE(JSOP_SETPROP
)
2268 BEGIN_CASE(JSOP_GETELEM
)
2269 BEGIN_CASE(JSOP_CALLELEM
)
2271 MutableHandleValue lval
= MutableHandleValue::fromMarkedLocation(®s
.sp
[-2]);
2272 HandleValue rval
= HandleValue::fromMarkedLocation(®s
.sp
[-1]);
2274 MutableHandleValue res
= MutableHandleValue::fromMarkedLocation(®s
.sp
[-2]);
2275 if (!GetElementOperation(cx
, op
, lval
, rval
, res
))
2277 TypeScript::Monitor(cx
, script
, regs
.pc
, res
);
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
))
2291 regs
.sp
[-3] = value
;
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
);
2306 if (!JSObject::setGeneric(cx
, obj
, obj
, id
, &rval
, script
->strict
))
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
))
2319 if (!InvokeKernel(cx
, args
))
2322 regs
.sp
= args
.spAfterCall();
2323 TypeScript::Monitor(cx
, script
, regs
.pc
, regs
.sp
[-1]);
2327 BEGIN_CASE(JSOP_FUNAPPLY
)
2328 if (!GuardFunApplyArgumentsOptimization(cx
))
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
2351 if (isFunction
&& fun
->isInterpreted()) {
2352 funScript
= fun
->getOrCreateScript(cx
);
2355 if (cx
->typeInferenceEnabled() && funScript
->shouldCloneAtCallsite
) {
2356 fun
= CloneFunctionAtCallsite(cx
, fun
, script
, regs
.pc
);
2359 args
.setCallee(ObjectValue(*fun
));
2363 /* Don't bother trying to fast-path calls to scripted non-constructors. */
2364 if (!isFunction
|| !fun
->isInterpretedConstructor()) {
2366 if (!InvokeConstructorKernel(cx
, args
))
2369 if (!InvokeKernel(cx
, args
))
2372 Value
*newsp
= args
.spAfterCall();
2373 TypeScript::Monitor(cx
, script
, regs
.pc
, newsp
[-1]);
2375 len
= JSOP_CALL_LENGTH
;
2379 if (!TypeMonitorCall(cx
, args
, construct
))
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
))
2389 regs
.fp()->setUseNewType();
2391 SET_SCRIPT(regs
.fp()->script());
2393 script
->resetLoopCount();
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
)
2402 if (status
== ion::Method_Compiled
) {
2403 ion::IonExecStatus exec
= ion::Cannon(cx
, regs
.fp());
2405 if (exec
== ion::IonExec_Bailout
) {
2406 SET_SCRIPT(regs
.fp()->script());
2407 op
= JSOp(*regs
.pc
);
2410 interpReturnOK
= !IsErrorStatus(exec
);
2417 if (!newType
&& cx
->methodJitEnabled
) {
2418 /* Try to ensure methods are method JIT'd. */
2419 mjit::CompileStatus status
= mjit::CanMethodJIT(cx
, script
, script
->code
,
2421 mjit::CompileRequest_Interpreter
,
2423 if (status
== mjit::Compile_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
);
2434 if (!regs
.fp()->prologue(cx
))
2436 if (cx
->compartment
->debugMode()) {
2437 switch (ScriptDebugPrologue(cx
, regs
.fp())) {
2438 case JSTRAP_CONTINUE
:
2441 interpReturnOK
= true;
2447 JS_NOT_REACHED("bad ScriptDebugPrologue status");
2451 /* Load first op and dispatch it (safe since JSOP_STOP). */
2452 op
= (JSOp
) *regs
.pc
;
2456 BEGIN_CASE(JSOP_SETCALL
)
2458 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_BAD_LEFTSIDE_OF_ASS
);
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
))
2475 RootedValue
&v
= rootValue0
;
2476 if (!ComputeImplicitThis(cx
, scope
, &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
))
2493 TypeScript::Monitor(cx
, script
, regs
.pc
, rval
);
2497 BEGIN_CASE(JSOP_GETINTRINSIC
)
2498 BEGIN_CASE(JSOP_CALLINTRINSIC
)
2500 RootedValue
&rval
= rootValue0
;
2502 if (!GetIntrinsicOperation(cx
, script
, regs
.pc
, &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
));
2522 BEGIN_CASE(JSOP_INT32
)
2523 PUSH_INT32(GET_INT32(regs
.pc
));
2524 END_CASE(JSOP_INT32
)
2526 BEGIN_CASE(JSOP_DOUBLE
)
2529 LOAD_DOUBLE(0, 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
2548 uint32_t index
= GET_UINT32_INDEX(regs
.pc
);
2549 JSObject
*proto
= regs
.fp()->global().getOrCreateRegExpPrototype(cx
);
2552 JSObject
*obj
= CloneRegExpObject(cx
, script
->getRegExp(index
), proto
);
2557 END_CASE(JSOP_REGEXP
)
2559 BEGIN_CASE(JSOP_ZERO
)
2563 BEGIN_CASE(JSOP_ONE
)
2567 BEGIN_CASE(JSOP_NULL
)
2571 BEGIN_CASE(JSOP_FALSE
)
2572 PUSH_BOOLEAN(false);
2573 END_CASE(JSOP_FALSE
)
2575 BEGIN_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
;
2592 if (rref
.isInt32()) {
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())))
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
);
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
);
2617 BEGIN_CASE(JSOP_ARGUMENTS
)
2618 JS_ASSERT(!regs
.fp()->fun()->hasRest());
2619 if (script
->needsArgsObj()) {
2620 ArgumentsObject
*obj
= ArgumentsObject::createExpected(cx
, regs
.fp());
2623 PUSH_COPY(ObjectValue(*obj
));
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
);
2635 PUSH_COPY(ObjectValue(*rest
));
2636 if (!SetInitializerObjectType(cx
, script
, regs
.pc
, rest
, GenericObject
))
2638 rootType0
= GetTypeCallerInitObject(cx
, JSProto_Array
);
2641 rest
->setType(rootType0
);
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
));
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]);
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
;
2717 RootedObject
&obj
= rootObject0
;
2718 obj
= ®s
.fp()->varObj();
2720 RootedPropertyName
&name
= rootName0
;
2721 name
= script
->getName(regs
.pc
);
2723 if (!DefVarOrConstOperation(cx
, obj
, name
, attrs
))
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
))
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());
2753 JS_ASSERT(obj
->getProto());
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
;
2772 RootedObject
&obj
= rootObject0
;
2776 id
= NameToId(script
->getName(regs
.pc
));
2785 FETCH_OBJECT(cx
, i
- 1, obj
);
2789 JS_ASSERT(regs
.stackDepth() >= 2);
2792 id
= NameToId(script
->getName(regs
.pc
));
2795 JS_ASSERT(op2
== JSOP_INITELEM
);
2796 JS_ASSERT(regs
.stackDepth() >= 3);
2802 const Value
&lref
= regs
.sp
[i
-1];
2803 JS_ASSERT(lref
.isObject());
2804 obj
= &lref
.toObject();
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
);
2820 * Getters and setters are just like watchpoints from an access control
2823 scratch
.setUndefined();
2825 if (!CheckAccess(cx
, obj
, id
, JSACC_WATCH
, &scratch
, &attrs
))
2829 StrictPropertyOp setter
;
2830 if (op
== JSOP_GETTER
) {
2831 getter
= CastAsPropertyOp(&rval
.toObject());
2832 setter
= JS_StrictPropertyStub
;
2833 attrs
= JSPROP_GETTER
;
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
))
2846 if (js_CodeSpec
[op2
].ndefs
> js_CodeSpec
[op2
].nuses
) {
2847 JS_ASSERT(js_CodeSpec
[op2
].ndefs
== js_CodeSpec
[op2
].nuses
+ 1);
2849 assertSameCompartmentDebugOnly(cx
, regs
.sp
[-1]);
2851 len
= js_CodeSpec
[op2
].length
;
2855 BEGIN_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
);
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
))
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
))
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
))
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
;
2927 /* Load the object being initialized into lval/obj. */
2928 RootedObject
&obj
= rootObject0
;
2929 obj
= ®s
.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)) {
2946 END_CASE(JSOP_INITPROP
);
2948 BEGIN_CASE(JSOP_INITELEM
)
2950 JS_ASSERT(regs
.stackDepth() >= 3);
2951 HandleValue val
= HandleValue::fromMarkedLocation(®s
.sp
[-1]);
2952 MutableHandleValue id
= MutableHandleValue::fromMarkedLocation(®s
.sp
[-2]);
2954 RootedObject
&obj
= rootObject0
;
2955 obj
= ®s
.sp
[-3].toObject();
2957 if (!InitElemOperation(cx
, obj
, id
, val
))
2962 END_CASE(JSOP_INITELEM
)
2964 BEGIN_CASE(JSOP_INITELEM_ARRAY
)
2966 JS_ASSERT(regs
.stackDepth() >= 2);
2967 HandleValue val
= HandleValue::fromMarkedLocation(®s
.sp
[-1]);
2969 RootedObject
&obj
= rootObject0
;
2970 obj
= ®s
.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
))
2980 END_CASE(JSOP_INITELEM_ARRAY
)
2982 BEGIN_CASE(JSOP_INITELEM_INC
)
2984 JS_ASSERT(regs
.stackDepth() >= 3);
2985 HandleValue val
= HandleValue::fromMarkedLocation(®s
.sp
[-1]);
2987 RootedObject
&obj
= rootObject0
;
2988 obj
= ®s
.sp
[-3].toObject();
2990 uint32_t index
= regs
.sp
[-2].toInt32();
2991 if (!InitArrayElemOperation(cx
, regs
.pc
, obj
, index
, val
))
2994 regs
.sp
[-2].setInt32(index
+ 1);
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
= ®s
.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
);
3013 iterVal
= iter
.value();
3014 if (!JSObject::defineElement(cx
, arr
, count
++, iterVal
, NULL
, NULL
, JSPROP_ENUMERATE
))
3019 regs
.sp
[-2].setInt32(count
);
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
);
3034 BEGIN_CASE(JSOP_RETSUB
)
3035 /* Pop [exception or hole, retsub pc-index]. */
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
);
3050 JS_ASSERT(rval
.isInt32());
3052 /* Increment the PC by this much. */
3053 len
= rval
.toInt32() - int32_t(regs
.pc
- script
->code
);
3057 BEGIN_CASE(JSOP_EXCEPTION
)
3060 MutableHandleValue res
= MutableHandleValue::fromMarkedLocation(®s
.sp
[-1]);
3061 if (!GetAndClearException(cx
, res
))
3064 END_CASE(JSOP_EXCEPTION
)
3066 BEGIN_CASE(JSOP_FINALLY
)
3068 END_CASE(JSOP_FINALLY
)
3070 BEGIN_CASE(JSOP_THROWING
)
3072 JS_ASSERT(!cx
->isExceptionPending());
3075 cx
->setPendingException(v
);
3077 END_CASE(JSOP_THROWING
)
3079 BEGIN_CASE(JSOP_THROW
)
3082 RootedValue
&v
= rootValue0
;
3084 JS_ALWAYS_FALSE(Throw(cx
, v
));
3085 /* let the code at error try to catch the exception. */
3089 BEGIN_CASE(JSOP_INSTANCEOF
)
3091 RootedValue
&rref
= rootValue0
;
3093 if (rref
.isPrimitive()) {
3094 js_ReportValueError(cx
, JSMSG_BAD_INSTANCEOF_RHS
, -1, rref
, NullPtr());
3097 RootedObject
&obj
= rootObject0
;
3098 obj
= &rref
.toObject();
3099 JSBool cond
= JS_FALSE
;
3100 if (!HasInstance(cx
, obj
, HandleValue::fromMarkedLocation(®s
.sp
[-2]), &cond
))
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
);
3118 case JSTRAP_CONTINUE
:
3121 regs
.fp()->setReturnValue(rval
);
3122 interpReturnOK
= true;
3125 cx
->setPendingException(rval
);
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
);
3146 /* Clone block iff there are any closed-over variables. */
3147 if (!regs
.fp()->pushBlock(cx
, blockObj
))
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
= ®s
.sp
[-1];
3167 regs
.sp
-= GET_UINT16(regs
.pc
);
3168 JS_ASSERT(regs
.stackDepth() == blockDepth
+ 1);
3171 /* Another op will pop; nothing to do here. */
3172 len
= JSOP_LEAVEFORLETIN_LENGTH
;
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
);
3187 regs
.fp()->setReturnValue(ObjectValue(*obj
));
3188 regs
.fp()->setYielding();
3189 interpReturnOK
= true;
3190 if (entryFrame
!= regs
.fp())
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());
3204 regs
.fp()->setReturnValue(regs
.sp
[-1]);
3205 regs
.fp()->setYielding();
3206 regs
.pc
+= JSOP_YIELD_LENGTH
;
3207 interpReturnOK
= true;
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
= ®s
.fp()->unaliasedLocal(slot
).toObject();
3217 if (!js_NewbornArrayPush(cx
, obj
, regs
.sp
[-1]))
3221 END_CASE(JSOP_ARRAYPUSH
)
3222 #endif /* JS_HAS_GENERATORS */
3227 JS_snprintf(numBuf
, sizeof numBuf
, "%d", op
);
3228 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
3229 JSMSG_BAD_BYTECODE
, numBuf
);
3237 JS_ASSERT(&cx
->regs() == ®s
);
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
);
3251 case JSTRAP_CONTINUE
:
3256 interpReturnOK
= true;
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
3274 regs
.pc
= (script
)->main() + tn
->start
+ tn
->length
;
3275 regs
.sp
= regs
.spForStackDepth(tn
->stackDepth
);
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
)))
3288 * Don't clear exceptions to save cx->exception from GC
3289 * until it is pushed to the stack via [exception] in the
3297 * Push (true, exception) pair for finally to indicate that
3298 * [retsub] should rethrow the exception.
3301 PUSH_COPY(cx
->getPendingException());
3302 cx
->clearPendingException();
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
= ®s
.sp
[-1].toObject();
3311 bool ok
= UnwindIteratorForException(cx
, obj
);
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();
3333 UnwindForUncatchableException(cx
, regs
);
3334 interpReturnOK
= false;
3338 UnwindScope(cx
, cx
->fp(), 0);
3339 regs
.setToEndOfScript();
3341 if (entryFrame
!= regs
.fp())
3345 if (cx
->compartment
->debugMode())
3346 interpReturnOK
= ScriptDebugEpilogue(cx
, regs
.fp(), interpReturnOK
);
3347 if (!regs
.fp()->isYielding())
3348 regs
.fp()->epilogue(cx
);
3350 Probes::exitScript(cx
, script
, script
->function(), regs
.fp());
3351 regs
.fp()->setFinishedInInterpreter();
3353 gc::MaybeVerifyBarriers(cx
, true);
3357 * This path is used when it's guaranteed the method can be finished
3360 leave_on_safe_point
:
3363 return interpReturnOK
? Interpret_Ok
: Interpret_Error
;
3367 js::Throw(JSContext
*cx
, HandleValue v
)
3369 JS_ASSERT(!cx
->isExceptionPending());
3370 cx
->setPendingException(v
);
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
))
3383 RootedObject
obj(cx
, ToObjectFromStack(cx
, v
));
3386 return JSObject::getProperty(cx
, obj
, obj
, name
, vp
);
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
))
3398 JSAutoByteString printable
;
3399 if (js_AtomToPrintableString(cx
, name
, &printable
))
3400 js_ReportIsNotDefined(cx
, printable
.ptr());
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.
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
))
3421 vp
.set(UndefinedValue());
3425 return JSObject::getProperty(cx
, obj
, obj
, name
, vp
);
3429 js::Lambda(JSContext
*cx
, HandleFunction fun
, HandleObject parent
)
3431 RootedObject
clone(cx
, CloneFunctionObjectIfNotSingleton(cx
, fun
, parent
));
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
))
3443 RootedValue
thisval(cx
, fp
->thisValue());
3444 clone
= js_fun_bind(cx
, clone
, thisval
, NULL
, 0);
3447 clone
->toFunction()->flags
|= JSFunction::ARROW
;
3450 JS_ASSERT(clone
->global() == clone
->global());
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
);
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
))
3495 RootedValue
rval(cx
, ObjectValue(*fun
));
3498 * ECMA requires functions defined when entering Eval code to be
3501 unsigned attrs
= script
->isActiveEval
3503 : JSPROP_ENUMERATE
| JSPROP_PERMANENT
;
3506 if (!shape
|| pobj
!= parent
) {
3507 return JSObject::defineProperty(cx
, parent
, name
, rval
, JS_PropertyStub
,
3508 JS_StrictPropertyStub
, attrs
);
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
,
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).
3538 return JSObject::setProperty(cx
, parent
, parent
, name
, &rval
, script
->strict
);
3542 js::GetAndClearException(JSContext
*cx
, MutableHandleValue res
)
3544 // Check the interrupt flag to allow interrupting deeply nested exception
3546 if (cx
->runtime
->interrupt
&& !js_HandleExecutionInterrupt(cx
))
3549 res
.set(cx
->getPendingException());
3550 cx
->clearPendingException();
3554 template <bool strict
>
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
>
3567 js::DeleteProperty(JSContext
*cx
, HandleValue v
, HandlePropertyName name
, JSBool
*bp
)
3569 // default op result is false (failure)
3572 // convert value to JSObject pointer
3573 RootedObject
obj(cx
, ToObjectFromStack(cx
, v
));
3577 // Call deleteProperty on obj
3578 RootedValue
result(cx
, NullValue());
3579 bool delprop_ok
= JSObject::deleteProperty(cx
, obj
, name
, &result
, strict
);
3583 // convert result into *bp and return
3584 *bp
= result
.toBoolean();
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
);
3592 js::GetElement(JSContext
*cx
, MutableHandleValue lref
, HandleValue rref
, MutableHandleValue vp
)
3594 return GetElementOperation(cx
, JSOP_GETELEM
, lref
, rref
, vp
);
3598 js::GetElementMonitored(JSContext
*cx
, MutableHandleValue lref
, HandleValue rref
,
3599 MutableHandleValue vp
)
3601 if (!GetElement(cx
, lref
, rref
, vp
))
3604 TypeScript::Monitor(cx
, vp
);
3609 js::CallElement(JSContext
*cx
, MutableHandleValue lref
, HandleValue rref
, MutableHandleValue res
)
3611 return GetElementOperation(cx
, JSOP_CALLELEM
, lref
, rref
, res
);
3615 js::SetObjectElement(JSContext
*cx
, HandleObject obj
, HandleValue index
, HandleValue value
,
3619 RootedValue
indexval(cx
, index
);
3620 if (!FetchElementId(cx
, obj
, indexval
, &id
, &indexval
))
3622 return SetObjectElementOperation(cx
, obj
, id
, value
, strict
);
3626 js::SetObjectElement(JSContext
*cx
, HandleObject obj
, HandleValue index
, HandleValue value
,
3627 JSBool strict
, HandleScript script
, jsbytecode
*pc
)
3631 RootedValue
indexval(cx
, index
);
3632 if (!FetchElementId(cx
, obj
, indexval
, &id
, &indexval
))
3634 return SetObjectElementOperation(cx
, obj
, id
, value
, strict
, script
, pc
);
3638 js::AddValues(JSContext
*cx
, HandleScript script
, jsbytecode
*pc
,
3639 MutableHandleValue lhs
, MutableHandleValue rhs
,
3642 return AddOperation(cx
, script
, pc
, lhs
, rhs
, res
);
3646 js::SubValues(JSContext
*cx
, HandleScript script
, jsbytecode
*pc
,
3647 MutableHandleValue lhs
, MutableHandleValue rhs
,
3650 return SubOperation(cx
, script
, pc
, lhs
, rhs
, res
);
3654 js::MulValues(JSContext
*cx
, HandleScript script
, jsbytecode
*pc
,
3655 MutableHandleValue lhs
, MutableHandleValue rhs
,
3658 return MulOperation(cx
, script
, pc
, lhs
, rhs
, res
);
3662 js::DivValues(JSContext
*cx
, HandleScript script
, jsbytecode
*pc
,
3663 MutableHandleValue lhs
, MutableHandleValue rhs
,
3666 return DivOperation(cx
, script
, pc
, lhs
, rhs
, res
);
3670 js::ModValues(JSContext
*cx
, HandleScript script
, jsbytecode
*pc
,
3671 MutableHandleValue lhs
, MutableHandleValue rhs
,
3674 return ModOperation(cx
, script
, pc
, lhs
, rhs
, res
);
3678 js::UrshValues(JSContext
*cx
, HandleScript script
, jsbytecode
*pc
,
3679 MutableHandleValue lhs
, MutableHandleValue rhs
,
3682 return UrshOperation(cx
, script
, pc
, lhs
, rhs
, res
);