Files
basilisk55/js/src/frontend/FunctionEmitter.cpp
T

1033 lines
28 KiB
C++

/* -*- 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/FunctionEmitter.h"
#include "mozilla/Assertions.h" // MOZ_ASSERT
#include "jsscript.h" // JSScript
#include "builtin/ModuleObject.h" // ModuleObject
#include "frontend/BytecodeEmitter.h" // BytecodeEmitter
#include "frontend/NameAnalysisTypes.h" // NameLocation
#include "frontend/NameOpEmitter.h" // NameOpEmitter
#include "frontend/Parser.h" // BindingIter
#include "frontend/PropOpEmitter.h" // PropOpEmitter
#include "frontend/SharedContext.h" // SharedContext
#include "vm/AsyncFunction.h" // AsyncFunctionResolveKind
#include "vm/Opcodes.h" // JSOP_*
#include "vm/Scope.h" // BindingKind
#include "wasm/AsmJS.h" // IsAsmJSModule
using namespace js;
using namespace js::frontend;
using mozilla::Maybe;
using mozilla::Some;
FunctionEmitter::FunctionEmitter(BytecodeEmitter* bce_, FunctionBox* funbox,
FunctionSyntaxKind syntaxKind,
bool 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()
{
// Mark as singletons any function which will only be executed once, or
// which is inner to a lambda we only expect to run once. In the latter
// case, if the lambda runs multiple times then CloneFunctionObject will
// make a deep clone of its contents.
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);
// [stack]
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);
// [stack]
if (!emitFunction()) {
// [stack] FUN?
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);
// [stack]
funbox_->wasEmitted = true;
if (!interpretedCommon())
return false;
// We need to update the static scope chain regardless of whether
// the LazyScript has already been initialized, due to the case
// where we previously successfully compiled an inner function's
// lazy script but failed to compile the outer script after the
// fact. If we attempt to compile the outer script again, the
// static scope chain will be newly allocated and will mismatch
// the previously compiled LazyScript's.
ScriptSourceObject* source = &bce_->script->sourceObject()->as<ScriptSourceObject>();
fun_->lazyScript()->setEnclosingScopeAndSource(bce_->innermostScope(), source);
if (bce_->emittingRunOnceLambda)
fun_->lazyScript()->setTreatAsRunOnce();
if (!emitFunction()) {
// [stack] FUN?
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());
// [stack]
// Annex B block-scoped functions are hoisted like any other assignment
// that assigns the function to the outer 'var' binding.
if (!funbox_->isAnnexB) {
#ifdef DEBUG
state_ = State::End;
#endif
return true;
}
// Get the location of the 'var' binding in the body scope. The
// name must be found, else there is a bug in the Annex B handling
// in Parser.
//
// In sloppy eval contexts, this location is dynamic.
Maybe<NameLocation> lhsLoc = bce_->locationOfNameBoundInScope(name_, bce_->varEmitterScope);
// If there are parameter expressions, the var name could be a
// parameter.
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_));
// [stack]
funbox_->wasEmitted = true;
if (!emitFunction()) {
// [stack]
return false;
}
#ifdef DEBUG
state_ = State::End;
#endif
return true;
}
bool FunctionEmitter::emitFunction()
{
// Make the function object a literal in the outer script's pool.
unsigned index = bce_->objectList.add(funbox_);
// [stack]
if (!isHoisted_) {
return emitNonHoisted(index);
// [stack] FUN?
}
bool topLevelFunction;
if (bce_->sc->isFunctionBox() ||
(bce_->sc->isEvalContext() && bce_->sc->strict())) {
// No nested functions inside other functions are top-level.
topLevelFunction = false;
} else {
// In sloppy eval scripts, top-level functions are accessed dynamically.
// In global and module scripts, top-level functions are those bound in
// the var scope.
NameLocation loc = bce_->lookupName(name_);
topLevelFunction = loc.kind() == NameLocation::Kind::Dynamic ||
loc.bindingKind() == BindingKind::Var;
}
if (topLevelFunction) {
return emitTopLevelFunction(index);
// [stack]
}
return emitHoisted(index);
// [stack]
}
bool FunctionEmitter::emitNonHoisted(unsigned index)
{
// Non-hoisted functions simply emit their respective op.
// [stack]
// JSOP_LAMBDA_ARROW is always preceded by a opcode that pushes new.target.
// See below.
MOZ_ASSERT(fun_->isArrow() == (syntaxKind_ == FunctionSyntaxKind::Arrow));
bool needsProto = syntaxKind_ == FunctionSyntaxKind::DerivedClassConstructor;
if (funbox_->isAsync()) {
MOZ_ASSERT(!needsProto);
return bce_->emitAsyncWrapper(index, funbox_->needsHomeObject(), fun_->isArrow(),
fun_->isStarGenerator());
}
if (fun_->isArrow()) {
if (!emitNewTargetForArrow()) {
// [stack] NEW.TARGET/NULL
return false;
}
}
if (needsProto) {
// [stack] PROTO
if (!bce_->emitIndex32(JSOP_FUNWITHPROTO, index)) {
// [stack] FUN
return false;
}
return true;
}
// This is a FunctionExpression, ArrowFunctionExpression, or class
// constructor. Emit the single instruction (without location info).
JSOp op = syntaxKind_ == FunctionSyntaxKind::Arrow ? JSOP_LAMBDA_ARROW
: JSOP_LAMBDA;
if (!bce_->emitIndex32(op, index)) {
// [stack] FUN
return false;
}
return true;
}
bool FunctionEmitter::emitHoisted(unsigned index)
{
MOZ_ASSERT(syntaxKind_ == FunctionSyntaxKind::Statement);
// [stack]
// For functions nested within functions and blocks, make a lambda and
// initialize the binding name of the function in the current scope.
NameOpEmitter noe(bce_, name_, NameOpEmitter::Kind::Initialize);
if (!noe.prepareForRhs()) {
// [stack]
return false;
}
if (funbox_->isAsync()) {
if (!bce_->emitAsyncWrapper(index, /* needsHomeObject = */ false,
/* isArrow = */ false, funbox_->isStarGenerator()))
{
return false;
}
} else {
if (!bce_->emitIndexOp(JSOP_LAMBDA, index)) {
return false;
}
}
if (!noe.emitAssignment()) {
// [stack] FUN
return false;
}
if (!bce_->emit1(JSOP_POP)) {
// [stack]
return false;
}
return true;
}
bool FunctionEmitter::emitTopLevelFunction(unsigned index)
{
// [stack]
if (bce_->sc->isModuleContext()) {
// For modules, we record the function and instantiate the binding
// during ModuleInstantiate(), before the script is run.
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);
bce_->switchToPrologue();
if (funbox_->isAsync()) {
if (!bce_->emitAsyncWrapper(index, fun_->isMethod(), fun_->isArrow(),
fun_->isStarGenerator()))
return false;
} else {
if (!bce_->emitIndex32(JSOP_LAMBDA, index))
return false;
}
if (!bce_->emit1(JSOP_DEFFUN)) {
// [stack]
return false;
}
bce_->switchToMain();
return true;
}
bool FunctionEmitter::emitNewTargetForArrow()
{
// [stack]
if (bce_->sc->allowNewTarget()) {
if (!bce_->emit1(JSOP_NEWTARGET)) {
// [stack] NEW.TARGET
return false;
}
} else {
if (!bce_->emit1(JSOP_NULL)) {
// [stack] NULL
return false;
}
}
return true;
}
bool FunctionScriptEmitter::prepareForParameters()
{
MOZ_ASSERT(state_ == State::Start);
// [stack]
if (paramStart_) {
bce_->setScriptStartOffsetIfUnset(*paramStart_);
}
// The ordering of these EmitterScopes is important. The named lambda
// scope needs to enclose the function scope needs to enclose the extra
// var scope.
if (funbox_->namedLambdaBindings()) {
namedLambdaEmitterScope_.emplace(bce_);
if (!namedLambdaEmitterScope_->enterNamedLambda(bce_, funbox_))
return false;
}
/*
* Emit a prologue for run-once scripts which will deoptimize JIT code
* if the script ends up running multiple times via foo.caller related
* shenanigans.
*
* Also mark the script so that initializers created within it may be
* given more precise types.
*/
if (bce_->isRunOnceLambda()) {
bce_->script->setTreatAsRunOnce();
MOZ_ASSERT(!bce_->script->hasRunOnce());
bce_->switchToPrologue();
if (!bce_->emit1(JSOP_RUNONCE))
return false;
bce_->switchToMain();
}
if (bodyEnd_) {
bce_->setFunctionBodyEndPos(*bodyEnd_);
}
if (paramStart_) {
if (!bce_->updateLineNumberNotes(*paramStart_))
return false;
}
tdzCache_.emplace(bce_);
functionEmitterScope_.emplace(bce_);
if (funbox_->hasParameterExprs) {
// There's parameter exprs, emit them in the main section.
//
// One caveat is that Debugger considers ops in the prologue to be
// unreachable (i.e. cannot set a breakpoint on it). If there are no
// parameter exprs, any unobservable environment ops (like pushing the
// call object, setting '.this', etc) need to go in the prologue, else it
// messes up breakpoint tests.
bce_->switchToMain();
}
if (!functionEmitterScope_->enterFunction(bce_, funbox_))
return false;
if (!bce_->emitInitializeFunctionSpecialNames()) {
// [stack]
return false;
}
if (!funbox_->hasParameterExprs)
bce_->switchToMain();
#ifdef DEBUG
state_ = State::Parameters;
#endif
return true;
}
bool FunctionScriptEmitter::prepareForBody()
{
MOZ_ASSERT(state_ == State::Parameters);
// [stack]
if (!emitExtraBodyVarScope()) {
// [stack]
return false;
}
if (funbox_->function()->kind() == JSFunction::FunctionKind::ClassConstructor) {
if (funbox_->isDerivedClassConstructor()) {
if (!bce_->emitCopyInitializersToLocalInitializers()) {
// [stack]
return false;
}
} else {
if (!bce_->emitInitializeInstanceFields(false)) {
// [stack]
return false;
}
}
}
#ifdef DEBUG
state_ = State::Body;
#endif
return true;
}
bool FunctionScriptEmitter::emitExtraBodyVarScope()
{
// [stack]
if (!funbox_->hasExtraBodyVarScope()) {
return true;
}
extraBodyVarEmitterScope_.emplace(bce_);
if (!extraBodyVarEmitterScope_->enterFunctionExtraBodyVar(bce_, funbox_))
return false;
if (!funbox_->extraVarScopeBindings() || !funbox_->functionScopeBindings())
return true;
// After emitting expressions for all parameters, copy over any formal
// parameters which have been redeclared as vars. For example, in the
// following, the var y in the body scope is 42:
//
// function f(x, y = 42) { var y; }
//
JS::Rooted<JSAtom*> name(bce_->cx);
for (BindingIter bi(*funbox_->functionScopeBindings(), true); bi; bi++) {
name = bi.name();
// There may not be a var binding of the same name.
if (!bce_->locationOfNameBoundInScope(name, extraBodyVarEmitterScope_.ptr())) {
continue;
}
// The '.this' and '.generator' function special
// bindings should never appear in the extra var
// scope. 'arguments', however, may.
MOZ_ASSERT(name != bce_->cx->names().dotThis &&
name != bce_->cx->names().dotGenerator);
NameOpEmitter noe(bce_, name, NameOpEmitter::Kind::Initialize);
if (!noe.prepareForRhs()) {
// [stack]
return false;
}
NameLocation paramLoc = *bce_->locationOfNameBoundInScope(name, functionEmitterScope_.ptr());
if (!bce_->emitGetNameAtLocation(name, paramLoc)) {
// [stack] VAL
return false;
}
if (!noe.emitAssignment()) {
// [stack] VAL
return false;
}
if (!bce_->emit1(JSOP_POP)) {
// [stack]
return false;
}
}
return true;
}
bool FunctionScriptEmitter::emitEndBody()
{
MOZ_ASSERT(state_ == State::Body);
// [stack]
if (funbox_->needsFinalYield()) {
// If we fall off the end of a generator, do a final yield.
bool needsIteratorResult = funbox_->needsIteratorResult();
if (needsIteratorResult) {
if (!bce_->emitPrepareIteratorResult()) {
// [stack] RESULT
return false;
}
}
if (!bce_->emit1(JSOP_UNDEFINED)) {
// [stack] RESULT? UNDEF
return false;
}
if (needsIteratorResult) {
if (!bce_->emitFinishIteratorResult(true)) {
// [stack] RESULT
return false;
}
}
if (!bce_->emit1(JSOP_SETRVAL)) {
// [stack]
return false;
}
if (!bce_->emitGetDotGeneratorInInnermostScope()) {
// [stack] GEN
return false;
}
// No need to check for finally blocks, etc as in EmitReturn.
if (!bce_->emitYieldOp(JSOP_FINALYIELDRVAL)) {
// [stack]
return false;
}
} else {
// Non-generator functions just return |undefined|. The
// JSOP_RETRVAL emitted below will do that, except if the
// script has a finally block: there can be a non-undefined
// value in the return value slot. Make sure the return value
// is |undefined|.
if (bce_->hasTryFinally) {
if (!bce_->emit1(JSOP_UNDEFINED)) {
// [stack] UNDEF
return false;
}
if (!bce_->emit1(JSOP_SETRVAL)) {
// [stack]
return false;
}
}
}
if (funbox_->isDerivedClassConstructor()) {
if (!bce_->emitCheckDerivedClassConstructorReturn()) {
// [stack]
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;
}
}
// Always end the script with a JSOP_RETRVAL. Some other parts of the
// codebase depend on this opcode,
// e.g. InterpreterRegs::setToEndOfScript.
if (!bce_->emit1(JSOP_RETRVAL)) {
// [stack]
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);
// [stack]
if (funbox_->hasParameterExprs) {
if (!bce_->emitArgOp(JSOP_GETARG, argSlot_)) {
// [stack] ARG
return false;
}
if (!emitAssignment(paramName)) {
// [stack]
return false;
}
}
argSlot_++;
return true;
}
bool FunctionParamsEmitter::prepareForDefault()
{
MOZ_ASSERT(state_ == State::Start);
// [stack]
if (!enterParameterExpressionVarScope()) {
return false;
}
if (!prepareForInitializer()) {
// [stack]
return false;
}
#ifdef DEBUG
state_ = State::Default;
#endif
return true;
}
bool FunctionParamsEmitter::emitDefaultEnd(JS::Handle<JSAtom*> paramName)
{
MOZ_ASSERT(state_ == State::Default);
// [stack] DEFAULT
if (!emitInitializerEnd()) {
// [stack] ARG/DEFAULT
return false;
}
if (!emitAssignment(paramName)) {
// [stack]
return false;
}
if (!leaveParameterExpressionVarScope()) {
return false;
}
argSlot_++;
#ifdef DEBUG
state_ = State::Start;
#endif
return true;
}
bool FunctionParamsEmitter::prepareForDestructuring()
{
MOZ_ASSERT(state_ == State::Start);
// [stack]
if (!enterParameterExpressionVarScope()) {
return false;
}
if (!bce_->emitArgOp(JSOP_GETARG, argSlot_)) {
// [stack] ARG
return false;
}
#ifdef DEBUG
state_ = State::Destructuring;
#endif
return true;
}
bool FunctionParamsEmitter::emitDestructuringEnd()
{
MOZ_ASSERT(state_ == State::Destructuring);
// [stack] ARG
if (!bce_->emit1(JSOP_POP)) {
// [stack]
return false;
}
if (!leaveParameterExpressionVarScope()) {
return false;
}
argSlot_++;
#ifdef DEBUG
state_ = State::Start;
#endif
return true;
}
bool FunctionParamsEmitter::prepareForDestructuringDefaultInitializer()
{
MOZ_ASSERT(state_ == State::Start);
// [stack]
if (!enterParameterExpressionVarScope()) {
return false;
}
if (!prepareForInitializer()) {
// [stack]
return false;
}
#ifdef DEBUG
state_ = State::DestructuringDefaultInitializer;
#endif
return true;
}
bool FunctionParamsEmitter::prepareForDestructuringDefault()
{
MOZ_ASSERT(state_ == State::DestructuringDefaultInitializer);
// [stack] DEFAULT
if (!emitInitializerEnd()) {
// [stack] ARG/DEFAULT
return false;
}
#ifdef DEBUG
state_ = State::DestructuringDefault;
#endif
return true;
}
bool FunctionParamsEmitter::emitDestructuringDefaultEnd()
{
MOZ_ASSERT(state_ == State::DestructuringDefault);
// [stack] ARG/DEFAULT
if (!bce_->emit1(JSOP_POP)) {
// [stack]
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);
// [stack]
if (!emitRestArray()) {
// [stack] REST
return false;
}
if (!emitAssignment(paramName)) {
// [stack]
return false;
}
#ifdef DEBUG
state_ = State::End;
#endif
return true;
}
bool FunctionParamsEmitter::prepareForDestructuringRest()
{
MOZ_ASSERT(state_ == State::Start);
// [stack]
if (!enterParameterExpressionVarScope()) {
return false;
}
if (!emitRestArray()) {
// [stack] REST
return false;
}
#ifdef DEBUG
state_ = State::DestructuringRest;
#endif
return true;
}
bool FunctionParamsEmitter::emitDestructuringRestEnd()
{
MOZ_ASSERT(state_ == State::DestructuringRest);
// [stack] REST
if (!bce_->emit1(JSOP_POP)) {
// [stack]
return false;
}
if (!leaveParameterExpressionVarScope())
return false;
#ifdef DEBUG
state_ = State::End;
#endif
return true;
}
bool FunctionParamsEmitter::enterParameterExpressionVarScope()
{
if (!funbox_->hasDirectEvalInParameterExpr)
return true;
// ES 14.1.19 says if BindingElement contains an expression in the
// production FormalParameter : BindingElement, it is evaluated in a
// new var environment. This is needed to prevent vars from escaping
// direct eval in parameter expressions.
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()
{
// [stack]
// If we have an initializer, emit the initializer and assign it
// to the argument slot. TDZ is taken care of afterwards.
MOZ_ASSERT(funbox_->hasParameterExprs);
if (!bce_->emitArgOp(JSOP_GETARG, argSlot_)) {
// [stack] ARG
return false;
}
default_.emplace(bce_);
if (!default_->prepareForDefault()) {
// [stack]
return false;
}
return true;
}
bool FunctionParamsEmitter::emitInitializerEnd()
{
// [stack] DEFAULT
if (!default_->emitEnd()) {
// [stack] ARG/DEFAULT
return false;
}
default_.reset();
return true;
}
bool FunctionParamsEmitter::emitRestArray()
{
// [stack]
if (!bce_->emit1(JSOP_REST)) {
// [stack] REST
return false;
}
return true;
}
bool FunctionParamsEmitter::emitAssignment(JS::Handle<JSAtom*> paramName)
{
// [stack] ARG
NameLocation paramLoc = *bce_->locationOfNameBoundInScope(paramName, functionEmitterScope_);
// RHS is already pushed in the caller side.
// Make sure prepareForRhs doesn't touch stack.
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()) {
// [stack] ARG
return false;
}
if (!noe.emitAssignment()) {
// [stack] ARG
return false;
}
if (!bce_->emit1(JSOP_POP)) {
// [stack]
return false;
}
return true;
}
DestructuringFlavor FunctionParamsEmitter::getDestructuringFlavor()
{
MOZ_ASSERT(state_ == State::Destructuring ||
state_ == State::DestructuringDefault ||
state_ == State::DestructuringRest);
return funbox_->hasDirectEvalInParameterExpr
? DestructuringFormalParameterInVarScope
: DestructuringDeclaration;
}