#include "frontend/CallOrNewEmitter.h"
#include "frontend/BytecodeEmitter.h"
#include "frontend/NameOpEmitter.h"
#include "frontend/SharedContext.h"
#include "vm/Opcodes.h"
#include "vm/StringType.h"
using namespace js;
using namespace js::frontend;
using mozilla::Maybe;
AutoEmittingRunOnceLambda::AutoEmittingRunOnceLambda(BytecodeEmitter* bce)
: bce_(bce) {
MOZ_ASSERT(!bce_->emittingRunOnceLambda);
bce_->emittingRunOnceLambda = true;
}
AutoEmittingRunOnceLambda::~AutoEmittingRunOnceLambda() {
bce_->emittingRunOnceLambda = false;
}
CallOrNewEmitter::CallOrNewEmitter(BytecodeEmitter* bce, JSOp op,
ArgumentsKind argumentsKind,
ValueUsage valueUsage)
: bce_(bce), op_(op), argumentsKind_(argumentsKind) {
if (op_ == JSOP_CALL && valueUsage == ValueUsage::IgnoreValue) {
op_ = JSOP_CALL_IGNORES_RV;
}
MOZ_ASSERT(isCall() || isNew() || isSuperCall());
}
bool CallOrNewEmitter::emitNameCallee(JSAtom* name) {
MOZ_ASSERT(state_ == State::Start);
NameOpEmitter noe(
bce_, name,
isCall() ? NameOpEmitter::Kind::Call : NameOpEmitter::Kind::Get);
if (!noe.emitGet()) {
return false;
}
state_ = State::NameCallee;
return true;
}
MOZ_MUST_USE PropOpEmitter& CallOrNewEmitter::prepareForPropCallee(
bool isSuperProp) {
MOZ_ASSERT(state_ == State::Start);
poe_.emplace(bce_,
isCall() ? PropOpEmitter::Kind::Call : PropOpEmitter::Kind::Get,
isSuperProp ? PropOpEmitter::ObjKind::Super
: PropOpEmitter::ObjKind::Other);
state_ = State::PropCallee;
return *poe_;
}
MOZ_MUST_USE ElemOpEmitter& CallOrNewEmitter::prepareForElemCallee(
bool isSuperElem) {
MOZ_ASSERT(state_ == State::Start);
eoe_.emplace(bce_,
isCall() ? ElemOpEmitter::Kind::Call : ElemOpEmitter::Kind::Get,
isSuperElem ? ElemOpEmitter::ObjKind::Super
: ElemOpEmitter::ObjKind::Other);
state_ = State::ElemCallee;
return *eoe_;
}
bool CallOrNewEmitter::prepareForFunctionCallee() {
MOZ_ASSERT(state_ == State::Start);
MOZ_ASSERT(!bce_->emittingRunOnceLambda);
if (bce_->checkRunOnceContext()) {
autoEmittingRunOnceLambda_.emplace(bce_);
}
state_ = State::FunctionCallee;
return true;
}
bool CallOrNewEmitter::emitSuperCallee() {
MOZ_ASSERT(state_ == State::Start);
if (!bce_->emitThisEnvironmentCallee()) {
return false;
}
if (!bce_->emit1(JSOP_SUPERFUN)) {
return false;
}
if (!bce_->emit1(JSOP_IS_CONSTRUCTING)) {
return false;
}
state_ = State::SuperCallee;
return true;
}
bool CallOrNewEmitter::prepareForOtherCallee() {
MOZ_ASSERT(state_ == State::Start);
state_ = State::OtherCallee;
return true;
}
bool CallOrNewEmitter::emitThis() {
MOZ_ASSERT(state_ == State::NameCallee || state_ == State::PropCallee ||
state_ == State::ElemCallee || state_ == State::FunctionCallee ||
state_ == State::SuperCallee || state_ == State::OtherCallee);
bool needsThis = false;
switch (state_) {
case State::NameCallee:
if (!isCall()) {
needsThis = true;
}
break;
case State::PropCallee:
poe_.reset();
if (!isCall()) {
needsThis = true;
}
break;
case State::ElemCallee:
eoe_.reset();
if (!isCall()) {
needsThis = true;
}
break;
case State::FunctionCallee:
autoEmittingRunOnceLambda_.reset();
needsThis = true;
break;
case State::SuperCallee:
break;
case State::OtherCallee:
needsThis = true;
break;
default:;
}
if (needsThis) {
if (isNew() || isSuperCall()) {
if (!bce_->emit1(JSOP_IS_CONSTRUCTING)) {
return false;
}
} else {
if (!bce_->emit1(JSOP_UNDEFINED)) {
return false;
}
}
}
state_ = State::This;
return true;
}
void CallOrNewEmitter::reset() {
MOZ_ASSERT(state_ == State::End);
state_ = State::Start;
}
bool CallOrNewEmitter::prepareForNonSpreadArguments() {
MOZ_ASSERT(state_ == State::This);
MOZ_ASSERT(!isSpread());
state_ = State::Arguments;
return true;
}
bool CallOrNewEmitter::wantSpreadOperand() {
MOZ_ASSERT(state_ == State::This);
MOZ_ASSERT(isSpread());
state_ = State::WantSpreadOperand;
return isSingleSpreadRest();
}
bool CallOrNewEmitter::emitSpreadArgumentsTest() {
MOZ_ASSERT(state_ == State::WantSpreadOperand);
MOZ_ASSERT(isSpread());
if (isSingleSpreadRest()) {
ifNotOptimizable_.emplace(bce_);
if (!bce_->emit1(JSOP_OPTIMIZE_SPREADCALL)) {
return false;
}
if (!bce_->emit1(JSOP_NOT)) {
return false;
}
if (!ifNotOptimizable_->emitThen()) {
return false;
}
if (!bce_->emit1(JSOP_POP)) {
return false;
}
}
state_ = State::Arguments;
return true;
}
bool CallOrNewEmitter::emitEnd(uint32_t argc, const Maybe<uint32_t>& beginPos) {
MOZ_ASSERT(state_ == State::Arguments);
if (isSingleSpreadRest()) {
if (!ifNotOptimizable_->emitEnd()) {
return false;
}
ifNotOptimizable_.reset();
}
if (isNew() || isSuperCall()) {
if (isSuperCall()) {
if (!bce_->emit1(JSOP_NEWTARGET)) {
return false;
}
} else {
uint32_t effectiveArgc = isSpread() ? 1 : argc;
if (!bce_->emitDupAt(effectiveArgc + 1)) {
return false;
}
}
}
if (beginPos) {
if (!bce_->updateSourceCoordNotes(*beginPos)) {
return false;
}
}
if (!bce_->markSimpleBreakpoint()) {
return false;
}
if (!isSpread()) {
if (!bce_->emitCall(op_, argc)) {
return false;
}
} else {
if (!bce_->emit1(op_)) {
return false;
}
}
if (isEval() && beginPos) {
uint32_t lineNum = bce_->parser->errorReporter().lineAt(*beginPos);
if (!bce_->emitUint32Operand(JSOP_LINENO, lineNum)) {
return false;
}
}
state_ = State::End;
return true;
}