#include "frontend/NameOpEmitter.h"
#include "frontend/BytecodeEmitter.h"
#include "frontend/SharedContext.h"
#include "frontend/TDZCheckCache.h"
#include "vm/Opcodes.h"
#include "vm/Scope.h"
#include "vm/StringType.h"
using namespace js;
using namespace js::frontend;
NameOpEmitter::NameOpEmitter(BytecodeEmitter* bce, JSAtom* name, Kind kind)
: bce_(bce),
kind_(kind),
name_(bce_->cx, name),
loc_(bce_->lookupName(name_)) {}
NameOpEmitter::NameOpEmitter(BytecodeEmitter* bce, JSAtom* name,
const NameLocation& loc, Kind kind)
: bce_(bce), kind_(kind), name_(bce_->cx, name), loc_(loc) {}
bool NameOpEmitter::emitGet() {
MOZ_ASSERT(state_ == State::Start);
switch (loc_.kind()) {
case NameLocation::Kind::Dynamic:
if (!bce_->emitAtomOp(name_, JSOP_GETNAME)) {
return false;
}
break;
case NameLocation::Kind::Global:
if (!bce_->emitAtomOp(name_, JSOP_GETGNAME)) {
return false;
}
break;
case NameLocation::Kind::Intrinsic:
if (!bce_->emitAtomOp(name_, JSOP_GETINTRINSIC)) {
return false;
}
break;
case NameLocation::Kind::NamedLambdaCallee:
if (!bce_->emit1(JSOP_CALLEE)) {
return false;
}
break;
case NameLocation::Kind::Import:
if (!bce_->emitAtomOp(name_, JSOP_GETIMPORT)) {
return false;
}
break;
case NameLocation::Kind::ArgumentSlot:
if (!bce_->emitArgOp(JSOP_GETARG, loc_.argumentSlot())) {
return false;
}
break;
case NameLocation::Kind::FrameSlot:
if (loc_.isLexical()) {
if (!bce_->emitTDZCheckIfNeeded(name_, loc_)) {
return false;
}
}
if (!bce_->emitLocalOp(JSOP_GETLOCAL, loc_.frameSlot())) {
return false;
}
break;
case NameLocation::Kind::EnvironmentCoordinate:
if (loc_.isLexical()) {
if (!bce_->emitTDZCheckIfNeeded(name_, loc_)) {
return false;
}
}
if (!bce_->emitEnvCoordOp(JSOP_GETALIASEDVAR,
loc_.environmentCoordinate())) {
return false;
}
break;
case NameLocation::Kind::DynamicAnnexBVar:
MOZ_CRASH(
"Synthesized vars for Annex B.3.3 should only be used in "
"initialization");
}
if (isCall()) {
switch (loc_.kind()) {
case NameLocation::Kind::Dynamic: {
JSOp thisOp =
bce_->needsImplicitThis() ? JSOP_IMPLICITTHIS : JSOP_GIMPLICITTHIS;
if (!bce_->emitAtomOp(name_, thisOp)) {
return false;
}
break;
}
case NameLocation::Kind::Global:
if (!bce_->emitAtomOp(name_, JSOP_GIMPLICITTHIS)) {
return false;
}
break;
case NameLocation::Kind::Intrinsic:
case NameLocation::Kind::NamedLambdaCallee:
case NameLocation::Kind::Import:
case NameLocation::Kind::ArgumentSlot:
case NameLocation::Kind::FrameSlot:
case NameLocation::Kind::EnvironmentCoordinate:
if (!bce_->emit1(JSOP_UNDEFINED)) {
return false;
}
break;
case NameLocation::Kind::DynamicAnnexBVar:
MOZ_CRASH(
"Synthesized vars for Annex B.3.3 should only be used in "
"initialization");
}
}
#ifdef DEBUG
state_ = State::Get;
#endif
return true;
}
bool NameOpEmitter::prepareForRhs() {
MOZ_ASSERT(state_ == State::Start);
switch (loc_.kind()) {
case NameLocation::Kind::Dynamic:
case NameLocation::Kind::Import:
case NameLocation::Kind::DynamicAnnexBVar:
if (!bce_->makeAtomIndex(name_, &atomIndex_)) {
return false;
}
if (loc_.kind() == NameLocation::Kind::DynamicAnnexBVar) {
if (!bce_->emit1(JSOP_BINDVAR)) {
return false;
}
} else {
if (!bce_->emitIndexOp(JSOP_BINDNAME, atomIndex_)) {
return false;
}
}
emittedBindOp_ = true;
break;
case NameLocation::Kind::Global:
if (!bce_->makeAtomIndex(name_, &atomIndex_)) {
return false;
}
if (loc_.isLexical() && isInitialize()) {
MOZ_ASSERT(bce_->innermostScope()->is<GlobalScope>());
} else {
if (!bce_->emitIndexOp(JSOP_BINDGNAME, atomIndex_)) {
return false;
}
emittedBindOp_ = true;
}
break;
case NameLocation::Kind::Intrinsic:
break;
case NameLocation::Kind::NamedLambdaCallee:
break;
case NameLocation::Kind::ArgumentSlot: {
FunctionBox* funbox = bce_->sc->asFunctionBox();
if (funbox->argumentsHasLocalBinding() && !funbox->hasMappedArgsObj()) {
funbox->setDefinitelyNeedsArgsObj();
}
break;
}
case NameLocation::Kind::FrameSlot:
break;
case NameLocation::Kind::EnvironmentCoordinate:
break;
}
if (isCompoundAssignment() || isIncDec()) {
if (loc_.kind() == NameLocation::Kind::Dynamic) {
if (!bce_->emit1(JSOP_DUP)) {
return false;
}
if (!bce_->emitAtomOp(name_, JSOP_GETBOUNDNAME)) {
return false;
}
} else {
if (!emitGet()) {
return false;
}
}
}
#ifdef DEBUG
state_ = State::Rhs;
#endif
return true;
}
bool NameOpEmitter::emitAssignment() {
MOZ_ASSERT(state_ == State::Rhs);
switch (loc_.kind()) {
case NameLocation::Kind::Dynamic:
case NameLocation::Kind::Import:
case NameLocation::Kind::DynamicAnnexBVar:
if (!bce_->emitIndexOp(bce_->strictifySetNameOp(JSOP_SETNAME),
atomIndex_)) {
return false;
}
break;
case NameLocation::Kind::Global: {
JSOp op;
if (emittedBindOp_) {
op = bce_->strictifySetNameOp(JSOP_SETGNAME);
} else {
op = JSOP_INITGLEXICAL;
}
if (!bce_->emitIndexOp(op, atomIndex_)) {
return false;
}
break;
}
case NameLocation::Kind::Intrinsic:
if (!bce_->emitAtomOp(name_, JSOP_SETINTRINSIC)) {
return false;
}
break;
case NameLocation::Kind::NamedLambdaCallee:
if (bce_->sc->strict()) {
if (!bce_->emit1(JSOP_THROWSETCALLEE)) {
return false;
}
}
break;
case NameLocation::Kind::ArgumentSlot:
if (!bce_->emitArgOp(JSOP_SETARG, loc_.argumentSlot())) {
return false;
}
break;
case NameLocation::Kind::FrameSlot: {
JSOp op = JSOP_SETLOCAL;
if (loc_.isLexical()) {
if (isInitialize()) {
op = JSOP_INITLEXICAL;
} else {
if (loc_.isConst()) {
op = JSOP_THROWSETCONST;
}
if (!bce_->emitTDZCheckIfNeeded(name_, loc_)) {
return false;
}
}
}
if (!bce_->emitLocalOp(op, loc_.frameSlot())) {
return false;
}
if (op == JSOP_INITLEXICAL) {
if (!bce_->innermostTDZCheckCache->noteTDZCheck(bce_, name_,
DontCheckTDZ)) {
return false;
}
}
break;
}
case NameLocation::Kind::EnvironmentCoordinate: {
JSOp op = JSOP_SETALIASEDVAR;
if (loc_.isLexical()) {
if (isInitialize()) {
op = JSOP_INITALIASEDLEXICAL;
} else {
if (loc_.isConst()) {
op = JSOP_THROWSETALIASEDCONST;
}
if (!bce_->emitTDZCheckIfNeeded(name_, loc_)) {
return false;
}
}
}
if (loc_.bindingKind() == BindingKind::NamedLambdaCallee) {
op = JSOP_THROWSETALIASEDCONST;
if (bce_->sc->strict()) {
if (!bce_->emitEnvCoordOp(op, loc_.environmentCoordinate())) {
return false;
}
}
} else {
if (!bce_->emitEnvCoordOp(op, loc_.environmentCoordinate())) {
return false;
}
}
if (op == JSOP_INITALIASEDLEXICAL) {
if (!bce_->innermostTDZCheckCache->noteTDZCheck(bce_, name_,
DontCheckTDZ)) {
return false;
}
}
break;
}
}
#ifdef DEBUG
state_ = State::Assignment;
#endif
return true;
}
bool NameOpEmitter::emitIncDec() {
MOZ_ASSERT(state_ == State::Start);
JSOp incOp = isInc() ? JSOP_INC : JSOP_DEC;
if (!prepareForRhs()) {
return false;
}
if (!bce_->emit1(JSOP_TONUMERIC)) {
return false;
}
if (isPostIncDec()) {
if (!bce_->emit1(JSOP_DUP)) {
return false;
}
}
if (!bce_->emit1(incOp)) {
return false;
}
if (isPostIncDec() && emittedBindOp()) {
if (!bce_->emit2(JSOP_PICK, 2)) {
return false;
}
if (!bce_->emit1(JSOP_SWAP)) {
return false;
}
}
if (!emitAssignment()) {
return false;
}
if (isPostIncDec()) {
if (!bce_->emit1(JSOP_POP)) {
return false;
}
}
#ifdef DEBUG
state_ = State::IncDec;
#endif
return true;
}