/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "frontend/PropOpEmitter.h" #include "frontend/BytecodeEmitter.h" #include "frontend/SharedContext.h" #include "vm/Opcodes.h" #include "vm/StringType.h" #include "vm/ThrowMsgKind.h" // ThrowMsgKind using namespace js; using namespace js::frontend; PropOpEmitter::PropOpEmitter(BytecodeEmitter* bce, Kind kind, ObjKind objKind) : bce_(bce), kind_(kind), objKind_(objKind) {} bool PropOpEmitter::prepareAtomIndex(const ParserAtom* prop) { return bce_->makeAtomIndex(prop, &propAtomIndex_); } bool PropOpEmitter::prepareForObj() { MOZ_ASSERT(state_ == State::Start); #ifdef DEBUG state_ = State::Obj; #endif return true; } bool PropOpEmitter::emitGet(const ParserAtom* prop) { MOZ_ASSERT(state_ == State::Obj); if (!prepareAtomIndex(prop)) { return false; } if (isCall()) { if (!bce_->emit1(JSOp::Dup)) { // [stack] # if Super // [stack] THIS THIS // [stack] # otherwise // [stack] OBJ OBJ return false; } } if (isSuper()) { if (!bce_->emitSuperBase()) { // [stack] THIS? THIS SUPERBASE return false; } } if (isIncDec() || isCompoundAssignment()) { if (isSuper()) { if (!bce_->emit1(JSOp::Dup2)) { // [stack] THIS SUPERBASE THIS SUPERBASE return false; } } else { if (!bce_->emit1(JSOp::Dup)) { // [stack] OBJ OBJ return false; } } } JSOp op = isSuper() ? JSOp::GetPropSuper : JSOp::GetProp; if (!bce_->emitAtomOp(op, propAtomIndex_, ShouldInstrument::Yes)) { // [stack] # if Get // [stack] PROP // [stack] # if Call // [stack] THIS PROP // [stack] # if Inc/Dec/Compound, Super] // [stack] THIS SUPERBASE PROP // [stack] # if Inc/Dec/Compound, other // [stack] OBJ PROP return false; } if (isCall()) { if (!bce_->emit1(JSOp::Swap)) { // [stack] PROP THIS return false; } } #ifdef DEBUG state_ = State::Get; #endif return true; } bool PropOpEmitter::prepareForRhs() { MOZ_ASSERT(isSimpleAssignment() || isPropInit() || isCompoundAssignment()); MOZ_ASSERT_IF(isSimpleAssignment() || isPropInit(), state_ == State::Obj); MOZ_ASSERT_IF(isCompoundAssignment(), state_ == State::Get); if (isSimpleAssignment() || isPropInit()) { // For CompoundAssignment, SuperBase is already emitted by emitGet. if (isSuper()) { if (!bce_->emitSuperBase()) { // [stack] THIS SUPERBASE return false; } } } #ifdef DEBUG state_ = State::Rhs; #endif return true; } bool PropOpEmitter::skipObjAndRhs() { MOZ_ASSERT(state_ == State::Start); MOZ_ASSERT(isSimpleAssignment() || isPropInit()); #ifdef DEBUG state_ = State::Rhs; #endif return true; } bool PropOpEmitter::emitDelete(const ParserAtom* prop) { MOZ_ASSERT_IF(!isSuper(), state_ == State::Obj); MOZ_ASSERT_IF(isSuper(), state_ == State::Start); MOZ_ASSERT(isDelete()); if (!prepareAtomIndex(prop)) { return false; } if (isSuper()) { if (!bce_->emitSuperBase()) { // [stack] THIS SUPERBASE return false; } // Unconditionally throw when attempting to delete a super-reference. if (!bce_->emit2(JSOp::ThrowMsg, uint8_t(ThrowMsgKind::CantDeleteSuper))) { // [stack] THIS SUPERBASE return false; } // Another wrinkle: Balance the stack from the emitter's point of view. // Execution will not reach here, as the last bytecode threw. if (!bce_->emit1(JSOp::Pop)) { // [stack] THIS return false; } } else { JSOp op = bce_->sc->strict() ? JSOp::StrictDelProp : JSOp::DelProp; if (!bce_->emitAtomOp(op, propAtomIndex_)) { // [stack] SUCCEEDED return false; } } #ifdef DEBUG state_ = State::Delete; #endif return true; } bool PropOpEmitter::emitAssignment(const ParserAtom* prop) { MOZ_ASSERT(isSimpleAssignment() || isPropInit() || isCompoundAssignment()); MOZ_ASSERT(state_ == State::Rhs); if (isSimpleAssignment() || isPropInit()) { if (!prepareAtomIndex(prop)) { return false; } } MOZ_ASSERT_IF(isPropInit(), !isSuper()); JSOp setOp = isPropInit() ? JSOp::InitProp : isSuper() ? bce_->sc->strict() ? JSOp::StrictSetPropSuper : JSOp::SetPropSuper : bce_->sc->strict() ? JSOp::StrictSetProp : JSOp::SetProp; if (!bce_->emitAtomOp(setOp, propAtomIndex_, ShouldInstrument::Yes)) { // [stack] VAL return false; } #ifdef DEBUG state_ = State::Assignment; #endif return true; } bool PropOpEmitter::emitIncDec(const ParserAtom* prop) { MOZ_ASSERT(state_ == State::Obj); MOZ_ASSERT(isIncDec()); if (!emitGet(prop)) { return false; } MOZ_ASSERT(state_ == State::Get); JSOp incOp = isInc() ? JSOp::Inc : JSOp::Dec; if (!bce_->emit1(JSOp::ToNumeric)) { // [stack] ... N return false; } if (isPostIncDec()) { // [stack] OBJ SUPERBASE? N if (!bce_->emit1(JSOp::Dup)) { // [stack] .. N N return false; } if (!bce_->emit2(JSOp::Unpick, 2 + isSuper())) { // [stack] N OBJ SUPERBASE? N return false; } } if (!bce_->emit1(incOp)) { // [stack] ... N+1 return false; } JSOp setOp = isSuper() ? bce_->sc->strict() ? JSOp::StrictSetPropSuper : JSOp::SetPropSuper : bce_->sc->strict() ? JSOp::StrictSetProp : JSOp::SetProp; if (!bce_->emitAtomOp(setOp, propAtomIndex_, ShouldInstrument::Yes)) { // [stack] N? N+1 return false; } if (isPostIncDec()) { if (!bce_->emit1(JSOp::Pop)) { // [stack] N return false; } } #ifdef DEBUG state_ = State::IncDec; #endif return true; }