in js/src/vm/PortableBaselineInterpret.cpp [5957:9308]
PBIResult PortableBaselineInterpret(
JSContext* cx_, State& state, Stack& stack, StackVal* sp,
JSObject* envChain, Value* ret, jsbytecode* pc, ImmutableScriptData* isd,
jsbytecode* restartEntryPC, BaselineFrame* restartFrame,
StackVal* restartEntryFrame, PBIResult restartCode) {
#define RESTART(code) \
if (!IsRestart) { \
TRACE_PRINTF("Restarting (code %d sp %p fp %p)\n", int(code), sp, \
ctx.stack.fp); \
SYNCSP(); \
restartCode = code; \
goto restart; \
}
#define GOTO_ERROR() \
do { \
SYNCSP(); \
RESTART(PBIResult::Error); \
goto error; \
} while (0)
// Update local state when we switch to a new script with a new PC.
#define RESET_PC(new_pc, new_script) \
pc = new_pc; \
entryPC = new_script->code(); \
isd = new_script->immutableScriptData(); \
icEntries = frame->icScript()->icEntries(); \
icEntry = frame->interpreterICEntry(); \
argsObjAliasesFormals = frame->script()->argsObjAliasesFormals(); \
resumeOffsets = isd->resumeOffsets().data();
#define OPCODE_LABEL(op, ...) LABEL(op),
#define TRAILING_LABEL(v) LABEL(default),
static const void* const addresses[EnableInterruptsPseudoOpcode + 1] = {
FOR_EACH_OPCODE(OPCODE_LABEL)
FOR_EACH_TRAILING_UNUSED_OPCODE(TRAILING_LABEL)};
#undef OPCODE_LABEL
#undef TRAILING_LABEL
BaselineFrame* frame = restartFrame;
StackVal* entryFrame = restartEntryFrame;
jsbytecode* entryPC = restartEntryPC;
if (!IsRestart) {
PUSHNATIVE(StackValNative(nullptr)); // Fake return address.
frame = stack.pushFrame(sp, cx_, envChain);
MOZ_ASSERT(frame); // safety: stack margin.
sp = reinterpret_cast<StackVal*>(frame);
// Save the entry frame so that when unwinding, we know when to
// return from this C++ frame.
entryFrame = sp;
// Save the entry PC so that we can compute offsets locally.
entryPC = pc;
}
bool from_unwind = false;
uint32_t nfixed = frame->script()->nfixed();
bool argsObjAliasesFormals = frame->script()->argsObjAliasesFormals();
PBIResult ic_result = PBIResult::Ok;
uint64_t ic_arg0 = 0, ic_arg1 = 0, ic_arg2 = 0, ic_ret = 0;
ICCtx ctx(cx_, frame, state, stack);
auto* icEntries = frame->icScript()->icEntries();
auto* icEntry = icEntries;
const uint32_t* resumeOffsets = isd->resumeOffsets().data();
if (IsRestart) {
ic_result = restartCode;
TRACE_PRINTF(
"Enter from restart: sp = %p ctx.stack.fp = %p ctx.frame = %p\n", sp,
ctx.stack.fp, ctx.frame);
goto ic_fail;
} else {
AutoCheckRecursionLimit recursion(ctx.frameMgr.cxForLocalUseOnly());
if (!recursion.checkDontReport(ctx.frameMgr.cxForLocalUseOnly())) {
PUSH_EXIT_FRAME();
ReportOverRecursed(ctx.frameMgr.cxForLocalUseOnly());
return PBIResult::Error;
}
}
// Check max stack depth once, so we don't need to check it
// otherwise below for ordinary stack-manipulation opcodes (just for
// exit frames).
if (!ctx.stack.check(sp, sizeof(StackVal) * frame->script()->nslots())) {
PUSH_EXIT_FRAME();
ReportOverRecursed(ctx.frameMgr.cxForLocalUseOnly());
return PBIResult::Error;
}
SYNCSP();
sp -= nfixed;
for (uint32_t i = 0; i < nfixed; i++) {
sp[i] = StackVal(UndefinedValue());
}
ret->setUndefined();
// Check if we are being debugged, and set a flag in the frame if so. This
// flag must be set before calling InitFunctionEnvironmentObjects.
if (frame->script()->isDebuggee()) {
TRACE_PRINTF("Script is debuggee\n");
frame->setIsDebuggee();
}
if (CalleeTokenIsFunction(frame->calleeToken())) {
JSFunction* func = CalleeTokenToFunction(frame->calleeToken());
frame->setEnvironmentChain(func->environment());
if (func->needsFunctionEnvironmentObjects()) {
PUSH_EXIT_FRAME();
if (!js::InitFunctionEnvironmentObjects(cx, frame)) {
GOTO_ERROR();
}
TRACE_PRINTF("callee is func %p; created environment object: %p\n", func,
frame->environmentChain());
}
}
// The debug prologue can't run until the function environment is set up.
if (frame->script()->isDebuggee()) {
PUSH_EXIT_FRAME();
if (!DebugPrologue(cx, frame)) {
GOTO_ERROR();
}
}
if (!frame->script()->hasScriptCounts()) {
if (ctx.frameMgr.cxForLocalUseOnly()->realm()->collectCoverageForDebug()) {
PUSH_EXIT_FRAME();
if (!frame->script()->initScriptCounts(cx)) {
GOTO_ERROR();
}
}
}
COUNT_COVERAGE_MAIN();
#ifdef ENABLE_INTERRUPT_CHECKS
if (ctx.frameMgr.cxForLocalUseOnly()->hasAnyPendingInterrupt()) {
PUSH_EXIT_FRAME();
if (!InterruptCheck(cx)) {
GOTO_ERROR();
}
}
#endif
TRACE_PRINTF("Entering: sp = %p fp = %p frame = %p, script = %p, pc = %p\n",
sp, ctx.stack.fp, frame, frame->script(), pc);
TRACE_PRINTF("nslots = %d nfixed = %d\n", int(frame->script()->nslots()),
int(frame->script()->nfixed()));
while (true) {
DEBUG_CHECK();
#if !defined(ENABLE_COMPUTED_GOTO_DISPATCH) || !defined(__wasi__)
dispatch:
#endif
#ifdef TRACE_INTERP
{
JSOp op = JSOp(*pc);
printf("sp[0] = %" PRIx64 " sp[1] = %" PRIx64 " sp[2] = %" PRIx64 "\n",
sp[0].asUInt64(), sp[1].asUInt64(), sp[2].asUInt64());
printf("script = %p pc = %p: %s (ic %d entry %p) pending = %d\n",
frame->script(), pc, CodeName(op), (int)(icEntry - icEntries),
icEntry, ctx.frameMgr.cxForLocalUseOnly()->isExceptionPending());
printf("sp = %p fp = %p\n", sp, ctx.stack.fp);
printf("TOS tag: %d\n", int(sp[0].asValue().asRawBits() >> 47));
fflush(stdout);
}
#endif
#ifdef ENABLE_COMPUTED_GOTO_DISPATCH
goto* addresses[*pc];
#else
(void)addresses; // Avoid unused-local error. We keep the table
// itself to avoid warnings (see note in IC
// interpreter above).
switch (JSOp(*pc))
#endif
{
CASE(Nop) { END_OP(Nop); }
CASE(NopIsAssignOp) { END_OP(NopIsAssignOp); }
CASE(Undefined) {
VIRTPUSH(StackVal(UndefinedValue()));
END_OP(Undefined);
}
CASE(Null) {
VIRTPUSH(StackVal(NullValue()));
END_OP(Null);
}
CASE(False) {
VIRTPUSH(StackVal(BooleanValue(false)));
END_OP(False);
}
CASE(True) {
VIRTPUSH(StackVal(BooleanValue(true)));
END_OP(True);
}
CASE(Int32) {
VIRTPUSH(StackVal(Int32Value(GET_INT32(pc))));
END_OP(Int32);
}
CASE(Zero) {
VIRTPUSH(StackVal(Int32Value(0)));
END_OP(Zero);
}
CASE(One) {
VIRTPUSH(StackVal(Int32Value(1)));
END_OP(One);
}
CASE(Int8) {
VIRTPUSH(StackVal(Int32Value(GET_INT8(pc))));
END_OP(Int8);
}
CASE(Uint16) {
VIRTPUSH(StackVal(Int32Value(GET_UINT16(pc))));
END_OP(Uint16);
}
CASE(Uint24) {
VIRTPUSH(StackVal(Int32Value(GET_UINT24(pc))));
END_OP(Uint24);
}
CASE(Double) {
VIRTPUSH(StackVal(GET_INLINE_VALUE(pc)));
END_OP(Double);
}
CASE(BigInt) {
VIRTPUSH(StackVal(JS::BigIntValue(frame->script()->getBigInt(pc))));
END_OP(BigInt);
}
CASE(String) {
VIRTPUSH(StackVal(StringValue(frame->script()->getString(pc))));
END_OP(String);
}
CASE(Symbol) {
VIRTPUSH(StackVal(SymbolValue(
ctx.frameMgr.cxForLocalUseOnly()->wellKnownSymbols().get(
GET_UINT8(pc)))));
END_OP(Symbol);
}
CASE(Void) {
VIRTSPWRITE(0, StackVal(JS::UndefinedValue()));
END_OP(Void);
}
CASE(Typeof)
CASE(TypeofExpr) {
static_assert(JSOpLength_Typeof == JSOpLength_TypeofExpr);
if (HybridICs) {
SYNCSP();
VIRTSPWRITE(
0,
StackVal(StringValue(TypeOfOperation(
SPHANDLE(0), ctx.frameMgr.cxForLocalUseOnly()->runtime()))));
NEXT_IC();
} else {
IC_POP_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(TypeOf, false);
}
END_OP(Typeof);
}
CASE(TypeofEq) {
if (HybridICs) {
TypeofEqOperand operand =
TypeofEqOperand::fromRawValue(GET_UINT8(pc));
bool result = js::TypeOfValue(SPHANDLE(0)) == operand.type();
if (operand.compareOp() == JSOp::Ne) {
result = !result;
}
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
} else {
IC_POP_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(TypeOfEq, false);
}
END_OP(TypeofEq);
}
CASE(Pos) {
if (VIRTSP(0).asValue().isNumber()) {
// Nothing!
NEXT_IC();
END_OP(Pos);
} else {
goto generic_unary;
}
}
CASE(Neg) {
Value v = VIRTSP(0).asValue();
if (v.isInt32()) {
int32_t i = v.toInt32();
if (i != 0 && i != INT32_MIN) {
VIRTSPWRITE(0, StackVal(Int32Value(-i)));
NEXT_IC();
END_OP(Neg);
}
}
if (v.isNumber()) {
VIRTSPWRITE(0, StackVal(NumberValue(-v.toNumber())));
NEXT_IC();
END_OP(Neg);
}
goto generic_unary;
}
CASE(Inc) {
Value v = VIRTSP(0).asValue();
if (v.isInt32()) {
int32_t i = v.toInt32();
if (i != INT32_MAX) {
VIRTSPWRITE(0, StackVal(Int32Value(i + 1)));
NEXT_IC();
END_OP(Inc);
}
}
if (v.isNumber()) {
VIRTSPWRITE(0, StackVal(NumberValue(v.toNumber() + 1)));
NEXT_IC();
END_OP(Inc);
}
goto generic_unary;
}
CASE(Dec) {
Value v = VIRTSP(0).asValue();
if (v.isInt32()) {
int32_t i = v.toInt32();
if (i != INT32_MIN) {
VIRTSPWRITE(0, StackVal(Int32Value(i - 1)));
NEXT_IC();
END_OP(Dec);
}
}
if (v.isNumber()) {
VIRTSPWRITE(0, StackVal(NumberValue(v.toNumber() - 1)));
NEXT_IC();
END_OP(Dec);
}
goto generic_unary;
}
CASE(BitNot) {
Value v = VIRTSP(0).asValue();
if (v.isInt32()) {
int32_t i = v.toInt32();
VIRTSPWRITE(0, StackVal(Int32Value(~i)));
NEXT_IC();
END_OP(Inc);
}
goto generic_unary;
}
CASE(ToNumeric) {
if (VIRTSP(0).asValue().isNumeric()) {
NEXT_IC();
} else if (HybridICs) {
SYNCSP();
MutableHandleValue val = SPHANDLEMUT(0);
PUSH_EXIT_FRAME();
if (!ToNumeric(cx, val)) {
GOTO_ERROR();
}
NEXT_IC();
} else {
goto generic_unary;
}
END_OP(ToNumeric);
}
generic_unary:;
{
static_assert(JSOpLength_Pos == JSOpLength_Neg);
static_assert(JSOpLength_Pos == JSOpLength_BitNot);
static_assert(JSOpLength_Pos == JSOpLength_Inc);
static_assert(JSOpLength_Pos == JSOpLength_Dec);
static_assert(JSOpLength_Pos == JSOpLength_ToNumeric);
IC_POP_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(UnaryArith, false);
END_OP(Pos);
}
CASE(Not) {
Value v = VIRTSP(0).asValue();
if (v.isBoolean()) {
VIRTSPWRITE(0, StackVal(BooleanValue(!v.toBoolean())));
NEXT_IC();
} else if (HybridICs) {
SYNCSP();
VIRTSPWRITE(0, StackVal(BooleanValue(!ToBoolean(SPHANDLE(0)))));
NEXT_IC();
} else {
IC_POP_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC(ToBool, false);
VIRTPUSH(
StackVal(BooleanValue(!Value::fromRawBits(ic_ret).toBoolean())));
}
END_OP(Not);
}
CASE(And) {
bool result;
Value v = VIRTSP(0).asValue();
if (v.isBoolean()) {
result = v.toBoolean();
NEXT_IC();
} else if (HybridICs) {
result = ToBoolean(SPHANDLE(0));
NEXT_IC();
} else {
IC_SET_ARG_FROM_STACK(0, 0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC(ToBool, false);
result = Value::fromRawBits(ic_ret).toBoolean();
}
int32_t jumpOffset = GET_JUMP_OFFSET(pc);
if (!result) {
ADVANCE(jumpOffset);
PREDICT_NEXT(JumpTarget);
PREDICT_NEXT(LoopHead);
} else {
ADVANCE(JSOpLength_And);
}
DISPATCH();
}
CASE(Or) {
bool result;
Value v = VIRTSP(0).asValue();
if (v.isBoolean()) {
result = v.toBoolean();
NEXT_IC();
} else if (HybridICs) {
result = ToBoolean(SPHANDLE(0));
NEXT_IC();
} else {
IC_SET_ARG_FROM_STACK(0, 0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC(ToBool, false);
result = Value::fromRawBits(ic_ret).toBoolean();
}
int32_t jumpOffset = GET_JUMP_OFFSET(pc);
if (result) {
ADVANCE(jumpOffset);
PREDICT_NEXT(JumpTarget);
PREDICT_NEXT(LoopHead);
} else {
ADVANCE(JSOpLength_Or);
}
DISPATCH();
}
CASE(JumpIfTrue) {
bool result;
Value v = VIRTSP(0).asValue();
if (v.isBoolean()) {
result = v.toBoolean();
VIRTPOP();
NEXT_IC();
} else if (HybridICs) {
result = ToBoolean(SPHANDLE(0));
VIRTPOP();
NEXT_IC();
} else {
IC_POP_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC(ToBool, false);
result = Value::fromRawBits(ic_ret).toBoolean();
}
int32_t jumpOffset = GET_JUMP_OFFSET(pc);
if (result) {
ADVANCE(jumpOffset);
PREDICT_NEXT(JumpTarget);
PREDICT_NEXT(LoopHead);
} else {
ADVANCE(JSOpLength_JumpIfTrue);
}
DISPATCH();
}
CASE(JumpIfFalse) {
bool result;
Value v = VIRTSP(0).asValue();
if (v.isBoolean()) {
result = v.toBoolean();
VIRTPOP();
NEXT_IC();
} else if (HybridICs) {
result = ToBoolean(SPHANDLE(0));
VIRTPOP();
NEXT_IC();
} else {
IC_POP_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC(ToBool, false);
result = Value::fromRawBits(ic_ret).toBoolean();
}
int32_t jumpOffset = GET_JUMP_OFFSET(pc);
if (!result) {
ADVANCE(jumpOffset);
PREDICT_NEXT(JumpTarget);
PREDICT_NEXT(LoopHead);
} else {
ADVANCE(JSOpLength_JumpIfFalse);
}
DISPATCH();
}
CASE(Add) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
int64_t lhs = v1.toInt32();
int64_t rhs = v0.toInt32();
int64_t result = lhs + rhs;
if (result >= int64_t(INT32_MIN) && result <= int64_t(INT32_MAX)) {
VIRTPOP();
VIRTSPWRITE(0, StackVal(Int32Value(int32_t(result))));
NEXT_IC();
END_OP(Add);
}
}
if (v0.isNumber() && v1.isNumber()) {
double lhs = v1.toNumber();
double rhs = v0.toNumber();
VIRTPOP();
VIRTSPWRITE(0, StackVal(NumberValue(lhs + rhs)));
NEXT_IC();
END_OP(Add);
}
MutableHandleValue lhs = SPHANDLEMUT(1);
MutableHandleValue rhs = SPHANDLEMUT(0);
MutableHandleValue result = SPHANDLEMUT(1);
{
PUSH_EXIT_FRAME();
if (!AddOperation(cx, lhs, rhs, result)) {
GOTO_ERROR();
}
}
VIRTPOP();
NEXT_IC();
END_OP(Add);
}
goto generic_binary;
}
CASE(Sub) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
int64_t lhs = v1.toInt32();
int64_t rhs = v0.toInt32();
int64_t result = lhs - rhs;
if (result >= int64_t(INT32_MIN) && result <= int64_t(INT32_MAX)) {
VIRTPOP();
VIRTSPWRITE(0, StackVal(Int32Value(int32_t(result))));
NEXT_IC();
END_OP(Sub);
}
}
if (v0.isNumber() && v1.isNumber()) {
double lhs = v1.toNumber();
double rhs = v0.toNumber();
VIRTPOP();
VIRTSPWRITE(0, StackVal(NumberValue(lhs - rhs)));
NEXT_IC();
END_OP(Add);
}
MutableHandleValue lhs = SPHANDLEMUT(1);
MutableHandleValue rhs = SPHANDLEMUT(0);
MutableHandleValue result = SPHANDLEMUT(1);
{
PUSH_EXIT_FRAME();
if (!SubOperation(cx, lhs, rhs, result)) {
GOTO_ERROR();
}
}
VIRTPOP();
NEXT_IC();
END_OP(Sub);
}
goto generic_binary;
}
CASE(Mul) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
int64_t lhs = v1.toInt32();
int64_t rhs = v0.toInt32();
int64_t product = lhs * rhs;
if (product >= int64_t(INT32_MIN) &&
product <= int64_t(INT32_MAX) &&
(product != 0 || !((lhs < 0) ^ (rhs < 0)))) {
VIRTPOP();
VIRTSPWRITE(0, StackVal(Int32Value(int32_t(product))));
NEXT_IC();
END_OP(Mul);
}
}
if (v0.isNumber() && v1.isNumber()) {
double lhs = v1.toNumber();
double rhs = v0.toNumber();
VIRTPOP();
VIRTSPWRITE(0, StackVal(NumberValue(lhs * rhs)));
NEXT_IC();
END_OP(Mul);
}
MutableHandleValue lhs = SPHANDLEMUT(1);
MutableHandleValue rhs = SPHANDLEMUT(0);
MutableHandleValue result = SPHANDLEMUT(1);
{
PUSH_EXIT_FRAME();
if (!MulOperation(cx, lhs, rhs, result)) {
GOTO_ERROR();
}
}
VIRTPOP();
NEXT_IC();
END_OP(Mul);
}
goto generic_binary;
}
CASE(Div) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isNumber() && v1.isNumber()) {
double lhs = v1.toNumber();
double rhs = v0.toNumber();
VIRTPOP();
VIRTSPWRITE(0, StackVal(NumberValue(NumberDiv(lhs, rhs))));
NEXT_IC();
END_OP(Div);
}
MutableHandleValue lhs = SPHANDLEMUT(1);
MutableHandleValue rhs = SPHANDLEMUT(0);
MutableHandleValue result = SPHANDLEMUT(1);
{
PUSH_EXIT_FRAME();
if (!DivOperation(cx, lhs, rhs, result)) {
GOTO_ERROR();
}
}
VIRTPOP();
NEXT_IC();
END_OP(Div);
}
goto generic_binary;
}
CASE(Mod) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
int64_t lhs = v1.toInt32();
int64_t rhs = v0.toInt32();
if (lhs > 0 && rhs > 0) {
int64_t mod = lhs % rhs;
VIRTPOP();
VIRTSPWRITE(0, StackVal(Int32Value(int32_t(mod))));
NEXT_IC();
END_OP(Mod);
}
}
if (v0.isNumber() && v1.isNumber()) {
double lhs = v1.toNumber();
double rhs = v0.toNumber();
VIRTPOP();
VIRTSPWRITE(0, StackVal(DoubleValue(NumberMod(lhs, rhs))));
NEXT_IC();
END_OP(Mod);
}
MutableHandleValue lhs = SPHANDLEMUT(1);
MutableHandleValue rhs = SPHANDLEMUT(0);
MutableHandleValue result = SPHANDLEMUT(1);
{
PUSH_EXIT_FRAME();
if (!ModOperation(cx, lhs, rhs, result)) {
GOTO_ERROR();
}
}
VIRTPOP();
NEXT_IC();
END_OP(Mod);
}
goto generic_binary;
}
CASE(Pow) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isNumber() && v1.isNumber()) {
double lhs = v1.toNumber();
double rhs = v0.toNumber();
VIRTPOP();
VIRTSPWRITE(0, StackVal(NumberValue(ecmaPow(lhs, rhs))));
NEXT_IC();
END_OP(Pow);
}
MutableHandleValue lhs = SPHANDLEMUT(1);
MutableHandleValue rhs = SPHANDLEMUT(0);
MutableHandleValue result = SPHANDLEMUT(1);
{
PUSH_EXIT_FRAME();
if (!PowOperation(cx, lhs, rhs, result)) {
GOTO_ERROR();
}
}
VIRTPOP();
NEXT_IC();
END_OP(Pow);
}
goto generic_binary;
}
CASE(BitOr) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
int32_t lhs = v1.toInt32();
int32_t rhs = v0.toInt32();
VIRTPOP();
VIRTSPWRITE(0, StackVal(Int32Value(lhs | rhs)));
NEXT_IC();
END_OP(BitOr);
}
}
goto generic_binary;
}
CASE(BitAnd) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
int32_t lhs = v1.toInt32();
int32_t rhs = v0.toInt32();
VIRTPOP();
VIRTSPWRITE(0, StackVal(Int32Value(lhs & rhs)));
NEXT_IC();
END_OP(BitAnd);
}
}
goto generic_binary;
}
CASE(BitXor) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
int32_t lhs = v1.toInt32();
int32_t rhs = v0.toInt32();
VIRTPOP();
VIRTSPWRITE(0, StackVal(Int32Value(lhs ^ rhs)));
NEXT_IC();
END_OP(BitXor);
}
}
goto generic_binary;
}
CASE(Lsh) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
// Unsigned to avoid undefined behavior on left-shift overflow
// (see comment in BitLshOperation in Interpreter.cpp).
uint32_t lhs = uint32_t(v1.toInt32());
uint32_t rhs = uint32_t(v0.toInt32());
VIRTPOP();
rhs &= 31;
VIRTSPWRITE(0, StackVal(Int32Value(int32_t(lhs << rhs))));
NEXT_IC();
END_OP(Lsh);
}
}
goto generic_binary;
}
CASE(Rsh) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
int32_t lhs = v1.toInt32();
int32_t rhs = v0.toInt32();
VIRTPOP();
rhs &= 31;
VIRTSPWRITE(0, StackVal(Int32Value(lhs >> rhs)));
NEXT_IC();
END_OP(Rsh);
}
}
goto generic_binary;
}
CASE(Ursh) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
uint32_t lhs = uint32_t(v1.toInt32());
int32_t rhs = v0.toInt32();
VIRTPOP();
rhs &= 31;
uint32_t result = lhs >> rhs;
StackVal stackResult(0);
if (result <= uint32_t(INT32_MAX)) {
stackResult = StackVal(Int32Value(int32_t(result)));
} else {
stackResult = StackVal(NumberValue(double(result)));
}
VIRTSPWRITE(0, stackResult);
NEXT_IC();
END_OP(Ursh);
}
}
goto generic_binary;
}
generic_binary:;
{
static_assert(JSOpLength_BitOr == JSOpLength_BitXor);
static_assert(JSOpLength_BitOr == JSOpLength_BitAnd);
static_assert(JSOpLength_BitOr == JSOpLength_Lsh);
static_assert(JSOpLength_BitOr == JSOpLength_Rsh);
static_assert(JSOpLength_BitOr == JSOpLength_Ursh);
static_assert(JSOpLength_BitOr == JSOpLength_Add);
static_assert(JSOpLength_BitOr == JSOpLength_Sub);
static_assert(JSOpLength_BitOr == JSOpLength_Mul);
static_assert(JSOpLength_BitOr == JSOpLength_Div);
static_assert(JSOpLength_BitOr == JSOpLength_Mod);
static_assert(JSOpLength_BitOr == JSOpLength_Pow);
IC_POP_ARG(1);
IC_POP_ARG(0);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(BinaryArith, false);
END_OP(Div);
}
CASE(Eq) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
bool result = v0.toInt32() == v1.toInt32();
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Eq);
}
if (v0.isNumber() && v1.isNumber()) {
double lhs = v1.toNumber();
double rhs = v0.toNumber();
bool result = lhs == rhs;
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Eq);
}
if (v0.isNumber() && v1.isNumber()) {
bool result = v0.toNumber() == v1.toNumber();
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Eq);
}
}
goto generic_cmp;
}
CASE(Ne) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
bool result = v0.toInt32() != v1.toInt32();
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Ne);
}
if (v0.isNumber() && v1.isNumber()) {
double lhs = v1.toNumber();
double rhs = v0.toNumber();
bool result = lhs != rhs;
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Ne);
}
if (v0.isNumber() && v1.isNumber()) {
bool result = v0.toNumber() != v1.toNumber();
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Eq);
}
}
goto generic_cmp;
}
CASE(Lt) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
bool result = v1.toInt32() < v0.toInt32();
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Lt);
}
if (v0.isNumber() && v1.isNumber()) {
double lhs = v1.toNumber();
double rhs = v0.toNumber();
bool result = lhs < rhs;
if (std::isnan(lhs) || std::isnan(rhs)) {
result = false;
}
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Lt);
}
}
goto generic_cmp;
}
CASE(Le) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
bool result = v1.toInt32() <= v0.toInt32();
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Le);
}
if (v0.isNumber() && v1.isNumber()) {
double lhs = v1.toNumber();
double rhs = v0.toNumber();
bool result = lhs <= rhs;
if (std::isnan(lhs) || std::isnan(rhs)) {
result = false;
}
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Le);
}
}
goto generic_cmp;
}
CASE(Gt) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
bool result = v1.toInt32() > v0.toInt32();
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Gt);
}
if (v0.isNumber() && v1.isNumber()) {
double lhs = v1.toNumber();
double rhs = v0.toNumber();
bool result = lhs > rhs;
if (std::isnan(lhs) || std::isnan(rhs)) {
result = false;
}
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Gt);
}
}
goto generic_cmp;
}
CASE(Ge) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
bool result = v1.toInt32() >= v0.toInt32();
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Ge);
}
if (v0.isNumber() && v1.isNumber()) {
double lhs = v1.toNumber();
double rhs = v0.toNumber();
bool result = lhs >= rhs;
if (std::isnan(lhs) || std::isnan(rhs)) {
result = false;
}
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Ge);
}
}
goto generic_cmp;
}
CASE(StrictEq)
CASE(StrictNe) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
bool result;
HandleValue lval = SPHANDLE(1);
HandleValue rval = SPHANDLE(0);
if (v0.isString() && v1.isString()) {
PUSH_EXIT_FRAME();
if (!js::StrictlyEqual(cx, lval, rval, &result)) {
GOTO_ERROR();
}
} else {
if (!js::StrictlyEqual(nullptr, lval, rval, &result)) {
GOTO_ERROR();
}
}
VIRTPOP();
VIRTSPWRITE(0,
StackVal(BooleanValue(
(JSOp(*pc) == JSOp::StrictEq) ? result : !result)));
NEXT_IC();
END_OP(StrictEq);
} else {
goto generic_cmp;
}
}
generic_cmp:;
{
static_assert(JSOpLength_Eq == JSOpLength_Ne);
static_assert(JSOpLength_Eq == JSOpLength_StrictEq);
static_assert(JSOpLength_Eq == JSOpLength_StrictNe);
static_assert(JSOpLength_Eq == JSOpLength_Lt);
static_assert(JSOpLength_Eq == JSOpLength_Gt);
static_assert(JSOpLength_Eq == JSOpLength_Le);
static_assert(JSOpLength_Eq == JSOpLength_Ge);
IC_POP_ARG(1);
IC_POP_ARG(0);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(Compare, false);
END_OP(Eq);
}
CASE(StrictConstantNe)
CASE(StrictConstantEq) {
JSOp op = JSOp(*pc);
uint16_t operand = GET_UINT16(pc);
{
ReservedRooted<JS::Value> val(&state.value0, VIRTPOP().asValue());
bool result;
{
PUSH_EXIT_FRAME();
if (!js::ConstantStrictEqual(cx, val, operand, &result)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(
BooleanValue(op == JSOp::StrictConstantEq ? result : !result)));
}
END_OP(StrictConstantEq);
}
CASE(Instanceof) {
IC_POP_ARG(1);
IC_POP_ARG(0);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(InstanceOf, false);
END_OP(Instanceof);
}
CASE(In) {
IC_POP_ARG(1);
IC_POP_ARG(0);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(In, false);
END_OP(In);
}
CASE(ToPropertyKey) {
IC_POP_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(ToPropertyKey, false);
END_OP(ToPropertyKey);
}
CASE(ToString) {
if (VIRTSP(0).asValue().isString()) {
END_OP(ToString);
}
{
ReservedRooted<Value> value0(&state.value0, VIRTPOP().asValue());
if (JSString* result = ToStringSlow<NoGC>(
ctx.frameMgr.cxForLocalUseOnly(), value0)) {
VIRTPUSH(StackVal(StringValue(result)));
} else {
{
PUSH_EXIT_FRAME();
result = ToString<CanGC>(cx, value0);
if (!result) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(StringValue(result)));
}
}
END_OP(ToString);
}
CASE(IsNullOrUndefined) {
Value v = VIRTSP(0).asValue();
bool result = v.isNull() || v.isUndefined();
VIRTPUSH(StackVal(BooleanValue(result)));
END_OP(IsNullOrUndefined);
}
CASE(GlobalThis) {
VIRTPUSH(StackVal(ObjectValue(*ctx.frameMgr.cxForLocalUseOnly()
->global()
->lexicalEnvironment()
.thisObject())));
END_OP(GlobalThis);
}
CASE(NonSyntacticGlobalThis) {
{
ReservedRooted<JSObject*> obj0(&state.obj0,
frame->environmentChain());
ReservedRooted<Value> value0(&state.value0);
{
PUSH_EXIT_FRAME();
js::GetNonSyntacticGlobalThis(cx, obj0, &value0);
}
VIRTPUSH(StackVal(value0));
}
END_OP(NonSyntacticGlobalThis);
}
CASE(NewTarget) {
VIRTPUSH(StackVal(frame->newTarget()));
END_OP(NewTarget);
}
CASE(DynamicImport) {
{
ReservedRooted<Value> value0(&state.value0,
VIRTPOP().asValue()); // options
ReservedRooted<Value> value1(&state.value1,
VIRTPOP().asValue()); // specifier
JSObject* promise;
{
PUSH_EXIT_FRAME();
ReservedRooted<JSScript*> script0(&state.script0, frame->script());
promise = StartDynamicModuleImport(cx, script0, value1, value0);
if (!promise) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(ObjectValue(*promise)));
}
END_OP(DynamicImport);
}
CASE(ImportMeta) {
IC_ZERO_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(LazyConstant, false);
END_OP(ImportMeta);
}
CASE(NewInit) {
if (HybridICs) {
JSObject* obj;
{
PUSH_EXIT_FRAME();
ReservedRooted<JSScript*> script0(&state.script0, frame->script());
obj = NewObjectOperation(cx, script0, pc);
if (!obj) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(ObjectValue(*obj)));
NEXT_IC();
END_OP(NewInit);
} else {
IC_ZERO_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(NewObject, false);
END_OP(NewInit);
}
}
CASE(NewObject) {
if (HybridICs) {
JSObject* obj;
{
PUSH_EXIT_FRAME();
ReservedRooted<JSScript*> script0(&state.script0, frame->script());
obj = NewObjectOperation(cx, script0, pc);
if (!obj) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(ObjectValue(*obj)));
NEXT_IC();
END_OP(NewObject);
} else {
IC_ZERO_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(NewObject, false);
END_OP(NewObject);
}
}
CASE(Object) {
VIRTPUSH(StackVal(ObjectValue(*frame->script()->getObject(pc))));
END_OP(Object);
}
CASE(ObjWithProto) {
{
ReservedRooted<Value> value0(&state.value0, VIRTSP(0).asValue());
JSObject* obj;
{
PUSH_EXIT_FRAME();
obj = ObjectWithProtoOperation(cx, value0);
if (!obj) {
GOTO_ERROR();
}
}
VIRTSPWRITE(0, StackVal(ObjectValue(*obj)));
}
END_OP(ObjWithProto);
}
CASE(InitElem)
CASE(InitHiddenElem)
CASE(InitLockedElem)
CASE(InitElemInc)
CASE(SetElem)
CASE(StrictSetElem) {
static_assert(JSOpLength_InitElem == JSOpLength_InitHiddenElem);
static_assert(JSOpLength_InitElem == JSOpLength_InitLockedElem);
static_assert(JSOpLength_InitElem == JSOpLength_InitElemInc);
static_assert(JSOpLength_InitElem == JSOpLength_SetElem);
static_assert(JSOpLength_InitElem == JSOpLength_StrictSetElem);
StackVal val = VIRTSP(0);
IC_POP_ARG(2);
IC_POP_ARG(1);
IC_SET_ARG_FROM_STACK(0, 0);
if (JSOp(*pc) == JSOp::SetElem || JSOp(*pc) == JSOp::StrictSetElem) {
VIRTSPWRITE(0, val);
}
INVOKE_IC(SetElem, true);
if (JSOp(*pc) == JSOp::InitElemInc) {
VIRTPUSH(
StackVal(Int32Value(Value::fromRawBits(ic_arg1).toInt32() + 1)));
}
END_OP(InitElem);
}
CASE(InitPropGetter)
CASE(InitHiddenPropGetter)
CASE(InitPropSetter)
CASE(InitHiddenPropSetter) {
static_assert(JSOpLength_InitPropGetter ==
JSOpLength_InitHiddenPropGetter);
static_assert(JSOpLength_InitPropGetter == JSOpLength_InitPropSetter);
static_assert(JSOpLength_InitPropGetter ==
JSOpLength_InitHiddenPropSetter);
{
ReservedRooted<JSObject*> obj1(
&state.obj1,
&VIRTPOP().asValue().toObject()); // val
ReservedRooted<JSObject*> obj0(
&state.obj0,
&VIRTSP(0).asValue().toObject()); // obj; leave on stack
ReservedRooted<PropertyName*> name0(&state.name0,
frame->script()->getName(pc));
{
PUSH_EXIT_FRAME();
if (!InitPropGetterSetterOperation(cx, pc, obj0, name0, obj1)) {
GOTO_ERROR();
}
}
}
END_OP(InitPropGetter);
}
CASE(InitElemGetter)
CASE(InitHiddenElemGetter)
CASE(InitElemSetter)
CASE(InitHiddenElemSetter) {
static_assert(JSOpLength_InitElemGetter ==
JSOpLength_InitHiddenElemGetter);
static_assert(JSOpLength_InitElemGetter == JSOpLength_InitElemSetter);
static_assert(JSOpLength_InitElemGetter ==
JSOpLength_InitHiddenElemSetter);
{
ReservedRooted<JSObject*> obj1(
&state.obj1,
&VIRTPOP().asValue().toObject()); // val
ReservedRooted<Value> value0(&state.value0,
VIRTPOP().asValue()); // idval
ReservedRooted<JSObject*> obj0(
&state.obj0,
&VIRTSP(0).asValue().toObject()); // obj; leave on stack
{
PUSH_EXIT_FRAME();
if (!InitElemGetterSetterOperation(cx, pc, obj0, value0, obj1)) {
GOTO_ERROR();
}
}
}
END_OP(InitElemGetter);
}
CASE(GetProp)
CASE(GetBoundName) {
static_assert(JSOpLength_GetProp == JSOpLength_GetBoundName);
IC_POP_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(GetProp, false);
END_OP(GetProp);
}
CASE(GetPropSuper) {
IC_POP_ARG(0);
IC_POP_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(GetPropSuper, false);
END_OP(GetPropSuper);
}
CASE(GetElem) {
if (HybridICs && VIRTSP(1).asValue().isString()) {
HandleValue lhs = SPHANDLE(1);
HandleValue rhs = SPHANDLE(0);
uint32_t index;
if (IsDefinitelyIndex(rhs, &index)) {
JSString* str = lhs.toString();
if (index < str->length() && str->isLinear()) {
JSLinearString* linear = &str->asLinear();
char16_t c = linear->latin1OrTwoByteChar(index);
StaticStrings& sstr =
ctx.frameMgr.cxForLocalUseOnly()->staticStrings();
if (sstr.hasUnit(c)) {
VIRTPOP();
VIRTSPWRITE(0, StackVal(StringValue(sstr.getUnit(c))));
NEXT_IC();
END_OP(GetElem);
}
}
}
}
IC_POP_ARG(1);
IC_POP_ARG(0);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(GetElem, false);
END_OP(GetElem);
}
CASE(GetElemSuper) {
// N.B.: second and third args are out of order! See the saga at
// https://bugzilla.mozilla.org/show_bug.cgi?id=1709328; this is
// an echo of that issue.
IC_POP_ARG(1);
IC_POP_ARG(2);
IC_POP_ARG(0);
INVOKE_IC_AND_PUSH(GetElemSuper, true);
END_OP(GetElemSuper);
}
CASE(DelProp) {
{
ReservedRooted<Value> value0(&state.value0, VIRTPOP().asValue());
ReservedRooted<PropertyName*> name0(&state.name0,
frame->script()->getName(pc));
bool res = false;
{
PUSH_EXIT_FRAME();
if (!DelPropOperation<false>(cx, value0, name0, &res)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(BooleanValue(res)));
}
END_OP(DelProp);
}
CASE(StrictDelProp) {
{
ReservedRooted<Value> value0(&state.value0, VIRTPOP().asValue());
ReservedRooted<PropertyName*> name0(&state.name0,
frame->script()->getName(pc));
bool res = false;
{
PUSH_EXIT_FRAME();
if (!DelPropOperation<true>(cx, value0, name0, &res)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(BooleanValue(res)));
}
END_OP(StrictDelProp);
}
CASE(DelElem) {
{
ReservedRooted<Value> value1(&state.value1, VIRTPOP().asValue());
ReservedRooted<Value> value0(&state.value0, VIRTPOP().asValue());
bool res = false;
{
PUSH_EXIT_FRAME();
if (!DelElemOperation<false>(cx, value0, value1, &res)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(BooleanValue(res)));
}
END_OP(DelElem);
}
CASE(StrictDelElem) {
{
ReservedRooted<Value> value1(&state.value1, VIRTPOP().asValue());
ReservedRooted<Value> value0(&state.value0, VIRTPOP().asValue());
bool res = false;
{
PUSH_EXIT_FRAME();
if (!DelElemOperation<true>(cx, value0, value1, &res)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(BooleanValue(res)));
}
END_OP(StrictDelElem);
}
CASE(HasOwn) {
IC_POP_ARG(1);
IC_POP_ARG(0);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(HasOwn, false);
END_OP(HasOwn);
}
CASE(CheckPrivateField) {
IC_SET_ARG_FROM_STACK(1, 0);
IC_SET_ARG_FROM_STACK(0, 1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(CheckPrivateField, false);
END_OP(CheckPrivateField);
}
CASE(NewPrivateName) {
{
ReservedRooted<JSAtom*> atom0(&state.atom0,
frame->script()->getAtom(pc));
JS::Symbol* symbol;
{
PUSH_EXIT_FRAME();
symbol = NewPrivateName(cx, atom0);
if (!symbol) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(SymbolValue(symbol)));
}
END_OP(NewPrivateName);
}
CASE(SuperBase) {
JSFunction& superEnvFunc =
VIRTPOP().asValue().toObject().as<JSFunction>();
MOZ_ASSERT(superEnvFunc.allowSuperProperty());
MOZ_ASSERT(superEnvFunc.baseScript()->needsHomeObject());
const Value& homeObjVal = superEnvFunc.getExtendedSlot(
FunctionExtended::METHOD_HOMEOBJECT_SLOT);
JSObject* homeObj = &homeObjVal.toObject();
JSObject* superBase = HomeObjectSuperBase(homeObj);
VIRTPUSH(StackVal(ObjectOrNullValue(superBase)));
END_OP(SuperBase);
}
CASE(SetPropSuper)
CASE(StrictSetPropSuper) {
// stack signature: receiver, lval, rval => rval
static_assert(JSOpLength_SetPropSuper == JSOpLength_StrictSetPropSuper);
bool strict = JSOp(*pc) == JSOp::StrictSetPropSuper;
{
ReservedRooted<Value> value2(&state.value2,
VIRTPOP().asValue()); // rval
ReservedRooted<Value> value1(&state.value1,
VIRTPOP().asValue()); // lval
ReservedRooted<Value> value0(&state.value0,
VIRTPOP().asValue()); // recevier
ReservedRooted<PropertyName*> name0(&state.name0,
frame->script()->getName(pc));
{
PUSH_EXIT_FRAME();
// SetPropertySuper(cx, lval, receiver, name, rval, strict)
// (N.B.: lval and receiver are transposed!)
if (!SetPropertySuper(cx, value1, value0, name0, value2, strict)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(value2));
}
END_OP(SetPropSuper);
}
CASE(SetElemSuper)
CASE(StrictSetElemSuper) {
// stack signature: receiver, key, lval, rval => rval
static_assert(JSOpLength_SetElemSuper == JSOpLength_StrictSetElemSuper);
bool strict = JSOp(*pc) == JSOp::StrictSetElemSuper;
{
ReservedRooted<Value> value3(&state.value3,
VIRTPOP().asValue()); // rval
ReservedRooted<Value> value2(&state.value2,
VIRTPOP().asValue()); // lval
ReservedRooted<Value> value1(&state.value1,
VIRTPOP().asValue()); // index
ReservedRooted<Value> value0(&state.value0,
VIRTPOP().asValue()); // receiver
{
PUSH_EXIT_FRAME();
// SetElementSuper(cx, lval, receiver, index, rval, strict)
// (N.B.: lval, receiver and index are rotated!)
if (!SetElementSuper(cx, value2, value0, value1, value3, strict)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(value3)); // value
}
END_OP(SetElemSuper);
}
CASE(Iter) {
IC_POP_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(GetIterator, false);
END_OP(Iter);
}
CASE(MoreIter) {
// iter => iter, name
Value v = IteratorMore(&VIRTSP(0).asValue().toObject());
VIRTPUSH(StackVal(v));
END_OP(MoreIter);
}
CASE(IsNoIter) {
// iter => iter, bool
bool result = VIRTSP(0).asValue().isMagic(JS_NO_ITER_VALUE);
VIRTPUSH(StackVal(BooleanValue(result)));
END_OP(IsNoIter);
}
CASE(EndIter) {
// iter, interval =>
VIRTPOP();
CloseIterator(&VIRTPOP().asValue().toObject());
END_OP(EndIter);
}
CASE(CloseIter) {
IC_SET_OBJ_ARG(0, &VIRTPOP().asValue().toObject());
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC(CloseIter, false);
END_OP(CloseIter);
}
CASE(CheckIsObj) {
if (!VIRTSP(0).asValue().isObject()) {
PUSH_EXIT_FRAME();
MOZ_ALWAYS_FALSE(
js::ThrowCheckIsObject(cx, js::CheckIsObjectKind(GET_UINT8(pc))));
/* abandon frame; error handler will re-establish sp */
GOTO_ERROR();
}
END_OP(CheckIsObj);
}
CASE(CheckObjCoercible) {
{
ReservedRooted<Value> value0(&state.value0, VIRTSP(0).asValue());
if (value0.isNullOrUndefined()) {
PUSH_EXIT_FRAME();
MOZ_ALWAYS_FALSE(ThrowObjectCoercible(cx, value0));
/* abandon frame; error handler will re-establish sp */
GOTO_ERROR();
}
}
END_OP(CheckObjCoercible);
}
CASE(ToAsyncIter) {
// iter, next => asynciter
{
ReservedRooted<Value> value0(&state.value0,
VIRTPOP().asValue()); // next
ReservedRooted<JSObject*> obj0(
&state.obj0,
&VIRTPOP().asValue().toObject()); // iter
JSObject* result;
{
PUSH_EXIT_FRAME();
result = CreateAsyncFromSyncIterator(cx, obj0, value0);
if (!result) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(ObjectValue(*result)));
}
END_OP(ToAsyncIter);
}
CASE(MutateProto) {
// obj, protoVal => obj
{
ReservedRooted<Value> value0(&state.value0, VIRTPOP().asValue());
ReservedRooted<JSObject*> obj0(&state.obj0,
&VIRTSP(0).asValue().toObject());
{
PUSH_EXIT_FRAME();
if (!MutatePrototype(cx, obj0.as<PlainObject>(), value0)) {
GOTO_ERROR();
}
}
}
END_OP(MutateProto);
}
CASE(NewArray) {
if (HybridICs) {
ArrayObject* obj;
{
PUSH_EXIT_FRAME();
uint32_t length = GET_UINT32(pc);
obj = NewArrayOperation(cx, length);
if (!obj) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(ObjectValue(*obj)));
NEXT_IC();
END_OP(NewArray);
} else {
IC_ZERO_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(NewArray, false);
END_OP(NewArray);
}
}
CASE(InitElemArray) {
// array, val => array
{
ReservedRooted<Value> value0(&state.value0, VIRTPOP().asValue());
ReservedRooted<JSObject*> obj0(&state.obj0,
&VIRTSP(0).asValue().toObject());
{
PUSH_EXIT_FRAME();
InitElemArrayOperation(cx, pc, obj0.as<ArrayObject>(), value0);
}
}
END_OP(InitElemArray);
}
CASE(Hole) {
VIRTPUSH(StackVal(MagicValue(JS_ELEMENTS_HOLE)));
END_OP(Hole);
}
CASE(RegExp) {
JSObject* obj;
{
PUSH_EXIT_FRAME();
ReservedRooted<JSObject*> obj0(&state.obj0,
frame->script()->getRegExp(pc));
obj = CloneRegExpObject(cx, obj0.as<RegExpObject>());
if (!obj) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(ObjectValue(*obj)));
END_OP(RegExp);
}
CASE(Lambda) {
if (HybridICs) {
JSObject* clone;
{
PUSH_EXIT_FRAME();
ReservedRooted<JSFunction*> fun0(&state.fun0,
frame->script()->getFunction(pc));
ReservedRooted<JSObject*> obj0(&state.obj0,
frame->environmentChain());
clone = Lambda(cx, fun0, obj0);
if (!clone) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(ObjectValue(*clone)));
NEXT_IC();
END_OP(Lambda);
} else {
IC_ZERO_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(Lambda, false);
END_OP(Lambda);
}
}
CASE(SetFunName) {
// fun, name => fun
{
ReservedRooted<Value> value0(&state.value0,
VIRTPOP().asValue()); // name
ReservedRooted<JSFunction*> fun0(
&state.fun0, &VIRTSP(0).asValue().toObject().as<JSFunction>());
FunctionPrefixKind prefixKind = FunctionPrefixKind(GET_UINT8(pc));
{
PUSH_EXIT_FRAME();
if (!SetFunctionName(cx, fun0, value0, prefixKind)) {
GOTO_ERROR();
}
}
}
END_OP(SetFunName);
}
CASE(InitHomeObject) {
// fun, homeObject => fun
{
ReservedRooted<JSObject*> obj0(
&state.obj0, &VIRTPOP().asValue().toObject()); // homeObject
ReservedRooted<JSFunction*> fun0(
&state.fun0, &VIRTSP(0).asValue().toObject().as<JSFunction>());
MOZ_ASSERT(fun0->allowSuperProperty());
MOZ_ASSERT(obj0->is<PlainObject>() || obj0->is<JSFunction>());
fun0->setExtendedSlot(FunctionExtended::METHOD_HOMEOBJECT_SLOT,
ObjectValue(*obj0));
}
END_OP(InitHomeObject);
}
CASE(CheckClassHeritage) {
{
ReservedRooted<Value> value0(&state.value0, VIRTSP(0).asValue());
{
PUSH_EXIT_FRAME();
if (!CheckClassHeritageOperation(cx, value0)) {
GOTO_ERROR();
}
}
}
END_OP(CheckClassHeritage);
}
CASE(FunWithProto) {
// proto => obj
{
ReservedRooted<JSObject*> obj0(
&state.obj0,
&VIRTPOP().asValue().toObject()); // proto
ReservedRooted<JSObject*> obj1(&state.obj1,
frame->environmentChain());
ReservedRooted<JSFunction*> fun0(&state.fun0,
frame->script()->getFunction(pc));
JSObject* obj;
{
PUSH_EXIT_FRAME();
obj = FunWithProtoOperation(cx, fun0, obj1, obj0);
if (!obj) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(ObjectValue(*obj)));
}
END_OP(FunWithProto);
}
CASE(BuiltinObject) {
IC_ZERO_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(LazyConstant, false);
END_OP(BuiltinObject);
}
CASE(Call)
CASE(CallIgnoresRv)
CASE(CallContent)
CASE(CallIter)
CASE(CallContentIter)
CASE(Eval)
CASE(StrictEval)
CASE(SuperCall)
CASE(New)
CASE(NewContent) {
static_assert(JSOpLength_Call == JSOpLength_CallIgnoresRv);
static_assert(JSOpLength_Call == JSOpLength_CallContent);
static_assert(JSOpLength_Call == JSOpLength_CallIter);
static_assert(JSOpLength_Call == JSOpLength_CallContentIter);
static_assert(JSOpLength_Call == JSOpLength_Eval);
static_assert(JSOpLength_Call == JSOpLength_StrictEval);
static_assert(JSOpLength_Call == JSOpLength_SuperCall);
static_assert(JSOpLength_Call == JSOpLength_New);
static_assert(JSOpLength_Call == JSOpLength_NewContent);
JSOp op = JSOp(*pc);
bool constructing = (op == JSOp::New || op == JSOp::NewContent ||
op == JSOp::SuperCall);
uint32_t argc = GET_ARGC(pc);
do {
{
// CallArgsFromSp would be called with
// - numValues = argc + 2 + constructing
// - stackSlots = argc + constructing
// - sp = vp + numValues
// CallArgs::create then gets
// - argc_ = stackSlots - constructing = argc
// - argv_ = sp - stackSlots = vp + 2
// our arguments are in reverse order compared to what CallArgs
// expects so we should subtract any array subscripts from (sp +
// stackSlots - 1)
StackVal* firstArg = sp + argc + constructing - 1;
// callee is argv_[-2] -> sp + argc + constructing + 1
// this is argv_[-1] -> sp + argc + constructing
// newTarget is argv_[argc_] -> sp + constructing - 1
// but this/newTarget are only used when constructing is 1 so we can
// simplify this is argv_[-1] -> sp + argc + 1 newTarget is
// argv_[argc_] -> sp
HandleValue callee = Stack::handle(firstArg + 2);
if (!callee.isObject() || !callee.toObject().is<JSFunction>()) {
TRACE_PRINTF("missed fastpath: not a function\n");
break;
}
ReservedRooted<JSFunction*> func(
&state.fun0, &callee.toObject().as<JSFunction>());
if (!func->hasBaseScript() || !func->isInterpreted()) {
TRACE_PRINTF("missed fastpath: not an interpreted script\n");
break;
}
if (!constructing && func->isClassConstructor()) {
TRACE_PRINTF(
"missed fastpath: constructor called without `new`\n");
break;
}
if (constructing && !func->isConstructor()) {
TRACE_PRINTF(
"missed fastpath: constructing with a non-constructor\n");
break;
}
if (!func->baseScript()->hasBytecode()) {
TRACE_PRINTF("missed fastpath: no bytecode\n");
break;
}
ReservedRooted<JSScript*> calleeScript(
&state.script0, func->baseScript()->asJSScript());
if (!calleeScript->hasJitScript()) {
TRACE_PRINTF("missed fastpath: no jit-script\n");
break;
}
if (ctx.frameMgr.cxForLocalUseOnly()->realm() !=
calleeScript->realm()) {
TRACE_PRINTF("missed fastpath: mismatched realm\n");
break;
}
if (argc < func->nargs()) {
TRACE_PRINTF("missed fastpath: not enough arguments\n");
break;
}
// Fast-path: function, interpreted, has JitScript, same realm, no
// argument underflow.
// Include newTarget in the args if it exists; exclude callee
uint32_t totalArgs = argc + 1 + constructing;
StackVal* origArgs = sp;
TRACE_PRINTF(
"Call fastpath: argc = %d origArgs = %p callee = %" PRIx64 "\n",
argc, origArgs, callee.get().asRawBits());
if (!ctx.stack.check(sp, sizeof(StackVal) * (totalArgs + 3))) {
TRACE_PRINTF("missed fastpath: would cause stack overrun\n");
break;
}
if (constructing) {
MutableHandleValue thisv = Stack::handleMut(firstArg + 1);
if (!thisv.isObject()) {
HandleValue newTarget = Stack::handle(firstArg - argc);
ReservedRooted<JSObject*> obj0(&state.obj0,
&newTarget.toObject());
PUSH_EXIT_FRAME();
// CreateThis might discard the JitScript but we're counting on
// it continuing to exist while we evaluate the fastpath.
AutoKeepJitScripts keepJitScript(cx);
if (!CreateThis(cx, func, obj0, GenericObject, thisv)) {
GOTO_ERROR();
}
TRACE_PRINTF("created %" PRIx64 "\n", thisv.get().asRawBits());
}
}
// 0. Save current PC and interpreter IC pointer in
// current frame, so we can retrieve them later.
frame->interpreterPC() = pc;
frame->interpreterICEntry() = icEntry;
// 1. Push a baseline stub frame. Don't use the frame manager
// -- we don't want the frame to be auto-freed when we leave
// this scope, and we don't want to shadow `sp`.
StackVal* exitFP = ctx.stack.pushExitFrame(sp, frame);
MOZ_ASSERT(exitFP); // safety: stack margin.
sp = exitFP;
TRACE_PRINTF("exit frame at %p\n", exitFP);
// 2. Modify exit code to nullptr (this is where ICStubReg is
// normally saved; the tracing code can skip if null).
PUSHNATIVE(StackValNative(nullptr));
// 3. Push args in proper order (they are reversed in our
// downward-growth stack compared to what the calling
// convention expects).
for (uint32_t i = 0; i < totalArgs; i++) {
VIRTPUSH(origArgs[i]);
}
// 4. Push inter-frame content: callee token, descriptor for
// above.
PUSHNATIVE(StackValNative(CalleeToToken(func, constructing)));
PUSHNATIVE(StackValNative(
MakeFrameDescriptorForJitCall(FrameType::BaselineStub, argc)));
// 5. Push fake return address, set script, push baseline frame.
PUSHNATIVE(StackValNative(nullptr));
BaselineFrame* newFrame =
ctx.stack.pushFrame(sp, ctx.frameMgr.cxForLocalUseOnly(),
/* envChain = */ func->environment());
MOZ_ASSERT(newFrame); // safety: stack margin.
TRACE_PRINTF("callee frame at %p\n", newFrame);
frame = newFrame;
ctx.frameMgr.switchToFrame(frame);
ctx.frame = frame;
// 6. Set up PC and SP for callee.
sp = reinterpret_cast<StackVal*>(frame);
RESET_PC(calleeScript->code(), calleeScript);
// 7. Check callee stack space for max stack depth.
if (!stack.check(sp, sizeof(StackVal) * calleeScript->nslots())) {
PUSH_EXIT_FRAME();
ReportOverRecursed(ctx.frameMgr.cxForLocalUseOnly());
GOTO_ERROR();
}
// 8. Push local slots, and set return value to `undefined` by
// default.
uint32_t nfixed = calleeScript->nfixed();
for (uint32_t i = 0; i < nfixed; i++) {
VIRTPUSH(StackVal(UndefinedValue()));
}
ret->setUndefined();
// 9. Initialize environment objects.
if (func->needsFunctionEnvironmentObjects()) {
PUSH_EXIT_FRAME();
if (!js::InitFunctionEnvironmentObjects(cx, frame)) {
GOTO_ERROR();
}
}
// 10. Set debug flag, if appropriate.
if (frame->script()->isDebuggee()) {
TRACE_PRINTF("Script is debuggee\n");
frame->setIsDebuggee();
PUSH_EXIT_FRAME();
if (!DebugPrologue(cx, frame)) {
GOTO_ERROR();
}
}
// 11. Check for interrupts.
#ifdef ENABLE_INTERRUPT_CHECKS
if (ctx.frameMgr.cxForLocalUseOnly()->hasAnyPendingInterrupt()) {
PUSH_EXIT_FRAME();
if (!InterruptCheck(cx)) {
GOTO_ERROR();
}
}
#endif
// 12. Initialize coverage tables, if needed.
if (!frame->script()->hasScriptCounts()) {
if (ctx.frameMgr.cxForLocalUseOnly()
->realm()
->collectCoverageForDebug()) {
PUSH_EXIT_FRAME();
if (!frame->script()->initScriptCounts(cx)) {
GOTO_ERROR();
}
}
}
COUNT_COVERAGE_MAIN();
}
// Everything is switched to callee context now -- dispatch!
DISPATCH();
} while (0);
// Slow path: use the IC!
ic_arg0 = argc;
ctx.icregs.extraArgs = 2 + constructing;
INVOKE_IC(Call, false);
VIRTPOPN(argc + 2 + constructing);
VIRTPUSH(StackVal(Value::fromRawBits(ic_ret)));
END_OP(Call);
}
CASE(SpreadCall)
CASE(SpreadEval)
CASE(StrictSpreadEval) {
static_assert(JSOpLength_SpreadCall == JSOpLength_SpreadEval);
static_assert(JSOpLength_SpreadCall == JSOpLength_StrictSpreadEval);
ic_arg0 = 1;
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
ctx.icregs.extraArgs = 2;
INVOKE_IC(SpreadCall, false);
VIRTPOPN(3);
VIRTPUSH(StackVal(Value::fromRawBits(ic_ret)));
END_OP(SpreadCall);
}
CASE(SpreadSuperCall)
CASE(SpreadNew) {
static_assert(JSOpLength_SpreadSuperCall == JSOpLength_SpreadNew);
ic_arg0 = 1;
ctx.icregs.extraArgs = 3;
INVOKE_IC(SpreadCall, false);
VIRTPOPN(4);
VIRTPUSH(StackVal(Value::fromRawBits(ic_ret)));
END_OP(SpreadSuperCall);
}
CASE(OptimizeSpreadCall) {
IC_POP_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(OptimizeSpreadCall, false);
END_OP(OptimizeSpreadCall);
}
CASE(OptimizeGetIterator) {
IC_POP_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(OptimizeGetIterator, false);
END_OP(OptimizeGetIterator);
}
CASE(ImplicitThis) {
{
ReservedRooted<JSObject*> env(&state.obj0,
&VIRTSP(0).asValue().toObject());
VIRTPOP();
PUSH_EXIT_FRAME();
ImplicitThisOperation(cx, env, &state.res);
}
VIRTPUSH(StackVal(state.res));
state.res.setUndefined();
END_OP(ImplicitThis);
}
CASE(CallSiteObj) {
JSObject* cso = frame->script()->getObject(pc);
MOZ_ASSERT(!cso->as<ArrayObject>().isExtensible());
MOZ_ASSERT(cso->as<ArrayObject>().containsPure(
ctx.frameMgr.cxForLocalUseOnly()->names().raw));
VIRTPUSH(StackVal(ObjectValue(*cso)));
END_OP(CallSiteObj);
}
CASE(IsConstructing) {
VIRTPUSH(StackVal(MagicValue(JS_IS_CONSTRUCTING)));
END_OP(IsConstructing);
}
CASE(SuperFun) {
JSObject* superEnvFunc = &VIRTPOP().asValue().toObject();
JSObject* superFun = SuperFunOperation(superEnvFunc);
VIRTPUSH(StackVal(ObjectOrNullValue(superFun)));
END_OP(SuperFun);
}
CASE(CheckThis) {
if (VIRTSP(0).asValue().isMagic(JS_UNINITIALIZED_LEXICAL)) {
PUSH_EXIT_FRAME();
MOZ_ALWAYS_FALSE(ThrowUninitializedThis(cx));
GOTO_ERROR();
}
END_OP(CheckThis);
}
CASE(CheckThisReinit) {
if (!VIRTSP(0).asValue().isMagic(JS_UNINITIALIZED_LEXICAL)) {
PUSH_EXIT_FRAME();
MOZ_ALWAYS_FALSE(ThrowInitializedThis(cx));
GOTO_ERROR();
}
END_OP(CheckThisReinit);
}
CASE(Generator) {
JSObject* generator;
{
PUSH_EXIT_FRAME();
generator = CreateGeneratorFromFrame(cx, frame);
if (!generator) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(ObjectValue(*generator)));
END_OP(Generator);
}
CASE(InitialYield) {
// gen => rval, gen, resumeKind
ReservedRooted<JSObject*> obj0(&state.obj0,
&VIRTSP(0).asValue().toObject());
uint32_t frameSize = ctx.stack.frameSize(sp, frame);
{
PUSH_EXIT_FRAME();
if (!NormalSuspend(cx, obj0, frame, frameSize, pc)) {
GOTO_ERROR();
}
}
frame->setReturnValue(VIRTSP(0).asValue());
goto do_return;
}
CASE(Await)
CASE(Yield) {
// rval1, gen => rval2, gen, resumeKind
ReservedRooted<JSObject*> obj0(&state.obj0,
&VIRTPOP().asValue().toObject());
uint32_t frameSize = ctx.stack.frameSize(sp, frame);
{
PUSH_EXIT_FRAME();
if (!NormalSuspend(cx, obj0, frame, frameSize, pc)) {
GOTO_ERROR();
}
}
frame->setReturnValue(VIRTSP(0).asValue());
goto do_return;
}
CASE(FinalYieldRval) {
// gen =>
ReservedRooted<JSObject*> obj0(&state.obj0,
&VIRTPOP().asValue().toObject());
{
PUSH_EXIT_FRAME();
if (!FinalSuspend(cx, obj0, pc)) {
GOTO_ERROR();
}
}
goto do_return;
}
CASE(IsGenClosing) {
bool result = VIRTSP(0).asValue() == MagicValue(JS_GENERATOR_CLOSING);
VIRTPUSH(StackVal(BooleanValue(result)));
END_OP(IsGenClosing);
}
CASE(AsyncAwait) {
// value, gen => promise
JSObject* promise;
{
ReservedRooted<JSObject*> obj0(
&state.obj0,
&VIRTPOP().asValue().toObject()); // gen
ReservedRooted<Value> value0(&state.value0,
VIRTPOP().asValue()); // value
PUSH_EXIT_FRAME();
promise = AsyncFunctionAwait(
cx, obj0.as<AsyncFunctionGeneratorObject>(), value0);
if (!promise) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(ObjectValue(*promise)));
END_OP(AsyncAwait);
}
CASE(AsyncResolve) {
// value, gen => promise
JSObject* promise;
{
ReservedRooted<JSObject*> obj0(
&state.obj0,
&VIRTPOP().asValue().toObject()); // gen
ReservedRooted<Value> value0(&state.value0,
VIRTPOP().asValue()); // value
PUSH_EXIT_FRAME();
promise = AsyncFunctionResolve(
cx, obj0.as<AsyncFunctionGeneratorObject>(), value0);
if (!promise) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(ObjectValue(*promise)));
END_OP(AsyncResolve);
}
CASE(AsyncReject) {
// reason, gen => promise
JSObject* promise;
{
ReservedRooted<JSObject*> obj0(
&state.obj0,
&VIRTPOP().asValue().toObject()); // gen
ReservedRooted<Value> value0(&state.value0,
VIRTPOP().asValue()); // stack
ReservedRooted<Value> value1(&state.value1,
VIRTPOP().asValue()); // reason
PUSH_EXIT_FRAME();
promise = AsyncFunctionReject(
cx, obj0.as<AsyncFunctionGeneratorObject>(), value1, value0);
if (!promise) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(ObjectValue(*promise)));
END_OP(AsyncReject);
}
CASE(CanSkipAwait) {
// value => value, can_skip
bool result = false;
{
ReservedRooted<Value> value0(&state.value0, VIRTSP(0).asValue());
PUSH_EXIT_FRAME();
if (!CanSkipAwait(cx, value0, &result)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(BooleanValue(result)));
END_OP(CanSkipAwait);
}
CASE(MaybeExtractAwaitValue) {
// value, can_skip => value_or_resolved, can_skip
{
Value can_skip = VIRTPOP().asValue();
ReservedRooted<Value> value0(&state.value0,
VIRTPOP().asValue()); // value
if (can_skip.toBoolean()) {
PUSH_EXIT_FRAME();
if (!ExtractAwaitValue(cx, value0, &value0)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(value0));
VIRTPUSH(StackVal(can_skip));
}
END_OP(MaybeExtractAwaitValue);
}
CASE(ResumeKind) {
GeneratorResumeKind resumeKind = ResumeKindFromPC(pc);
VIRTPUSH(StackVal(Int32Value(int32_t(resumeKind))));
END_OP(ResumeKind);
}
CASE(CheckResumeKind) {
// rval, gen, resumeKind => rval
{
GeneratorResumeKind resumeKind =
IntToResumeKind(VIRTPOP().asValue().toInt32());
ReservedRooted<JSObject*> obj0(
&state.obj0,
&VIRTPOP().asValue().toObject()); // gen
ReservedRooted<Value> value0(&state.value0,
VIRTSP(0).asValue()); // rval
if (resumeKind != GeneratorResumeKind::Next) {
PUSH_EXIT_FRAME();
MOZ_ALWAYS_FALSE(GeneratorThrowOrReturn(
cx, frame, obj0.as<AbstractGeneratorObject>(), value0,
resumeKind));
GOTO_ERROR();
}
}
END_OP(CheckResumeKind);
}
CASE(Resume) {
SYNCSP();
Value gen = VIRTSP(2).asValue();
Value* callerSP = reinterpret_cast<Value*>(sp);
{
ReservedRooted<Value> value0(&state.value0);
ReservedRooted<JSObject*> obj0(&state.obj0, &gen.toObject());
{
PUSH_EXIT_FRAME();
TRACE_PRINTF("Going to C++ interp for Resume\n");
if (!InterpretResume(cx, obj0, callerSP, &value0)) {
GOTO_ERROR();
}
}
VIRTPOPN(2);
VIRTSPWRITE(0, StackVal(value0));
}
END_OP(Resume);
}
CASE(JumpTarget) {
int32_t icIndex = GET_INT32(pc);
icEntry = icEntries + icIndex;
COUNT_COVERAGE_PC(pc);
END_OP(JumpTarget);
}
CASE(LoopHead) {
int32_t icIndex = GET_INT32(pc);
icEntry = icEntries + icIndex;
#ifdef ENABLE_INTERRUPT_CHECKS
if (ctx.frameMgr.cxForLocalUseOnly()->hasAnyPendingInterrupt()) {
PUSH_EXIT_FRAME();
if (!InterruptCheck(cx)) {
GOTO_ERROR();
}
}
#endif
COUNT_COVERAGE_PC(pc);
END_OP(LoopHead);
}
CASE(AfterYield) {
int32_t icIndex = GET_INT32(pc);
icEntry = icEntries + icIndex;
if (frame->script()->isDebuggee()) {
TRACE_PRINTF("doing DebugAfterYield\n");
PUSH_EXIT_FRAME();
ReservedRooted<JSScript*> script0(&state.script0, frame->script());
if (DebugAPI::hasAnyBreakpointsOrStepMode(script0) &&
!HandleDebugTrap(cx, frame, pc)) {
TRACE_PRINTF("HandleDebugTrap returned error\n");
GOTO_ERROR();
}
if (!DebugAfterYield(cx, frame)) {
TRACE_PRINTF("DebugAfterYield returned error\n");
GOTO_ERROR();
}
}
COUNT_COVERAGE_PC(pc);
END_OP(AfterYield);
}
CASE(Goto) {
ADVANCE(GET_JUMP_OFFSET(pc));
PREDICT_NEXT(JumpTarget);
PREDICT_NEXT(LoopHead);
DISPATCH();
}
CASE(Coalesce) {
if (!VIRTSP(0).asValue().isNullOrUndefined()) {
ADVANCE(GET_JUMP_OFFSET(pc));
DISPATCH();
} else {
END_OP(Coalesce);
}
}
CASE(Case) {
bool cond = VIRTPOP().asValue().toBoolean();
if (cond) {
VIRTPOP();
ADVANCE(GET_JUMP_OFFSET(pc));
DISPATCH();
} else {
END_OP(Case);
}
}
CASE(Default) {
VIRTPOP();
ADVANCE(GET_JUMP_OFFSET(pc));
DISPATCH();
}
CASE(TableSwitch) {
int32_t len = GET_JUMP_OFFSET(pc);
int32_t low = GET_JUMP_OFFSET(pc + 1 * JUMP_OFFSET_LEN);
int32_t high = GET_JUMP_OFFSET(pc + 2 * JUMP_OFFSET_LEN);
Value v = VIRTPOP().asValue();
int32_t i = 0;
if (v.isInt32()) {
i = v.toInt32();
} else if (!v.isDouble() ||
!mozilla::NumberEqualsInt32(v.toDouble(), &i)) {
ADVANCE(len);
DISPATCH();
}
if (i >= low && i <= high) {
uint32_t idx = uint32_t(i) - uint32_t(low);
uint32_t firstResumeIndex = GET_RESUMEINDEX(pc + 3 * JUMP_OFFSET_LEN);
pc = entryPC + resumeOffsets[firstResumeIndex + idx];
DISPATCH();
}
ADVANCE(len);
DISPATCH();
}
CASE(Return) {
frame->setReturnValue(VIRTPOP().asValue());
goto do_return;
}
CASE(GetRval) {
VIRTPUSH(StackVal(frame->returnValue()));
END_OP(GetRval);
}
CASE(SetRval) {
frame->setReturnValue(VIRTPOP().asValue());
END_OP(SetRval);
}
do_return:
CASE(RetRval) {
SYNCSP();
bool ok = true;
if (frame->isDebuggee() && !from_unwind) {
TRACE_PRINTF("doing DebugEpilogueOnBaselineReturn\n");
PUSH_EXIT_FRAME();
ok = DebugEpilogueOnBaselineReturn(cx, frame, pc);
}
from_unwind = false;
uint32_t argc = frame->numActualArgs();
sp = ctx.stack.popFrame();
// If FP is higher than the entry frame now, return; otherwise,
// do an inline state update.
if (stack.fp > entryFrame) {
*ret = frame->returnValue();
TRACE_PRINTF("ret = %" PRIx64 "\n", ret->asRawBits());
return ok ? PBIResult::Ok : PBIResult::Error;
} else {
TRACE_PRINTF("Return fastpath\n");
Value ret = frame->returnValue();
TRACE_PRINTF("ret = %" PRIx64 "\n", ret.asRawBits());
// Pop exit frame as well.
sp = ctx.stack.popFrame();
// Pop fake return address and descriptor.
POPNNATIVE(2);
// Set PC, frame, and current script.
frame = reinterpret_cast<BaselineFrame*>(
reinterpret_cast<uintptr_t>(stack.fp) - BaselineFrame::Size());
TRACE_PRINTF(" sp -> %p, fp -> %p, frame -> %p\n", sp, ctx.stack.fp,
frame);
ctx.frameMgr.switchToFrame(frame);
ctx.frame = frame;
RESET_PC(frame->interpreterPC(), frame->script());
// Adjust caller's stack to complete the call op that PC still points
// to in that frame (pop args, push return value).
JSOp op = JSOp(*pc);
bool constructing = (op == JSOp::New || op == JSOp::NewContent ||
op == JSOp::SuperCall);
// Fix-up return value; EnterJit would do this if we hadn't bypassed
// it.
if (constructing && ret.isPrimitive()) {
ret = sp[argc + constructing].asValue();
TRACE_PRINTF("updated ret = %" PRIx64 "\n", ret.asRawBits());
}
// Pop args -- this is 1 more than how many are pushed in the
// `totalArgs` count during the call fastpath because it includes
// the callee.
VIRTPOPN(argc + 2 + constructing);
// Push return value.
VIRTPUSH(StackVal(ret));
if (!ok) {
GOTO_ERROR();
}
// Advance past call instruction, and advance past IC.
NEXT_IC();
ADVANCE(JSOpLength_Call);
DISPATCH();
}
}
CASE(CheckReturn) {
Value thisval = VIRTPOP().asValue();
// inlined version of frame->checkReturn(thisval, result)
// (js/src/vm/Stack.cpp).
HandleValue retVal = frame->returnValue();
if (retVal.isObject()) {
VIRTPUSH(StackVal(retVal));
} else if (!retVal.isUndefined()) {
PUSH_EXIT_FRAME();
MOZ_ALWAYS_FALSE(ReportValueError(cx, JSMSG_BAD_DERIVED_RETURN,
JSDVG_IGNORE_STACK, retVal,
nullptr));
GOTO_ERROR();
} else if (thisval.isMagic(JS_UNINITIALIZED_LEXICAL)) {
PUSH_EXIT_FRAME();
MOZ_ALWAYS_FALSE(ThrowUninitializedThis(cx));
GOTO_ERROR();
} else {
VIRTPUSH(StackVal(thisval));
}
END_OP(CheckReturn);
}
CASE(Throw) {
{
ReservedRooted<Value> value0(&state.value0, VIRTPOP().asValue());
PUSH_EXIT_FRAME();
MOZ_ALWAYS_FALSE(ThrowOperation(cx, value0));
GOTO_ERROR();
}
END_OP(Throw);
}
CASE(ThrowWithStack) {
{
ReservedRooted<Value> value0(&state.value0, VIRTPOP().asValue());
ReservedRooted<Value> value1(&state.value1, VIRTPOP().asValue());
PUSH_EXIT_FRAME();
MOZ_ALWAYS_FALSE(ThrowWithStackOperation(cx, value1, value0));
GOTO_ERROR();
}
END_OP(ThrowWithStack);
}
CASE(ThrowMsg) {
{
PUSH_EXIT_FRAME();
MOZ_ALWAYS_FALSE(ThrowMsgOperation(cx, GET_UINT8(pc)));
GOTO_ERROR();
}
END_OP(ThrowMsg);
}
CASE(ThrowSetConst) {
{
PUSH_EXIT_FRAME();
ReservedRooted<JSScript*> script0(&state.script0, frame->script());
ReportRuntimeLexicalError(cx, JSMSG_BAD_CONST_ASSIGN, script0, pc);
GOTO_ERROR();
}
END_OP(ThrowSetConst);
}
CASE(Try)
CASE(TryDestructuring) {
static_assert(JSOpLength_Try == JSOpLength_TryDestructuring);
END_OP(Try);
}
CASE(Exception) {
{
PUSH_EXIT_FRAME();
if (!GetAndClearException(cx, &state.res)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(state.res));
state.res.setUndefined();
END_OP(Exception);
}
CASE(ExceptionAndStack) {
{
ReservedRooted<Value> value0(&state.value0);
{
PUSH_EXIT_FRAME();
if (!cx.getCx()->getPendingExceptionStack(&value0)) {
GOTO_ERROR();
}
if (!GetAndClearException(cx, &state.res)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(state.res));
VIRTPUSH(StackVal(value0));
state.res.setUndefined();
}
END_OP(ExceptionAndStack);
}
CASE(Finally) {
#ifdef ENABLE_INTERRUPT_CHECKS
if (ctx.frameMgr.cxForLocalUseOnly()->hasAnyPendingInterrupt()) {
PUSH_EXIT_FRAME();
if (!InterruptCheck(cx)) {
GOTO_ERROR();
}
}
#endif
END_OP(Finally);
}
CASE(Uninitialized) {
VIRTPUSH(StackVal(MagicValue(JS_UNINITIALIZED_LEXICAL)));
END_OP(Uninitialized);
}
CASE(InitLexical) {
uint32_t i = GET_LOCALNO(pc);
frame->unaliasedLocal(i) = VIRTSP(0).asValue();
END_OP(InitLexical);
}
CASE(InitAliasedLexical) {
EnvironmentCoordinate ec = EnvironmentCoordinate(pc);
EnvironmentObject& obj = getEnvironmentFromCoordinate(frame, ec);
obj.setAliasedBinding(ec, VIRTSP(0).asValue());
END_OP(InitAliasedLexical);
}
CASE(CheckLexical) {
if (VIRTSP(0).asValue().isMagic(JS_UNINITIALIZED_LEXICAL)) {
PUSH_EXIT_FRAME();
ReservedRooted<JSScript*> script0(&state.script0, frame->script());
ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, script0,
pc);
GOTO_ERROR();
}
END_OP(CheckLexical);
}
CASE(CheckAliasedLexical) {
if (VIRTSP(0).asValue().isMagic(JS_UNINITIALIZED_LEXICAL)) {
PUSH_EXIT_FRAME();
ReservedRooted<JSScript*> script0(&state.script0, frame->script());
ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, script0,
pc);
GOTO_ERROR();
}
END_OP(CheckAliasedLexical);
}
CASE(BindUnqualifiedGName) {
IC_SET_OBJ_ARG(
0,
&ctx.frameMgr.cxForLocalUseOnly()->global()->lexicalEnvironment());
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(BindName, false);
END_OP(BindUnqualifiedGName);
}
CASE(BindName) {
IC_SET_OBJ_ARG(0, frame->environmentChain());
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(BindName, false);
END_OP(BindName);
}
CASE(BindUnqualifiedName) {
IC_SET_OBJ_ARG(0, frame->environmentChain());
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(BindName, false);
END_OP(BindUnqualifiedName);
}
CASE(GetGName) {
IC_SET_OBJ_ARG(
0,
&ctx.frameMgr.cxForLocalUseOnly()->global()->lexicalEnvironment());
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(GetName, false);
END_OP(GetGName);
}
CASE(GetName) {
IC_SET_OBJ_ARG(0, frame->environmentChain());
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(GetName, false);
END_OP(GetName);
}
CASE(GetArg) {
unsigned i = GET_ARGNO(pc);
if (argsObjAliasesFormals) {
VIRTPUSH(StackVal(frame->argsObj().arg(i)));
} else {
VIRTPUSH(StackVal(frame->argv()[i]));
}
END_OP(GetArg);
}
CASE(GetFrameArg) {
uint32_t i = GET_ARGNO(pc);
VIRTPUSH(StackVal(frame->argv()[i]));
END_OP(GetFrameArg);
}
CASE(GetLocal) {
uint32_t i = GET_LOCALNO(pc);
TRACE_PRINTF(" -> local: %d\n", int(i));
VIRTPUSH(StackVal(GETLOCAL(i)));
END_OP(GetLocal);
}
CASE(ArgumentsLength) {
VIRTPUSH(StackVal(Int32Value(frame->numActualArgs())));
END_OP(ArgumentsLength);
}
CASE(GetActualArg) {
MOZ_ASSERT(!frame->script()->needsArgsObj());
uint32_t index = VIRTSP(0).asValue().toInt32();
VIRTSPWRITE(0, StackVal(frame->unaliasedActual(index)));
END_OP(GetActualArg);
}
CASE(GetAliasedVar)
CASE(GetAliasedDebugVar) {
static_assert(JSOpLength_GetAliasedVar ==
JSOpLength_GetAliasedDebugVar);
EnvironmentCoordinate ec = EnvironmentCoordinate(pc);
EnvironmentObject& obj = getEnvironmentFromCoordinate(frame, ec);
VIRTPUSH(StackVal(obj.aliasedBinding(ec)));
END_OP(GetAliasedVar);
}
CASE(GetImport) {
IC_ZERO_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(GetImport, false);
END_OP(GetImport);
}
CASE(GetIntrinsic) {
IC_ZERO_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(LazyConstant, false);
END_OP(GetIntrinsic);
}
CASE(Callee) {
VIRTPUSH(StackVal(frame->calleev()));
END_OP(Callee);
}
CASE(EnvCallee) {
uint8_t numHops = GET_UINT8(pc);
JSObject* env = &frame->environmentChain()->as<EnvironmentObject>();
for (unsigned i = 0; i < numHops; i++) {
env = &env->as<EnvironmentObject>().enclosingEnvironment();
}
VIRTPUSH(StackVal(ObjectValue(env->as<CallObject>().callee())));
END_OP(EnvCallee);
}
CASE(SetProp)
CASE(StrictSetProp)
CASE(SetName)
CASE(StrictSetName)
CASE(SetGName)
CASE(StrictSetGName) {
static_assert(JSOpLength_SetProp == JSOpLength_StrictSetProp);
static_assert(JSOpLength_SetProp == JSOpLength_SetName);
static_assert(JSOpLength_SetProp == JSOpLength_StrictSetName);
static_assert(JSOpLength_SetProp == JSOpLength_SetGName);
static_assert(JSOpLength_SetProp == JSOpLength_StrictSetGName);
IC_POP_ARG(1);
IC_POP_ARG(0);
IC_ZERO_ARG(2);
VIRTPUSH(StackVal(ic_arg1));
INVOKE_IC(SetProp, false);
END_OP(SetProp);
}
CASE(InitProp)
CASE(InitHiddenProp)
CASE(InitLockedProp) {
static_assert(JSOpLength_InitProp == JSOpLength_InitHiddenProp);
static_assert(JSOpLength_InitProp == JSOpLength_InitLockedProp);
IC_POP_ARG(1);
IC_SET_ARG_FROM_STACK(0, 0);
IC_ZERO_ARG(2);
INVOKE_IC(SetProp, false);
END_OP(InitProp);
}
CASE(InitGLexical) {
IC_SET_ARG_FROM_STACK(1, 0);
IC_SET_OBJ_ARG(
0,
&ctx.frameMgr.cxForLocalUseOnly()->global()->lexicalEnvironment());
IC_ZERO_ARG(2);
INVOKE_IC(SetProp, false);
END_OP(InitGLexical);
}
CASE(SetArg) {
unsigned i = GET_ARGNO(pc);
if (argsObjAliasesFormals) {
frame->argsObj().setArg(i, VIRTSP(0).asValue());
} else {
frame->argv()[i] = VIRTSP(0).asValue();
}
END_OP(SetArg);
}
CASE(SetLocal) {
uint32_t i = GET_LOCALNO(pc);
TRACE_PRINTF(" -> local: %d\n", int(i));
SETLOCAL(i, VIRTSP(0).asValue());
END_OP(SetLocal);
}
CASE(SetAliasedVar) {
EnvironmentCoordinate ec = EnvironmentCoordinate(pc);
EnvironmentObject& obj = getEnvironmentFromCoordinate(frame, ec);
MOZ_ASSERT(!IsUninitializedLexical(obj.aliasedBinding(ec)));
obj.setAliasedBinding(ec, VIRTSP(0).asValue());
END_OP(SetAliasedVar);
}
CASE(SetIntrinsic) {
{
ReservedRooted<Value> value0(&state.value0, VIRTSP(0).asValue());
ReservedRooted<JSScript*> script0(&state.script0, frame->script());
{
PUSH_EXIT_FRAME();
if (!SetIntrinsicOperation(cx, script0, pc, value0)) {
GOTO_ERROR();
}
}
}
END_OP(SetIntrinsic);
}
CASE(PushLexicalEnv) {
{
ReservedRooted<Scope*> scope0(&state.scope0,
frame->script()->getScope(pc));
{
PUSH_EXIT_FRAME();
if (!frame->pushLexicalEnvironment(cx, scope0.as<LexicalScope>())) {
GOTO_ERROR();
}
}
}
END_OP(PushLexicalEnv);
}
CASE(PopLexicalEnv) {
if (frame->isDebuggee()) {
TRACE_PRINTF("doing DebugLeaveThenPopLexicalEnv\n");
PUSH_EXIT_FRAME();
if (!DebugLeaveThenPopLexicalEnv(cx, frame, pc)) {
GOTO_ERROR();
}
} else {
frame->popOffEnvironmentChain<LexicalEnvironmentObject>();
}
END_OP(PopLexicalEnv);
}
CASE(DebugLeaveLexicalEnv) {
if (frame->isDebuggee()) {
TRACE_PRINTF("doing DebugLeaveLexicalEnv\n");
PUSH_EXIT_FRAME();
if (!DebugLeaveLexicalEnv(cx, frame, pc)) {
GOTO_ERROR();
}
}
END_OP(DebugLeaveLexicalEnv);
}
CASE(RecreateLexicalEnv) {
{
PUSH_EXIT_FRAME();
if (frame->isDebuggee()) {
TRACE_PRINTF("doing DebuggeeRecreateLexicalEnv\n");
if (!DebuggeeRecreateLexicalEnv(cx, frame, pc)) {
GOTO_ERROR();
}
} else {
if (!frame->recreateLexicalEnvironment<false>(cx)) {
GOTO_ERROR();
}
}
}
END_OP(RecreateLexicalEnv);
}
CASE(FreshenLexicalEnv) {
{
PUSH_EXIT_FRAME();
if (frame->isDebuggee()) {
TRACE_PRINTF("doing DebuggeeFreshenLexicalEnv\n");
if (!DebuggeeFreshenLexicalEnv(cx, frame, pc)) {
GOTO_ERROR();
}
} else {
if (!frame->freshenLexicalEnvironment<false>(cx)) {
GOTO_ERROR();
}
}
}
END_OP(FreshenLexicalEnv);
}
CASE(PushClassBodyEnv) {
{
ReservedRooted<Scope*> scope0(&state.scope0,
frame->script()->getScope(pc));
PUSH_EXIT_FRAME();
if (!frame->pushClassBodyEnvironment(cx,
scope0.as<ClassBodyScope>())) {
GOTO_ERROR();
}
}
END_OP(PushClassBodyEnv);
}
CASE(PushVarEnv) {
{
ReservedRooted<Scope*> scope0(&state.scope0,
frame->script()->getScope(pc));
PUSH_EXIT_FRAME();
if (!frame->pushVarEnvironment(cx, scope0)) {
GOTO_ERROR();
}
}
END_OP(PushVarEnv);
}
CASE(EnterWith) {
{
ReservedRooted<Scope*> scope0(&state.scope0,
frame->script()->getScope(pc));
ReservedRooted<Value> value0(&state.value0, VIRTPOP().asValue());
PUSH_EXIT_FRAME();
if (!EnterWithOperation(cx, frame, value0, scope0.as<WithScope>())) {
GOTO_ERROR();
}
}
END_OP(EnterWith);
}
CASE(LeaveWith) {
frame->popOffEnvironmentChain<WithEnvironmentObject>();
END_OP(LeaveWith);
}
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
CASE(AddDisposable) {
{
ReservedRooted<JSObject*> env(&state.obj0, frame->environmentChain());
ReservedRooted<JS::Value> needsClosure(&state.value0,
VIRTPOP().asValue());
ReservedRooted<JS::Value> method(&state.value1, VIRTPOP().asValue());
ReservedRooted<JS::Value> val(&state.value2, VIRTPOP().asValue());
UsingHint hint = UsingHint(GET_UINT8(pc));
PUSH_EXIT_FRAME();
if (!AddDisposableResourceToCapability(
cx, env, val, method, needsClosure.toBoolean(), hint)) {
GOTO_ERROR();
}
}
END_OP(AddDisposable);
}
CASE(TakeDisposeCapability) {
{
ReservedRooted<JSObject*> env(&state.obj0, frame->environmentChain());
JS::Value maybeDisposables =
env->as<DisposableEnvironmentObject>().getDisposables();
MOZ_ASSERT(maybeDisposables.isObject() ||
maybeDisposables.isUndefined());
if (maybeDisposables.isUndefined()) {
VIRTPUSH(StackVal(UndefinedValue()));
} else {
VIRTPUSH(StackVal(maybeDisposables));
env->as<DisposableEnvironmentObject>().clearDisposables();
}
}
END_OP(TakeDisposeCapability);
}
CASE(CreateSuppressedError) {
ErrorObject* errorObj;
{
ReservedRooted<JS::Value> error(&state.value0, VIRTPOP().asValue());
ReservedRooted<JS::Value> suppressed(&state.value1,
VIRTPOP().asValue());
PUSH_EXIT_FRAME();
errorObj = CreateSuppressedError(cx, error, suppressed);
if (!errorObj) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(ObjectValue(*errorObj)));
END_OP(CreateSuppressedError);
}
#endif
CASE(BindVar) {
JSObject* varObj;
{
ReservedRooted<JSObject*> obj0(&state.obj0,
frame->environmentChain());
PUSH_EXIT_FRAME();
varObj = BindVarOperation(cx, obj0);
}
VIRTPUSH(StackVal(ObjectValue(*varObj)));
END_OP(BindVar);
}
CASE(GlobalOrEvalDeclInstantiation) {
GCThingIndex lastFun = GET_GCTHING_INDEX(pc);
{
ReservedRooted<JSObject*> obj0(&state.obj0,
frame->environmentChain());
ReservedRooted<JSScript*> script0(&state.script0, frame->script());
PUSH_EXIT_FRAME();
if (!GlobalOrEvalDeclInstantiation(cx, obj0, script0, lastFun)) {
GOTO_ERROR();
}
}
END_OP(GlobalOrEvalDeclInstantiation);
}
CASE(DelName) {
{
ReservedRooted<PropertyName*> name0(&state.name0,
frame->script()->getName(pc));
ReservedRooted<JSObject*> obj0(&state.obj0,
frame->environmentChain());
PUSH_EXIT_FRAME();
if (!DeleteNameOperation(cx, name0, obj0, &state.res)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(state.res));
state.res.setUndefined();
END_OP(DelName);
}
CASE(Arguments) {
{
PUSH_EXIT_FRAME();
if (!NewArgumentsObject(cx, frame, &state.res)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(state.res));
state.res.setUndefined();
END_OP(Arguments);
}
CASE(Rest) {
IC_ZERO_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(Rest, false);
END_OP(Rest);
}
CASE(FunctionThis) {
{
PUSH_EXIT_FRAME();
if (!js::GetFunctionThis(cx, frame, &state.res)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(state.res));
state.res.setUndefined();
END_OP(FunctionThis);
}
CASE(Pop) {
VIRTPOP();
END_OP(Pop);
}
CASE(PopN) {
SYNCSP();
uint32_t n = GET_UINT16(pc);
VIRTPOPN(n);
END_OP(PopN);
}
CASE(Dup) {
StackVal value = VIRTSP(0);
VIRTPUSH(value);
END_OP(Dup);
}
CASE(Dup2) {
StackVal value1 = VIRTSP(0);
StackVal value2 = VIRTSP(1);
VIRTPUSH(value2);
VIRTPUSH(value1);
END_OP(Dup2);
}
CASE(DupAt) {
unsigned i = GET_UINT24(pc);
StackVal value = VIRTSP(i);
VIRTPUSH(value);
END_OP(DupAt);
}
CASE(Swap) {
StackVal v0 = VIRTSP(0);
StackVal v1 = VIRTSP(1);
VIRTSPWRITE(0, v1);
VIRTSPWRITE(1, v0);
END_OP(Swap);
}
CASE(Pick) {
unsigned i = GET_UINT8(pc);
SYNCSP();
StackVal tmp = sp[i];
memmove(&sp[1], &sp[0], sizeof(StackVal) * i);
VIRTSPWRITE(0, tmp);
END_OP(Pick);
}
CASE(Unpick) {
unsigned i = GET_UINT8(pc);
StackVal tmp = VIRTSP(0);
SYNCSP();
memmove(&sp[0], &sp[1], sizeof(StackVal) * i);
sp[i] = tmp;
END_OP(Unpick);
}
CASE(DebugCheckSelfHosted) {
HandleValue val = SPHANDLE(0);
{
PUSH_EXIT_FRAME();
if (!Debug_CheckSelfHosted(cx, val)) {
GOTO_ERROR();
}
}
END_OP(DebugCheckSelfHosted);
}
CASE(Lineno) { END_OP(Lineno); }
CASE(NopDestructuring) { END_OP(NopDestructuring); }
CASE(ForceInterpreter) { END_OP(ForceInterpreter); }
CASE(Debugger) {
{
PUSH_EXIT_FRAME();
if (!OnDebuggerStatement(cx, frame)) {
GOTO_ERROR();
}
}
END_OP(Debugger);
}
label_default:
#ifndef ENABLE_COMPUTED_GOTO_DISPATCH
default:
#endif
MOZ_CRASH("Bad opcode");
}
}
restart:
// This is a `goto` target so that we exit any on-stack exit frames
// before restarting, to match previous behavior.
return PortableBaselineInterpret<true, HybridICs>(
ctx.frameMgr.cxForLocalUseOnly(), ctx.state, ctx.stack, sp, envChain, ret,
pc, isd, entryPC, frame, entryFrame, restartCode);
error:
TRACE_PRINTF("HandleException: frame %p\n", frame);
{
ResumeFromException rfe;
{
PUSH_EXIT_FRAME();
HandleException(&rfe);
}
switch (rfe.kind) {
case ExceptionResumeKind::EntryFrame:
TRACE_PRINTF(" -> Return from entry frame\n");
frame->setReturnValue(MagicValue(JS_ION_ERROR));
ctx.stack.fp = reinterpret_cast<StackVal*>(rfe.framePointer);
ctx.stack.unwindingSP = reinterpret_cast<StackVal*>(rfe.stackPointer);
ctx.stack.unwindingFP = reinterpret_cast<StackVal*>(rfe.framePointer);
goto unwind_error;
case ExceptionResumeKind::Catch:
RESET_PC(frame->interpreterPC(), frame->script());
ctx.stack.fp = reinterpret_cast<StackVal*>(rfe.framePointer);
ctx.stack.unwindingSP = reinterpret_cast<StackVal*>(rfe.stackPointer);
ctx.stack.unwindingFP = reinterpret_cast<StackVal*>(rfe.framePointer);
TRACE_PRINTF(" -> catch to pc %p\n", pc);
goto unwind;
case ExceptionResumeKind::Finally:
RESET_PC(frame->interpreterPC(), frame->script());
ctx.stack.fp = reinterpret_cast<StackVal*>(rfe.framePointer);
sp = reinterpret_cast<StackVal*>(rfe.stackPointer);
TRACE_PRINTF(" -> finally to pc %p\n", pc);
VIRTPUSH(StackVal(rfe.exception));
VIRTPUSH(StackVal(rfe.exceptionStack));
VIRTPUSH(StackVal(BooleanValue(true)));
ctx.stack.unwindingSP = sp;
ctx.stack.unwindingFP = ctx.stack.fp;
goto unwind;
case ExceptionResumeKind::ForcedReturnBaseline:
RESET_PC(frame->interpreterPC(), frame->script());
ctx.stack.fp = reinterpret_cast<StackVal*>(rfe.framePointer);
ctx.stack.unwindingSP = reinterpret_cast<StackVal*>(rfe.stackPointer);
ctx.stack.unwindingFP = reinterpret_cast<StackVal*>(rfe.framePointer);
TRACE_PRINTF(" -> forced return\n");
goto unwind_ret;
case ExceptionResumeKind::ForcedReturnIon:
MOZ_CRASH(
"Unexpected ForcedReturnIon exception-resume kind in Portable "
"Baseline");
case ExceptionResumeKind::Bailout:
MOZ_CRASH(
"Unexpected Bailout exception-resume kind in Portable Baseline");
case ExceptionResumeKind::WasmInterpEntry:
MOZ_CRASH(
"Unexpected WasmInterpEntry exception-resume kind in Portable "
"Baseline");
case ExceptionResumeKind::WasmCatch:
MOZ_CRASH(
"Unexpected WasmCatch exception-resume kind in Portable "
"Baseline");
}
}
DISPATCH();
ic_fail:
RESTART(ic_result);
switch (ic_result) {
case PBIResult::Ok:
MOZ_CRASH("Unreachable: ic_result must be an error if we reach ic_fail");
case PBIResult::Error:
goto error;
case PBIResult::Unwind:
goto unwind;
case PBIResult::UnwindError:
goto unwind_error;
case PBIResult::UnwindRet:
goto unwind_ret;
}
unwind:
TRACE_PRINTF("unwind: fp = %p entryFrame = %p\n", ctx.stack.fp, entryFrame);
if (reinterpret_cast<uintptr_t>(ctx.stack.unwindingFP) >
reinterpret_cast<uintptr_t>(entryFrame) + BaselineFrame::Size()) {
TRACE_PRINTF(" -> returning\n");
return PBIResult::Unwind;
}
sp = ctx.stack.unwindingSP;
ctx.stack.fp = ctx.stack.unwindingFP;
frame = reinterpret_cast<BaselineFrame*>(
reinterpret_cast<uintptr_t>(ctx.stack.fp) - BaselineFrame::Size());
TRACE_PRINTF(" -> setting sp to %p, frame to %p\n", sp, frame);
ctx.frameMgr.switchToFrame(frame);
ctx.frame = frame;
RESET_PC(frame->interpreterPC(), frame->script());
DISPATCH();
unwind_error:
TRACE_PRINTF("unwind_error: fp = %p entryFrame = %p\n", ctx.stack.fp,
entryFrame);
if (reinterpret_cast<uintptr_t>(ctx.stack.unwindingFP) >
reinterpret_cast<uintptr_t>(entryFrame) + BaselineFrame::Size()) {
return PBIResult::UnwindError;
}
if (reinterpret_cast<uintptr_t>(ctx.stack.unwindingFP) ==
reinterpret_cast<uintptr_t>(entryFrame) + BaselineFrame::Size()) {
return PBIResult::Error;
}
sp = ctx.stack.unwindingSP;
ctx.stack.fp = ctx.stack.unwindingFP;
frame = reinterpret_cast<BaselineFrame*>(
reinterpret_cast<uintptr_t>(ctx.stack.fp) - BaselineFrame::Size());
TRACE_PRINTF(" -> setting sp to %p, frame to %p\n", sp, frame);
ctx.frameMgr.switchToFrame(frame);
ctx.frame = frame;
RESET_PC(frame->interpreterPC(), frame->script());
goto error;
unwind_ret:
TRACE_PRINTF("unwind_ret: fp = %p entryFrame = %p\n", ctx.stack.fp,
entryFrame);
if (reinterpret_cast<uintptr_t>(ctx.stack.unwindingFP) >
reinterpret_cast<uintptr_t>(entryFrame) + BaselineFrame::Size()) {
return PBIResult::UnwindRet;
}
if (reinterpret_cast<uintptr_t>(ctx.stack.unwindingFP) ==
reinterpret_cast<uintptr_t>(entryFrame) + BaselineFrame::Size()) {
*ret = frame->returnValue();
return PBIResult::Ok;
}
sp = ctx.stack.unwindingSP;
ctx.stack.fp = ctx.stack.unwindingFP;
frame = reinterpret_cast<BaselineFrame*>(
reinterpret_cast<uintptr_t>(ctx.stack.fp) - BaselineFrame::Size());
TRACE_PRINTF(" -> setting sp to %p, frame to %p\n", sp, frame);
ctx.frameMgr.switchToFrame(frame);
ctx.frame = frame;
RESET_PC(frame->interpreterPC(), frame->script());
from_unwind = true;
goto do_return;
#ifndef __wasi__
debug:;
{
TRACE_PRINTF("hit debug point\n");
PUSH_EXIT_FRAME();
if (!HandleDebugTrap(cx, frame, pc)) {
TRACE_PRINTF("HandleDebugTrap returned error\n");
goto error;
}
RESET_PC(frame->interpreterPC(), frame->script());
TRACE_PRINTF("HandleDebugTrap done\n");
}
goto dispatch;
#endif
}