#include "frontend/PropOpEmitter.h"
#include "frontend/BytecodeEmitter.h"
#include "frontend/SharedContext.h"
#include "vm/Opcodes.h"
#include "vm/StringType.h"
using namespace js;
using namespace js::frontend;
PropOpEmitter::PropOpEmitter(BytecodeEmitter* bce, Kind kind, ObjKind objKind)
: bce_(bce), kind_(kind), objKind_(objKind) {}
bool PropOpEmitter::prepareAtomIndex(JSAtom* prop) {
if (!bce_->makeAtomIndex(prop, &propAtomIndex_)) {
return false;
}
isLength_ = prop == bce_->cx->names().length;
return true;
}
bool PropOpEmitter::prepareForObj() {
MOZ_ASSERT(state_ == State::Start);
#ifdef DEBUG
state_ = State::Obj;
#endif
return true;
}
bool PropOpEmitter::emitGet(JSAtom* prop) {
MOZ_ASSERT(state_ == State::Obj);
if (!prepareAtomIndex(prop)) {
return false;
}
if (isCall()) {
if (!bce_->emit1(JSOP_DUP)) {
return false;
}
}
if (isSuper()) {
if (!bce_->emitSuperBase()) {
return false;
}
}
if (isIncDec() || isCompoundAssignment()) {
if (isSuper()) {
if (!bce_->emit1(JSOP_DUP2)) {
return false;
}
} else {
if (!bce_->emit1(JSOP_DUP)) {
return false;
}
}
}
JSOp op;
if (isSuper()) {
op = JSOP_GETPROP_SUPER;
} else if (isCall()) {
op = JSOP_CALLPROP;
} else {
op = isLength_ ? JSOP_LENGTH : JSOP_GETPROP;
}
if (!bce_->emitAtomOp(propAtomIndex_, op)) {
return false;
}
if (isCall()) {
if (!bce_->emit1(JSOP_SWAP)) {
return false;
}
}
#ifdef DEBUG
state_ = State::Get;
#endif
return true;
}
bool PropOpEmitter::prepareForRhs() {
MOZ_ASSERT(isSimpleAssignment() || isCompoundAssignment());
MOZ_ASSERT_IF(isSimpleAssignment(), state_ == State::Obj);
MOZ_ASSERT_IF(isCompoundAssignment(), state_ == State::Get);
if (isSimpleAssignment()) {
if (isSuper()) {
if (!bce_->emitSuperBase()) {
return false;
}
}
}
#ifdef DEBUG
state_ = State::Rhs;
#endif
return true;
}
bool PropOpEmitter::skipObjAndRhs() {
MOZ_ASSERT(state_ == State::Start);
MOZ_ASSERT(isSimpleAssignment());
#ifdef DEBUG
state_ = State::Rhs;
#endif
return true;
}
bool PropOpEmitter::emitDelete(JSAtom* 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()) {
return false;
}
if (!bce_->emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER)) {
return false;
}
if (!bce_->emit1(JSOP_POP)) {
return false;
}
} else {
JSOp op = bce_->sc->strict() ? JSOP_STRICTDELPROP : JSOP_DELPROP;
if (!bce_->emitAtomOp(propAtomIndex_, op)) {
return false;
}
}
#ifdef DEBUG
state_ = State::Delete;
#endif
return true;
}
bool PropOpEmitter::emitAssignment(JSAtom* prop) {
MOZ_ASSERT(isSimpleAssignment() || isCompoundAssignment());
MOZ_ASSERT(state_ == State::Rhs);
if (isSimpleAssignment()) {
if (!prepareAtomIndex(prop)) {
return false;
}
}
JSOp setOp =
isSuper()
? bce_->sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER
: bce_->sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP;
if (!bce_->emitAtomOp(propAtomIndex_, setOp)) {
return false;
}
#ifdef DEBUG
state_ = State::Assignment;
#endif
return true;
}
bool PropOpEmitter::emitIncDec(JSAtom* 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)) {
return false;
}
if (isPostIncDec()) {
if (!bce_->emit1(JSOP_DUP)) {
return false;
}
}
if (!bce_->emit1(incOp)) {
return false;
}
if (isPostIncDec()) {
if (isSuper()) {
if (!bce_->emit2(JSOP_PICK, 3)) {
return false;
}
if (!bce_->emit1(JSOP_SWAP)) {
return false;
}
if (!bce_->emit2(JSOP_PICK, 3)) {
return false;
}
if (!bce_->emit1(JSOP_SWAP)) {
return false;
}
} else {
if (!bce_->emit2(JSOP_PICK, 2)) {
return false;
}
if (!bce_->emit1(JSOP_SWAP)) {
return false;
}
}
}
JSOp setOp =
isSuper()
? bce_->sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER
: bce_->sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP;
if (!bce_->emitAtomOp(propAtomIndex_, setOp)) {
return false;
}
if (isPostIncDec()) {
if (!bce_->emit1(JSOP_POP)) {
return false;
}
}
#ifdef DEBUG
state_ = State::IncDec;
#endif
return true;
}