#include "frontend/FunctionEmitter.h"
#include "mozilla/Assertions.h"
#include "builtin/ModuleObject.h"
#include "frontend/BytecodeEmitter.h"
#include "frontend/ModuleSharedContext.h"
#include "frontend/NameAnalysisTypes.h"
#include "frontend/NameOpEmitter.h"
#include "frontend/ParseContext.h"
#include "frontend/PropOpEmitter.h"
#include "frontend/SharedContext.h"
#include "vm/AsyncFunction.h"
#include "vm/JSScript.h"
#include "vm/Opcodes.h"
#include "vm/Scope.h"
#include "wasm/AsmJS.h"
using namespace js;
using namespace js::frontend;
using mozilla::Maybe;
using mozilla::Some;
FunctionEmitter::FunctionEmitter(BytecodeEmitter* bce, FunctionBox* funbox,
FunctionSyntaxKind syntaxKind,
IsHoisted isHoisted)
: bce_(bce),
funbox_(funbox),
fun_(bce_->cx, funbox_->function()),
name_(bce_->cx, fun_->explicitName()),
syntaxKind_(syntaxKind),
isHoisted_(isHoisted) {
MOZ_ASSERT_IF(fun_->isInterpretedLazy(), fun_->lazyScript());
}
bool FunctionEmitter::interpretedCommon() {
bool singleton = bce_->checkRunOnceContext();
if (!JSFunction::setTypeForScriptedFunction(bce_->cx, fun_, singleton)) {
return false;
}
SharedContext* outersc = bce_->sc;
if (outersc->isFunctionBox()) {
outersc->asFunctionBox()->setHasInnerFunctions();
}
return true;
}
bool FunctionEmitter::prepareForNonLazy() {
MOZ_ASSERT(state_ == State::Start);
MOZ_ASSERT(fun_->isInterpreted());
MOZ_ASSERT(!fun_->isInterpretedLazy());
MOZ_ASSERT(!funbox_->wasEmitted);
funbox_->wasEmitted = true;
if (!interpretedCommon()) {
return false;
}
MOZ_ASSERT_IF(bce_->sc->strict(), funbox_->strictScript);
#ifdef DEBUG
state_ = State::NonLazy;
#endif
return true;
}
bool FunctionEmitter::emitNonLazyEnd() {
MOZ_ASSERT(state_ == State::NonLazy);
if (!emitFunction()) {
return false;
}
#ifdef DEBUG
state_ = State::End;
#endif
return true;
}
bool FunctionEmitter::emitLazy() {
MOZ_ASSERT(state_ == State::Start);
MOZ_ASSERT(fun_->isInterpreted());
MOZ_ASSERT(fun_->isInterpretedLazy());
MOZ_ASSERT(!funbox_->wasEmitted);
funbox_->wasEmitted = true;
if (!interpretedCommon()) {
return false;
}
funbox_->setEnclosingScopeForInnerLazyFunction(bce_->innermostScope());
if (bce_->emittingRunOnceLambda) {
fun_->lazyScript()->setTreatAsRunOnce();
}
if (!emitFunction()) {
return false;
}
#ifdef DEBUG
state_ = State::End;
#endif
return true;
}
bool FunctionEmitter::emitAgain() {
MOZ_ASSERT(state_ == State::Start);
MOZ_ASSERT(funbox_->wasEmitted);
MOZ_ASSERT_IF(fun_->hasScript(), fun_->nonLazyScript());
if (!funbox_->isAnnexB) {
#ifdef DEBUG
state_ = State::End;
#endif
return true;
}
Maybe<NameLocation> lhsLoc =
bce_->locationOfNameBoundInScope(name_, bce_->varEmitterScope);
if (!lhsLoc && bce_->sc->isFunctionBox() &&
bce_->sc->asFunctionBox()->hasExtraBodyVarScope()) {
lhsLoc = bce_->locationOfNameBoundInScope(
name_, bce_->varEmitterScope->enclosingInFrame());
}
if (!lhsLoc) {
lhsLoc = Some(NameLocation::DynamicAnnexBVar());
} else {
MOZ_ASSERT(lhsLoc->bindingKind() == BindingKind::Var ||
lhsLoc->bindingKind() == BindingKind::FormalParameter ||
(lhsLoc->bindingKind() == BindingKind::Let &&
bce_->sc->asFunctionBox()->hasParameterExprs));
}
NameOpEmitter noe(bce_, name_, *lhsLoc,
NameOpEmitter::Kind::SimpleAssignment);
if (!noe.prepareForRhs()) {
return false;
}
if (!bce_->emitGetName(name_)) {
return false;
}
if (!noe.emitAssignment()) {
return false;
}
if (!bce_->emit1(JSOP_POP)) {
return false;
}
#ifdef DEBUG
state_ = State::End;
#endif
return true;
}
bool FunctionEmitter::emitAsmJSModule() {
MOZ_ASSERT(state_ == State::Start);
MOZ_ASSERT(!funbox_->wasEmitted);
MOZ_ASSERT(IsAsmJSModule(fun_));
funbox_->wasEmitted = true;
if (!emitFunction()) {
return false;
}
#ifdef DEBUG
state_ = State::End;
#endif
return true;
}
bool FunctionEmitter::emitFunction() {
unsigned index = bce_->objectList.add(funbox_);
if (isHoisted_ == IsHoisted::No) {
return emitNonHoisted(index);
}
bool topLevelFunction;
if (bce_->sc->isFunctionBox() ||
(bce_->sc->isEvalContext() && bce_->sc->strict())) {
topLevelFunction = false;
} else {
NameLocation loc = bce_->lookupName(name_);
topLevelFunction = loc.kind() == NameLocation::Kind::Dynamic ||
loc.bindingKind() == BindingKind::Var;
}
if (topLevelFunction) {
return emitTopLevelFunction(index);
}
return emitHoisted(index);
}
bool FunctionEmitter::emitNonHoisted(unsigned index) {
MOZ_ASSERT(fun_->isArrow() == (syntaxKind_ == FunctionSyntaxKind::Arrow));
if (fun_->isArrow()) {
if (!emitNewTargetForArrow()) {
return false;
}
}
if (syntaxKind_ == FunctionSyntaxKind::DerivedClassConstructor) {
if (!bce_->emitIndex32(JSOP_FUNWITHPROTO, index)) {
return false;
}
return true;
}
JSOp op = syntaxKind_ == FunctionSyntaxKind::Arrow ? JSOP_LAMBDA_ARROW
: JSOP_LAMBDA;
if (!bce_->emitIndex32(op, index)) {
return false;
}
return true;
}
bool FunctionEmitter::emitHoisted(unsigned index) {
MOZ_ASSERT(syntaxKind_ == FunctionSyntaxKind::Statement);
NameOpEmitter noe(bce_, name_, NameOpEmitter::Kind::Initialize);
if (!noe.prepareForRhs()) {
return false;
}
if (!bce_->emitIndexOp(JSOP_LAMBDA, index)) {
return false;
}
if (!noe.emitAssignment()) {
return false;
}
if (!bce_->emit1(JSOP_POP)) {
return false;
}
return true;
}
bool FunctionEmitter::emitTopLevelFunction(unsigned index) {
if (bce_->sc->isModuleContext()) {
JS::Rooted<ModuleObject*> module(bce_->cx,
bce_->sc->asModuleContext()->module());
if (!module->noteFunctionDeclaration(bce_->cx, name_, fun_)) {
return false;
}
return true;
}
MOZ_ASSERT(bce_->sc->isGlobalContext() || bce_->sc->isEvalContext());
MOZ_ASSERT(syntaxKind_ == FunctionSyntaxKind::Statement);
MOZ_ASSERT(bce_->inPrologue());
if (!bce_->emitIndex32(JSOP_LAMBDA, index)) {
return false;
}
if (!bce_->emit1(JSOP_DEFFUN)) {
return false;
}
return true;
}
bool FunctionEmitter::emitNewTargetForArrow() {
if (bce_->sc->allowNewTarget()) {
if (!bce_->emit1(JSOP_NEWTARGET)) {
return false;
}
} else {
if (!bce_->emit1(JSOP_NULL)) {
return false;
}
}
return true;
}
bool FunctionScriptEmitter::prepareForParameters() {
MOZ_ASSERT(state_ == State::Start);
MOZ_ASSERT(bce_->inPrologue());
if (paramStart_) {
bce_->setScriptStartOffsetIfUnset(*paramStart_);
}
if (funbox_->namedLambdaBindings()) {
namedLambdaEmitterScope_.emplace(bce_);
if (!namedLambdaEmitterScope_->enterNamedLambda(bce_, funbox_)) {
return false;
}
}
if (bce_->isRunOnceLambda()) {
bce_->script->setTreatAsRunOnce();
MOZ_ASSERT(!bce_->script->hasRunOnce());
}
if (bodyEnd_) {
bce_->setFunctionBodyEndPos(*bodyEnd_);
}
if (paramStart_) {
if (!bce_->updateLineNumberNotes(*paramStart_)) {
return false;
}
}
tdzCache_.emplace(bce_);
functionEmitterScope_.emplace(bce_);
if (funbox_->hasParameterExprs) {
bce_->switchToMain();
}
if (!functionEmitterScope_->enterFunction(bce_, funbox_)) {
return false;
}
if (!bce_->emitInitializeFunctionSpecialNames()) {
return false;
}
if (!funbox_->hasParameterExprs) {
bce_->switchToMain();
}
if (funbox_->hasParameterExprs && funbox_->needsPromiseResult()) {
if (!emitAsyncFunctionRejectPrologue()) {
return false;
}
}
#ifdef DEBUG
state_ = State::Parameters;
#endif
return true;
}
bool FunctionScriptEmitter::prepareForBody() {
MOZ_ASSERT(state_ == State::Parameters);
if (rejectTryCatch_) {
if (!emitAsyncFunctionRejectEpilogue()) {
return false;
}
}
if (!emitExtraBodyVarScope()) {
return false;
}
if (funbox_->needsPromiseResult()) {
if (!emitAsyncFunctionRejectPrologue()) {
return false;
}
}
if (funbox_->function()->kind() ==
JSFunction::FunctionKind::ClassConstructor) {
if (!emitInitializeInstanceFields()) {
return false;
}
}
#ifdef DEBUG
state_ = State::Body;
#endif
return true;
}
bool FunctionScriptEmitter::emitAsyncFunctionRejectPrologue() {
rejectTryCatch_.emplace(bce_, TryEmitter::Kind::TryCatch,
TryEmitter::ControlKind::NonSyntactic);
return rejectTryCatch_->emitTry();
}
bool FunctionScriptEmitter::emitAsyncFunctionRejectEpilogue() {
if (!rejectTryCatch_->emitCatch()) {
return false;
}
if (!bce_->emit1(JSOP_EXCEPTION)) {
return false;
}
if (!bce_->emitGetDotGeneratorInInnermostScope()) {
return false;
}
if (!bce_->emit2(JSOP_ASYNCRESOLVE,
uint8_t(AsyncFunctionResolveKind::Reject))) {
return false;
}
if (!bce_->emit1(JSOP_SETRVAL)) {
return false;
}
if (!bce_->emitGetDotGeneratorInInnermostScope()) {
return false;
}
if (!bce_->emit1(JSOP_FINALYIELDRVAL)) {
return false;
}
if (!rejectTryCatch_->emitEnd()) {
return false;
}
rejectTryCatch_.reset();
return true;
}
bool FunctionScriptEmitter::emitExtraBodyVarScope() {
if (!funbox_->hasExtraBodyVarScope()) {
return true;
}
extraBodyVarEmitterScope_.emplace(bce_);
if (!extraBodyVarEmitterScope_->enterFunctionExtraBodyVar(bce_, funbox_)) {
return false;
}
if (!funbox_->extraVarScopeBindings() || !funbox_->functionScopeBindings()) {
return true;
}
JS::Rooted<JSAtom*> name(bce_->cx);
for (BindingIter bi(*funbox_->functionScopeBindings(), true); bi; bi++) {
name = bi.name();
if (!bce_->locationOfNameBoundInScope(name,
extraBodyVarEmitterScope_.ptr())) {
continue;
}
MOZ_ASSERT(name != bce_->cx->names().dotThis &&
name != bce_->cx->names().dotGenerator);
NameOpEmitter noe(bce_, name, NameOpEmitter::Kind::Initialize);
if (!noe.prepareForRhs()) {
return false;
}
NameLocation paramLoc =
*bce_->locationOfNameBoundInScope(name, functionEmitterScope_.ptr());
if (!bce_->emitGetNameAtLocation(name, paramLoc)) {
return false;
}
if (!noe.emitAssignment()) {
return false;
}
if (!bce_->emit1(JSOP_POP)) {
return false;
}
}
return true;
}
bool FunctionScriptEmitter::emitInitializeInstanceFields() {
MOZ_ASSERT(bce_->fieldInitializers_.valid);
size_t numFields = bce_->fieldInitializers_.numFieldInitializers;
if (numFields == 0) {
return true;
}
if (!bce_->emitGetName(bce_->cx->names().dotInitializers)) {
return false;
}
for (size_t fieldIndex = 0; fieldIndex < numFields; fieldIndex++) {
if (fieldIndex < numFields - 1) {
if (!bce_->emit1(JSOP_DUP)) {
return false;
}
}
if (!bce_->emitNumberOp(fieldIndex)) {
return false;
}
if (!bce_->emit1(JSOP_CALLELEM)) {
return false;
}
if (!bce_->emitGetName(bce_->cx->names().dotThis)) {
return false;
}
if (!bce_->emitCall(JSOP_CALL_IGNORES_RV, 0)) {
return false;
}
if (!bce_->emit1(JSOP_POP)) {
return false;
}
}
return true;
}
bool FunctionScriptEmitter::emitEndBody() {
MOZ_ASSERT(state_ == State::Body);
if (funbox_->needsFinalYield()) {
bool needsIteratorResult = funbox_->needsIteratorResult();
if (needsIteratorResult) {
if (!bce_->emitPrepareIteratorResult()) {
return false;
}
}
if (!bce_->emit1(JSOP_UNDEFINED)) {
return false;
}
if (needsIteratorResult) {
if (!bce_->emitFinishIteratorResult(true)) {
return false;
}
}
if (funbox_->needsPromiseResult()) {
if (!bce_->emitGetDotGeneratorInInnermostScope()) {
return false;
}
if (!bce_->emit2(JSOP_ASYNCRESOLVE,
uint8_t(AsyncFunctionResolveKind::Fulfill))) {
return false;
}
}
if (!bce_->emit1(JSOP_SETRVAL)) {
return false;
}
if (!bce_->emitGetDotGeneratorInInnermostScope()) {
return false;
}
if (!bce_->emitYieldOp(JSOP_FINALYIELDRVAL)) {
return false;
}
} else {
if (bce_->hasTryFinally) {
if (!bce_->emit1(JSOP_UNDEFINED)) {
return false;
}
if (!bce_->emit1(JSOP_SETRVAL)) {
return false;
}
}
}
if (funbox_->isDerivedClassConstructor()) {
if (!bce_->emitCheckDerivedClassConstructorReturn()) {
return false;
}
}
if (rejectTryCatch_) {
if (!emitAsyncFunctionRejectEpilogue()) {
return false;
}
}
if (extraBodyVarEmitterScope_) {
if (!extraBodyVarEmitterScope_->leave(bce_)) {
return false;
}
extraBodyVarEmitterScope_.reset();
}
if (!functionEmitterScope_->leave(bce_)) {
return false;
}
functionEmitterScope_.reset();
tdzCache_.reset();
if (bodyEnd_) {
if (!bce_->updateSourceCoordNotes(*bodyEnd_)) {
return false;
}
}
if (!funbox_->hasExprBody()) {
if (!bce_->markSimpleBreakpoint()) {
return false;
}
}
if (!bce_->emit1(JSOP_RETRVAL)) {
return false;
}
if (namedLambdaEmitterScope_) {
if (!namedLambdaEmitterScope_->leave(bce_)) {
return false;
}
namedLambdaEmitterScope_.reset();
}
#ifdef DEBUG
state_ = State::EndBody;
#endif
return true;
}
bool FunctionScriptEmitter::initScript() {
MOZ_ASSERT(state_ == State::EndBody);
if (!JSScript::fullyInitFromEmitter(bce_->cx, bce_->script, bce_)) {
return false;
}
bce_->tellDebuggerAboutCompiledScript(bce_->cx);
#ifdef DEBUG
state_ = State::End;
#endif
return true;
}
FunctionParamsEmitter::FunctionParamsEmitter(BytecodeEmitter* bce,
FunctionBox* funbox)
: bce_(bce),
funbox_(funbox),
functionEmitterScope_(bce_->innermostEmitterScope()) {}
bool FunctionParamsEmitter::emitSimple(JS::Handle<JSAtom*> paramName) {
MOZ_ASSERT(state_ == State::Start);
if (funbox_->hasParameterExprs) {
if (!bce_->emitArgOp(JSOP_GETARG, argSlot_)) {
return false;
}
if (!emitAssignment(paramName)) {
return false;
}
}
argSlot_++;
return true;
}
bool FunctionParamsEmitter::prepareForDefault() {
MOZ_ASSERT(state_ == State::Start);
if (!enterParameterExpressionVarScope()) {
return false;
}
if (!prepareForInitializer()) {
return false;
}
#ifdef DEBUG
state_ = State::Default;
#endif
return true;
}
bool FunctionParamsEmitter::emitDefaultEnd(JS::Handle<JSAtom*> paramName) {
MOZ_ASSERT(state_ == State::Default);
if (!emitInitializerEnd()) {
return false;
}
if (!emitAssignment(paramName)) {
return false;
}
if (!leaveParameterExpressionVarScope()) {
return false;
}
argSlot_++;
#ifdef DEBUG
state_ = State::Start;
#endif
return true;
}
bool FunctionParamsEmitter::prepareForDestructuring() {
MOZ_ASSERT(state_ == State::Start);
if (!enterParameterExpressionVarScope()) {
return false;
}
if (!bce_->emitArgOp(JSOP_GETARG, argSlot_)) {
return false;
}
#ifdef DEBUG
state_ = State::Destructuring;
#endif
return true;
}
bool FunctionParamsEmitter::emitDestructuringEnd() {
MOZ_ASSERT(state_ == State::Destructuring);
if (!bce_->emit1(JSOP_POP)) {
return false;
}
if (!leaveParameterExpressionVarScope()) {
return false;
}
argSlot_++;
#ifdef DEBUG
state_ = State::Start;
#endif
return true;
}
bool FunctionParamsEmitter::prepareForDestructuringDefaultInitializer() {
MOZ_ASSERT(state_ == State::Start);
if (!enterParameterExpressionVarScope()) {
return false;
}
if (!prepareForInitializer()) {
return false;
}
#ifdef DEBUG
state_ = State::DestructuringDefaultInitializer;
#endif
return true;
}
bool FunctionParamsEmitter::prepareForDestructuringDefault() {
MOZ_ASSERT(state_ == State::DestructuringDefaultInitializer);
if (!emitInitializerEnd()) {
return false;
}
#ifdef DEBUG
state_ = State::DestructuringDefault;
#endif
return true;
}
bool FunctionParamsEmitter::emitDestructuringDefaultEnd() {
MOZ_ASSERT(state_ == State::DestructuringDefault);
if (!bce_->emit1(JSOP_POP)) {
return false;
}
if (!leaveParameterExpressionVarScope()) {
return false;
}
argSlot_++;
#ifdef DEBUG
state_ = State::Start;
#endif
return true;
}
bool FunctionParamsEmitter::emitRest(JS::Handle<JSAtom*> paramName) {
MOZ_ASSERT(state_ == State::Start);
if (!emitRestArray()) {
return false;
}
if (!emitAssignment(paramName)) {
return false;
}
#ifdef DEBUG
state_ = State::End;
#endif
return true;
}
bool FunctionParamsEmitter::prepareForDestructuringRest() {
MOZ_ASSERT(state_ == State::Start);
if (!enterParameterExpressionVarScope()) {
return false;
}
if (!emitRestArray()) {
return false;
}
#ifdef DEBUG
state_ = State::DestructuringRest;
#endif
return true;
}
bool FunctionParamsEmitter::emitDestructuringRestEnd() {
MOZ_ASSERT(state_ == State::DestructuringRest);
if (!bce_->emit1(JSOP_POP)) {
return false;
}
if (!leaveParameterExpressionVarScope()) {
return false;
}
#ifdef DEBUG
state_ = State::End;
#endif
return true;
}
bool FunctionParamsEmitter::enterParameterExpressionVarScope() {
if (!funbox_->hasDirectEvalInParameterExpr) {
return true;
}
paramExprVarEmitterScope_.emplace(bce_);
if (!paramExprVarEmitterScope_->enterParameterExpressionVar(bce_)) {
return false;
}
return true;
}
bool FunctionParamsEmitter::leaveParameterExpressionVarScope() {
if (!paramExprVarEmitterScope_) {
return true;
}
if (!paramExprVarEmitterScope_->leave(bce_)) {
return false;
}
paramExprVarEmitterScope_.reset();
return true;
}
bool FunctionParamsEmitter::prepareForInitializer() {
MOZ_ASSERT(funbox_->hasParameterExprs);
if (!bce_->emitArgOp(JSOP_GETARG, argSlot_)) {
return false;
}
default_.emplace(bce_);
if (!default_->prepareForDefault()) {
return false;
}
return true;
}
bool FunctionParamsEmitter::emitInitializerEnd() {
if (!default_->emitEnd()) {
return false;
}
default_.reset();
return true;
}
bool FunctionParamsEmitter::emitRestArray() {
if (!bce_->emit1(JSOP_REST)) {
return false;
}
return true;
}
bool FunctionParamsEmitter::emitAssignment(JS::Handle<JSAtom*> paramName) {
NameLocation paramLoc =
*bce_->locationOfNameBoundInScope(paramName, functionEmitterScope_);
MOZ_ASSERT(paramLoc.kind() == NameLocation::Kind::ArgumentSlot ||
paramLoc.kind() == NameLocation::Kind::FrameSlot ||
paramLoc.kind() == NameLocation::Kind::EnvironmentCoordinate);
NameOpEmitter noe(bce_, paramName, paramLoc, NameOpEmitter::Kind::Initialize);
if (!noe.prepareForRhs()) {
return false;
}
if (!noe.emitAssignment()) {
return false;
}
if (!bce_->emit1(JSOP_POP)) {
return false;
}
return true;
}
DestructuringFlavor FunctionParamsEmitter::getDestructuringFlavor() {
MOZ_ASSERT(state_ == State::Destructuring ||
state_ == State::DestructuringDefault ||
state_ == State::DestructuringRest);
return funbox_->hasDirectEvalInParameterExpr
? DestructuringFlavor::FormalParameterInVarScope
: DestructuringFlavor::Declaration;
}