#include "frontend/EmitterScope.h"
#include "frontend/BytecodeEmitter.h"
#include "frontend/ModuleSharedContext.h"
#include "frontend/TDZCheckCache.h"
#include "vm/GlobalObject.h"
using namespace js;
using namespace js::frontend;
using mozilla::DebugOnly;
using mozilla::Maybe;
using mozilla::Nothing;
using mozilla::Some;
EmitterScope::EmitterScope(BytecodeEmitter* bce)
: Nestable<EmitterScope>(&bce->innermostEmitterScope_),
nameCache_(bce->cx->frontendCollectionPool()),
hasEnvironment_(false),
environmentChainLength_(0),
nextFrameSlot_(0),
scopeIndex_(ScopeNote::NoScopeIndex),
noteIndex_(ScopeNote::NoScopeNoteIndex) {}
static inline void MarkAllBindingsClosedOver(LexicalScope::Data& data) {
TrailingNamesArray& names = data.trailingNames;
for (uint32_t i = 0; i < data.length; i++) {
names[i] = BindingName(names[i].name(), true);
}
}
bool EmitterScope::ensureCache(BytecodeEmitter* bce) {
return nameCache_.acquire(bce->cx);
}
template <typename BindingIter>
bool EmitterScope::checkSlotLimits(BytecodeEmitter* bce,
const BindingIter& bi) {
if (bi.nextFrameSlot() >= LOCALNO_LIMIT ||
bi.nextEnvironmentSlot() >= ENVCOORD_SLOT_LIMIT) {
bce->reportError(nullptr, JSMSG_TOO_MANY_LOCALS);
return false;
}
return true;
}
bool EmitterScope::checkEnvironmentChainLength(BytecodeEmitter* bce) {
uint32_t hops;
if (EmitterScope* emitterScope = enclosing(&bce)) {
hops = emitterScope->environmentChainLength_;
} else {
hops = bce->sc->compilationEnclosingScope()->environmentChainLength();
}
if (hops >= ENVCOORD_HOPS_LIMIT - 1) {
bce->reportError(nullptr, JSMSG_TOO_DEEP, js_function_str);
return false;
}
environmentChainLength_ = mozilla::AssertedCast<uint8_t>(hops + 1);
return true;
}
void EmitterScope::updateFrameFixedSlots(BytecodeEmitter* bce,
const BindingIter& bi) {
nextFrameSlot_ = bi.nextFrameSlot();
if (nextFrameSlot_ > bce->maxFixedSlots) {
bce->maxFixedSlots = nextFrameSlot_;
}
MOZ_ASSERT_IF(
bce->sc->isFunctionBox() && (bce->sc->asFunctionBox()->isGenerator() ||
bce->sc->asFunctionBox()->isAsync()),
bce->maxFixedSlots == 0);
}
bool EmitterScope::putNameInCache(BytecodeEmitter* bce, JSAtom* name,
NameLocation loc) {
NameLocationMap& cache = *nameCache_;
NameLocationMap::AddPtr p = cache.lookupForAdd(name);
MOZ_ASSERT(!p);
if (!cache.add(p, name, loc)) {
ReportOutOfMemory(bce->cx);
return false;
}
return true;
}
Maybe<NameLocation> EmitterScope::lookupInCache(BytecodeEmitter* bce,
JSAtom* name) {
if (NameLocationMap::Ptr p = nameCache_->lookup(name)) {
return Some(p->value().wrapped);
}
if (fallbackFreeNameLocation_ && nameCanBeFree(bce, name)) {
return fallbackFreeNameLocation_;
}
return Nothing();
}
EmitterScope* EmitterScope::enclosing(BytecodeEmitter** bce) const {
if (EmitterScope* inFrame = enclosingInFrame()) {
return inFrame;
}
if ((*bce)->parent) {
*bce = (*bce)->parent;
return (*bce)->innermostEmitterScopeNoCheck();
}
return nullptr;
}
Scope* EmitterScope::enclosingScope(BytecodeEmitter* bce) const {
if (EmitterScope* es = enclosing(&bce)) {
return es->scope(bce);
}
return bce->sc->compilationEnclosingScope();
}
bool EmitterScope::nameCanBeFree(BytecodeEmitter* bce, JSAtom* name) {
return name != bce->cx->names().dotGenerator;
}
#ifdef DEBUG
static bool NameIsOnEnvironment(Scope* scope, JSAtom* name) {
for (BindingIter bi(scope); bi; bi++) {
if (bi.name() == name) {
BindingLocation::Kind kind = bi.location().kind();
if (bi.hasArgumentSlot()) {
JSScript* script = scope->as<FunctionScope>().script();
if (!script->strict() && !script->functionHasParameterExprs()) {
for (BindingIter bi2(bi); bi2 && bi2.hasArgumentSlot(); bi2++) {
if (bi2.name() == name) {
kind = bi2.location().kind();
}
}
}
}
return kind == BindingLocation::Kind::Global ||
kind == BindingLocation::Kind::Environment ||
kind == BindingLocation::Kind::Import;
}
}
return true;
}
#endif
NameLocation EmitterScope::searchInEnclosingScope(JSAtom* name, Scope* scope,
uint8_t hops) {
for (ScopeIter si(scope); si; si++) {
MOZ_ASSERT(NameIsOnEnvironment(si.scope(), name));
bool hasEnv = si.hasSyntacticEnvironment();
switch (si.kind()) {
case ScopeKind::Function:
if (hasEnv) {
JSScript* script = si.scope()->as<FunctionScope>().script();
if (script->funHasExtensibleScope()) {
return NameLocation::Dynamic();
}
for (BindingIter bi(si.scope()); bi; bi++) {
if (bi.name() != name) {
continue;
}
BindingLocation bindLoc = bi.location();
if (bi.hasArgumentSlot() && !script->strict() &&
!script->functionHasParameterExprs()) {
for (BindingIter bi2(bi); bi2 && bi2.hasArgumentSlot(); bi2++) {
if (bi2.name() == name) {
bindLoc = bi2.location();
}
}
}
MOZ_ASSERT(bindLoc.kind() == BindingLocation::Kind::Environment);
return NameLocation::EnvironmentCoordinate(bi.kind(), hops,
bindLoc.slot());
}
}
break;
case ScopeKind::FunctionBodyVar:
case ScopeKind::ParameterExpressionVar:
case ScopeKind::Lexical:
case ScopeKind::NamedLambda:
case ScopeKind::StrictNamedLambda:
case ScopeKind::SimpleCatch:
case ScopeKind::Catch:
if (hasEnv) {
for (BindingIter bi(si.scope()); bi; bi++) {
if (bi.name() != name) {
continue;
}
BindingLocation bindLoc = bi.location();
MOZ_ASSERT(bindLoc.kind() == BindingLocation::Kind::Environment);
return NameLocation::EnvironmentCoordinate(bi.kind(), hops,
bindLoc.slot());
}
}
break;
case ScopeKind::Module:
if (hasEnv) {
for (BindingIter bi(si.scope()); bi; bi++) {
if (bi.name() != name) {
continue;
}
BindingLocation bindLoc = bi.location();
if (bindLoc.kind() == BindingLocation::Kind::Import) {
MOZ_ASSERT(si.kind() == ScopeKind::Module);
return NameLocation::Import();
}
MOZ_ASSERT(bindLoc.kind() == BindingLocation::Kind::Environment);
return NameLocation::EnvironmentCoordinate(bi.kind(), hops,
bindLoc.slot());
}
}
break;
case ScopeKind::Eval:
case ScopeKind::StrictEval:
if (!hasEnv && si.scope()->enclosing()->is<GlobalScope>()) {
return NameLocation::Global(BindingKind::Var);
}
return NameLocation::Dynamic();
case ScopeKind::Global:
return NameLocation::Global(BindingKind::Var);
case ScopeKind::With:
case ScopeKind::NonSyntactic:
return NameLocation::Dynamic();
case ScopeKind::WasmInstance:
case ScopeKind::WasmFunction:
MOZ_CRASH("No direct eval inside wasm functions");
}
if (hasEnv) {
MOZ_ASSERT(hops < ENVCOORD_HOPS_LIMIT - 1);
hops++;
}
}
MOZ_CRASH("Malformed scope chain");
}
NameLocation EmitterScope::searchAndCache(BytecodeEmitter* bce, JSAtom* name) {
Maybe<NameLocation> loc;
uint8_t hops = hasEnvironment() ? 1 : 0;
DebugOnly<bool> inCurrentScript = enclosingInFrame();
for (EmitterScope* es = enclosing(&bce); es; es = es->enclosing(&bce)) {
loc = es->lookupInCache(bce, name);
if (loc) {
if (loc->kind() == NameLocation::Kind::EnvironmentCoordinate) {
*loc = loc->addHops(hops);
}
break;
}
if (es->hasEnvironment()) {
hops++;
}
#ifdef DEBUG
if (!es->enclosingInFrame()) {
inCurrentScript = false;
}
#endif
}
if (!loc) {
inCurrentScript = false;
loc = Some(searchInEnclosingScope(
name, bce->sc->compilationEnclosingScope(), hops));
}
MOZ_ASSERT_IF(!inCurrentScript, loc->kind() != NameLocation::Kind::FrameSlot);
if (!putNameInCache(bce, name, *loc)) {
bce->cx->recoverFromOutOfMemory();
}
return *loc;
}
template <typename ScopeCreator>
bool EmitterScope::internScope(BytecodeEmitter* bce, ScopeCreator createScope) {
RootedScope enclosing(bce->cx, enclosingScope(bce));
Scope* scope = createScope(bce->cx, enclosing);
if (!scope) {
return false;
}
hasEnvironment_ = scope->hasEnvironment();
scopeIndex_ = bce->scopeList.length();
return bce->scopeList.append(scope);
}
template <typename ScopeCreator>
bool EmitterScope::internBodyScope(BytecodeEmitter* bce,
ScopeCreator createScope) {
MOZ_ASSERT(bce->bodyScopeIndex == UINT32_MAX,
"There can be only one body scope");
bce->bodyScopeIndex = bce->scopeList.length();
return internScope(bce, createScope);
}
bool EmitterScope::appendScopeNote(BytecodeEmitter* bce) {
MOZ_ASSERT(ScopeKindIsInBody(scope(bce)->kind()) && enclosingInFrame(),
"Scope notes are not needed for body-level scopes.");
noteIndex_ = bce->scopeNoteList.length();
return bce->scopeNoteList.append(index(), bce->offset(),
enclosingInFrame()
? enclosingInFrame()->noteIndex()
: ScopeNote::NoScopeNoteIndex);
}
bool EmitterScope::deadZoneFrameSlotRange(BytecodeEmitter* bce,
uint32_t slotStart,
uint32_t slotEnd) const {
if (slotStart != slotEnd) {
if (!bce->emit1(JSOP_UNINITIALIZED)) {
return false;
}
for (uint32_t slot = slotStart; slot < slotEnd; slot++) {
if (!bce->emitLocalOp(JSOP_INITLEXICAL, slot)) {
return false;
}
}
if (!bce->emit1(JSOP_POP)) {
return false;
}
}
return true;
}
void EmitterScope::dump(BytecodeEmitter* bce) {
fprintf(stdout, "EmitterScope [%s] %p\n", ScopeKindString(scope(bce)->kind()),
this);
for (NameLocationMap::Range r = nameCache_->all(); !r.empty(); r.popFront()) {
const NameLocation& l = r.front().value();
UniqueChars bytes = AtomToPrintableString(bce->cx, r.front().key());
if (!bytes) {
return;
}
if (l.kind() != NameLocation::Kind::Dynamic) {
fprintf(stdout, " %s %s ", BindingKindString(l.bindingKind()),
bytes.get());
} else {
fprintf(stdout, " %s ", bytes.get());
}
switch (l.kind()) {
case NameLocation::Kind::Dynamic:
fprintf(stdout, "dynamic\n");
break;
case NameLocation::Kind::Global:
fprintf(stdout, "global\n");
break;
case NameLocation::Kind::Intrinsic:
fprintf(stdout, "intrinsic\n");
break;
case NameLocation::Kind::NamedLambdaCallee:
fprintf(stdout, "named lambda callee\n");
break;
case NameLocation::Kind::Import:
fprintf(stdout, "import\n");
break;
case NameLocation::Kind::ArgumentSlot:
fprintf(stdout, "arg slot=%u\n", l.argumentSlot());
break;
case NameLocation::Kind::FrameSlot:
fprintf(stdout, "frame slot=%u\n", l.frameSlot());
break;
case NameLocation::Kind::EnvironmentCoordinate:
fprintf(stdout, "environment hops=%u slot=%u\n",
l.environmentCoordinate().hops(),
l.environmentCoordinate().slot());
break;
case NameLocation::Kind::DynamicAnnexBVar:
fprintf(stdout, "dynamic annex b var\n");
break;
}
}
fprintf(stdout, "\n");
}
bool EmitterScope::enterLexical(BytecodeEmitter* bce, ScopeKind kind,
Handle<LexicalScope::Data*> bindings) {
MOZ_ASSERT(kind != ScopeKind::NamedLambda &&
kind != ScopeKind::StrictNamedLambda);
MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck());
if (!ensureCache(bce)) {
return false;
}
if (bce->sc->allBindingsClosedOver()) {
MarkAllBindingsClosedOver(*bindings);
}
TDZCheckCache* tdzCache = bce->innermostTDZCheckCache;
uint32_t firstFrameSlot = frameSlotStart();
BindingIter bi(*bindings, firstFrameSlot, false);
for (; bi; bi++) {
if (!checkSlotLimits(bce, bi)) {
return false;
}
NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
if (!putNameInCache(bce, bi.name(), loc)) {
return false;
}
if (!tdzCache->noteTDZCheck(bce, bi.name(), CheckTDZ)) {
return false;
}
}
updateFrameFixedSlots(bce, bi);
auto createScope = [kind, bindings, firstFrameSlot](JSContext* cx,
HandleScope enclosing) {
return LexicalScope::create(cx, kind, bindings, firstFrameSlot, enclosing);
};
if (!internScope(bce, createScope)) {
return false;
}
if (ScopeKindIsInBody(kind) && hasEnvironment()) {
if (!bce->emitInternedScopeOp(index(), JSOP_PUSHLEXICALENV)) {
return false;
}
}
if (!appendScopeNote(bce)) {
return false;
}
if (!deadZoneFrameSlotRange(bce, firstFrameSlot, frameSlotEnd())) {
return false;
}
return checkEnvironmentChainLength(bce);
}
bool EmitterScope::enterNamedLambda(BytecodeEmitter* bce, FunctionBox* funbox) {
MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck());
MOZ_ASSERT(funbox->namedLambdaBindings());
if (!ensureCache(bce)) {
return false;
}
if (funbox->allBindingsClosedOver()) {
MarkAllBindingsClosedOver(*funbox->namedLambdaBindings());
}
BindingIter bi(*funbox->namedLambdaBindings(), LOCALNO_LIMIT,
true);
MOZ_ASSERT(bi.kind() == BindingKind::NamedLambdaCallee);
NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
if (!putNameInCache(bce, bi.name(), loc)) {
return false;
}
bi++;
MOZ_ASSERT(!bi, "There should be exactly one binding in a NamedLambda scope");
auto createScope = [funbox](JSContext* cx, HandleScope enclosing) {
ScopeKind scopeKind = funbox->strict() ? ScopeKind::StrictNamedLambda
: ScopeKind::NamedLambda;
return LexicalScope::create(cx, scopeKind, funbox->namedLambdaBindings(),
LOCALNO_LIMIT, enclosing);
};
if (!internScope(bce, createScope)) {
return false;
}
return checkEnvironmentChainLength(bce);
}
bool EmitterScope::enterFunction(BytecodeEmitter* bce, FunctionBox* funbox) {
MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck());
if (!funbox->hasExtraBodyVarScope()) {
bce->setVarEmitterScope(this);
}
if (!ensureCache(bce)) {
return false;
}
auto bindings = funbox->functionScopeBindings();
Maybe<uint32_t> lastLexicalSlot;
if (bindings) {
NameLocationMap& cache = *nameCache_;
BindingIter bi(*bindings, funbox->hasParameterExprs);
for (; bi; bi++) {
if (!checkSlotLimits(bce, bi)) {
return false;
}
NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
NameLocationMap::AddPtr p = cache.lookupForAdd(bi.name());
if (p) {
MOZ_ASSERT(bi.kind() == BindingKind::FormalParameter);
MOZ_ASSERT(!funbox->hasDestructuringArgs);
MOZ_ASSERT(!funbox->hasRest());
p->value() = loc;
continue;
}
if (!cache.add(p, bi.name(), loc)) {
ReportOutOfMemory(bce->cx);
return false;
}
}
updateFrameFixedSlots(bce, bi);
} else {
nextFrameSlot_ = 0;
}
if (!funbox->hasParameterExprs && funbox->hasExtensibleScope()) {
fallbackFreeNameLocation_ = Some(NameLocation::Dynamic());
}
if (funbox->hasParameterExprs && nextFrameSlot_) {
uint32_t paramFrameSlotEnd = 0;
for (BindingIter bi(*bindings, true); bi; bi++) {
if (!BindingKindIsLexical(bi.kind())) {
break;
}
NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
if (loc.kind() == NameLocation::Kind::FrameSlot) {
MOZ_ASSERT(paramFrameSlotEnd <= loc.frameSlot());
paramFrameSlotEnd = loc.frameSlot() + 1;
}
}
if (!deadZoneFrameSlotRange(bce, 0, paramFrameSlotEnd)) {
return false;
}
}
auto createScope = [funbox](JSContext* cx, HandleScope enclosing) {
RootedFunction fun(cx, funbox->function());
return FunctionScope::create(
cx, funbox->functionScopeBindings(), funbox->hasParameterExprs,
funbox->needsCallObjectRegardlessOfBindings(), fun, enclosing);
};
if (!internBodyScope(bce, createScope)) {
return false;
}
return checkEnvironmentChainLength(bce);
}
bool EmitterScope::enterFunctionExtraBodyVar(BytecodeEmitter* bce,
FunctionBox* funbox) {
MOZ_ASSERT(funbox->hasParameterExprs);
MOZ_ASSERT(funbox->extraVarScopeBindings() ||
funbox->needsExtraBodyVarEnvironmentRegardlessOfBindings());
MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck());
bce->setVarEmitterScope(this);
if (!ensureCache(bce)) {
return false;
}
uint32_t firstFrameSlot = frameSlotStart();
if (auto bindings = funbox->extraVarScopeBindings()) {
BindingIter bi(*bindings, firstFrameSlot);
for (; bi; bi++) {
if (!checkSlotLimits(bce, bi)) {
return false;
}
NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
if (!putNameInCache(bce, bi.name(), loc)) {
return false;
}
}
updateFrameFixedSlots(bce, bi);
} else {
nextFrameSlot_ = firstFrameSlot;
}
if (funbox->hasExtensibleScope()) {
fallbackFreeNameLocation_ = Some(NameLocation::Dynamic());
}
auto createScope = [funbox, firstFrameSlot](JSContext* cx,
HandleScope enclosing) {
return VarScope::create(
cx, ScopeKind::FunctionBodyVar, funbox->extraVarScopeBindings(),
firstFrameSlot,
funbox->needsExtraBodyVarEnvironmentRegardlessOfBindings(), enclosing);
};
if (!internScope(bce, createScope)) {
return false;
}
if (hasEnvironment()) {
if (!bce->emitInternedScopeOp(index(), JSOP_PUSHVARENV)) {
return false;
}
}
if (!appendScopeNote(bce)) {
return false;
}
return checkEnvironmentChainLength(bce);
}
bool EmitterScope::enterParameterExpressionVar(BytecodeEmitter* bce) {
MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck());
if (!ensureCache(bce)) {
return false;
}
fallbackFreeNameLocation_ = Some(NameLocation::Dynamic());
uint32_t firstFrameSlot = frameSlotStart();
auto createScope = [firstFrameSlot](JSContext* cx, HandleScope enclosing) {
return VarScope::create(cx, ScopeKind::ParameterExpressionVar,
nullptr, firstFrameSlot,
true, enclosing);
};
if (!internScope(bce, createScope)) {
return false;
}
MOZ_ASSERT(hasEnvironment());
if (!bce->emitInternedScopeOp(index(), JSOP_PUSHVARENV)) {
return false;
}
if (!appendScopeNote(bce)) {
return false;
}
return checkEnvironmentChainLength(bce);
}
class DynamicBindingIter : public BindingIter {
public:
explicit DynamicBindingIter(GlobalSharedContext* sc)
: BindingIter(*sc->bindings) {}
explicit DynamicBindingIter(EvalSharedContext* sc)
: BindingIter(*sc->bindings, false) {
MOZ_ASSERT(!sc->strict());
}
JSOp bindingOp() const {
switch (kind()) {
case BindingKind::Var:
return JSOP_DEFVAR;
case BindingKind::Let:
return JSOP_DEFLET;
case BindingKind::Const:
return JSOP_DEFCONST;
default:
MOZ_CRASH("Bad BindingKind");
}
}
};
bool EmitterScope::enterGlobal(BytecodeEmitter* bce,
GlobalSharedContext* globalsc) {
MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck());
bce->setVarEmitterScope(this);
if (!ensureCache(bce)) {
return false;
}
if (bce->emitterMode == BytecodeEmitter::SelfHosting) {
fallbackFreeNameLocation_ = Some(NameLocation::Intrinsic());
auto createScope = [](JSContext* cx, HandleScope enclosing) {
MOZ_ASSERT(!enclosing);
return &cx->global()->emptyGlobalScope();
};
return internBodyScope(bce, createScope);
}
if (globalsc->bindings) {
for (DynamicBindingIter bi(globalsc); bi; bi++) {
NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
JSAtom* name = bi.name();
if (!putNameInCache(bce, name, loc)) {
return false;
}
if (bi.isTopLevelFunction()) {
continue;
}
if (!bce->emitAtomOp(name, bi.bindingOp())) {
return false;
}
}
}
if (globalsc->scopeKind() == ScopeKind::Global) {
fallbackFreeNameLocation_ = Some(NameLocation::Global(BindingKind::Var));
} else {
fallbackFreeNameLocation_ = Some(NameLocation::Dynamic());
}
auto createScope = [globalsc](JSContext* cx, HandleScope enclosing) {
MOZ_ASSERT(!enclosing);
return GlobalScope::create(cx, globalsc->scopeKind(), globalsc->bindings);
};
return internBodyScope(bce, createScope);
}
bool EmitterScope::enterEval(BytecodeEmitter* bce, EvalSharedContext* evalsc) {
MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck());
bce->setVarEmitterScope(this);
if (!ensureCache(bce)) {
return false;
}
fallbackFreeNameLocation_ = Some(NameLocation::Dynamic());
auto createScope = [evalsc](JSContext* cx, HandleScope enclosing) {
ScopeKind scopeKind =
evalsc->strict() ? ScopeKind::StrictEval : ScopeKind::Eval;
return EvalScope::create(cx, scopeKind, evalsc->bindings, enclosing);
};
if (!internBodyScope(bce, createScope)) {
return false;
}
if (hasEnvironment()) {
if (!bce->emitInternedScopeOp(index(), JSOP_PUSHVARENV)) {
return false;
}
} else {
if (!hasEnvironment() && evalsc->bindings) {
for (DynamicBindingIter bi(evalsc); bi; bi++) {
MOZ_ASSERT(bi.bindingOp() == JSOP_DEFVAR);
if (bi.isTopLevelFunction()) {
continue;
}
if (!bce->emitAtomOp(bi.name(), JSOP_DEFVAR)) {
return false;
}
}
}
if (scope(bce)->enclosing()->is<GlobalScope>()) {
fallbackFreeNameLocation_ = Some(NameLocation::Global(BindingKind::Var));
}
}
return true;
}
bool EmitterScope::enterModule(BytecodeEmitter* bce,
ModuleSharedContext* modulesc) {
MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck());
bce->setVarEmitterScope(this);
if (!ensureCache(bce)) {
return false;
}
TDZCheckCache* tdzCache = bce->innermostTDZCheckCache;
Maybe<uint32_t> firstLexicalFrameSlot;
if (ModuleScope::Data* bindings = modulesc->bindings) {
BindingIter bi(*bindings);
for (; bi; bi++) {
if (!checkSlotLimits(bce, bi)) {
return false;
}
NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
if (!putNameInCache(bce, bi.name(), loc)) {
return false;
}
if (BindingKindIsLexical(bi.kind())) {
if (loc.kind() == NameLocation::Kind::FrameSlot &&
!firstLexicalFrameSlot) {
firstLexicalFrameSlot = Some(loc.frameSlot());
}
if (!tdzCache->noteTDZCheck(bce, bi.name(), CheckTDZ)) {
return false;
}
}
}
updateFrameFixedSlots(bce, bi);
} else {
nextFrameSlot_ = 0;
}
fallbackFreeNameLocation_ = Some(NameLocation::Global(BindingKind::Var));
if (firstLexicalFrameSlot) {
if (!deadZoneFrameSlotRange(bce, *firstLexicalFrameSlot, frameSlotEnd())) {
return false;
}
}
auto createScope = [modulesc](JSContext* cx, HandleScope enclosing) {
return ModuleScope::create(cx, modulesc->bindings, modulesc->module(),
enclosing);
};
if (!internBodyScope(bce, createScope)) {
return false;
}
return checkEnvironmentChainLength(bce);
}
bool EmitterScope::enterWith(BytecodeEmitter* bce) {
MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck());
if (!ensureCache(bce)) {
return false;
}
fallbackFreeNameLocation_ = Some(NameLocation::Dynamic());
auto createScope = [](JSContext* cx, HandleScope enclosing) {
return WithScope::create(cx, enclosing);
};
if (!internScope(bce, createScope)) {
return false;
}
if (!bce->emitInternedScopeOp(index(), JSOP_ENTERWITH)) {
return false;
}
if (!appendScopeNote(bce)) {
return false;
}
return checkEnvironmentChainLength(bce);
}
bool EmitterScope::deadZoneFrameSlots(BytecodeEmitter* bce) const {
return deadZoneFrameSlotRange(bce, frameSlotStart(), frameSlotEnd());
}
bool EmitterScope::leave(BytecodeEmitter* bce, bool nonLocal) {
MOZ_ASSERT_IF(!nonLocal, this == bce->innermostEmitterScopeNoCheck());
ScopeKind kind = scope(bce)->kind();
switch (kind) {
case ScopeKind::Lexical:
case ScopeKind::SimpleCatch:
case ScopeKind::Catch:
if (!bce->emit1(hasEnvironment() ? JSOP_POPLEXICALENV
: JSOP_DEBUGLEAVELEXICALENV)) {
return false;
}
break;
case ScopeKind::With:
if (!bce->emit1(JSOP_LEAVEWITH)) {
return false;
}
break;
case ScopeKind::ParameterExpressionVar:
MOZ_ASSERT(hasEnvironment());
if (!bce->emit1(JSOP_POPVARENV)) {
return false;
}
break;
case ScopeKind::Function:
case ScopeKind::FunctionBodyVar:
case ScopeKind::NamedLambda:
case ScopeKind::StrictNamedLambda:
case ScopeKind::Eval:
case ScopeKind::StrictEval:
case ScopeKind::Global:
case ScopeKind::NonSyntactic:
case ScopeKind::Module:
break;
case ScopeKind::WasmInstance:
case ScopeKind::WasmFunction:
MOZ_CRASH("No wasm function scopes in JS");
}
if (!nonLocal) {
if (ScopeKindIsInBody(kind)) {
uint32_t offset =
kind == ScopeKind::FunctionBodyVar ? UINT32_MAX : bce->offset();
bce->scopeNoteList.recordEnd(noteIndex_, offset);
}
}
return true;
}
Scope* EmitterScope::scope(const BytecodeEmitter* bce) const {
return bce->scopeList.vector[index()];
}
NameLocation EmitterScope::lookup(BytecodeEmitter* bce, JSAtom* name) {
if (Maybe<NameLocation> loc = lookupInCache(bce, name)) {
return *loc;
}
return searchAndCache(bce, name);
}
Maybe<NameLocation> EmitterScope::locationBoundInScope(JSAtom* name,
EmitterScope* target) {
uint8_t extraHops = 0;
for (EmitterScope* es = this; es != target; es = es->enclosingInFrame()) {
if (es->hasEnvironment()) {
extraHops++;
}
}
Maybe<NameLocation> loc;
if (NameLocationMap::Ptr p = target->nameCache_->lookup(name)) {
NameLocation l = p->value().wrapped;
if (l.kind() == NameLocation::Kind::EnvironmentCoordinate) {
loc = Some(l.addHops(extraHops));
} else {
loc = Some(l);
}
}
return loc;
}