/* -*- 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/. */
#ifndef frontend_EmitterScope_h
#define frontend_EmitterScope_h
#include "mozilla/Maybe.h"
#include <stdint.h>
#include "ds/Nestable.h"
#include "frontend/AbstractScopePtr.h"
#include "frontend/NameAnalysisTypes.h"
#include "frontend/NameCollections.h"
#include "frontend/Stencil.h"
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
# include
"frontend/UsingEmitter.h"
#endif
#include "vm/Opcodes.h" // JSOp
#include "vm/SharedStencil.h" // GCThingIndex
namespace js {
namespace frontend {
struct BytecodeEmitter;
class EvalSharedContext;
class FunctionBox;
class GlobalSharedContext;
class ModuleSharedContext;
class TaggedParserAtomIndex;
// A scope that introduces bindings.
class EmitterScope :
public Nestable<EmitterScope> {
// The cache of bound names that may be looked up in the
// scope. Initially populated as the set of names this scope binds. As
// names are looked up in enclosing scopes, they are cached on the
// current scope.
PooledMapPtr<NameLocationMap> nameCache_;
// If this scope's cache does not include free names, such as the
// global scope, the NameLocation to return.
mozilla::Maybe<NameLocation> fallbackFreeNameLocation_;
// True if there is a corresponding EnvironmentObject on the environment
// chain, false if all bindings are stored in frame slots on the stack.
bool hasEnvironment_;
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
mozilla::Maybe<UsingEmitter> usingEmitter_;
mozilla::Maybe<ForOfDisposalEmitter> forOfDisposalEmitter_;
private:
BlockKind blockKind_ = BlockKind::Other;
#endif
// The number of enclosing environments. Used for error checking.
uint8_t environmentChainLength_;
// The next usable slot on the frame for not-closed over bindings.
//
// The initial frame slot when assigning slots to bindings is the
// enclosing scope's nextFrameSlot. For the first scope in a frame,
// the initial frame slot is 0.
uint32_t nextFrameSlot_;
// The index in the BytecodeEmitter's interned scope vector, otherwise
// ScopeNote::NoScopeIndex.
GCThingIndex scopeIndex_;
// If kind is Lexical, Catch, or With, the index in the BytecodeEmitter's
// block scope note list. Otherwise ScopeNote::NoScopeNote.
uint32_t noteIndex_;
[[nodiscard]]
bool ensureCache(BytecodeEmitter* bce);
[[nodiscard]]
bool checkSlotLimits(BytecodeEmitter* bce,
const ParserBindingIter& bi);
[[nodiscard]]
bool checkEnvironmentChainLength(BytecodeEmitter* bce);
void updateFrameFixedSlots(BytecodeEmitter* bce,
const ParserBindingIter& bi);
[[nodiscard]]
bool putNameInCache(BytecodeEmitter* bce,
TaggedParserAtomIndex name,
NameLocation loc);
mozilla::Maybe<NameLocation> lookupInCache(BytecodeEmitter* bce,
TaggedParserAtomIndex name);
EmitterScope* enclosing(BytecodeEmitter** bce)
const;
mozilla::Maybe<ScopeIndex> enclosingScopeIndex(BytecodeEmitter* bce)
const;
static bool nameCanBeFree(BytecodeEmitter* bce, TaggedParserAtomIndex name);
NameLocation searchAndCache(BytecodeEmitter* bce, TaggedParserAtomIndex name);
[[nodiscard]]
bool internEmptyGlobalScopeAsBody(BytecodeEmitter* bce);
[[nodiscard]]
bool internScopeStencil(BytecodeEmitter* bce, ScopeIndex index);
[[nodiscard]]
bool internBodyScopeStencil(BytecodeEmitter* bce,
ScopeIndex index);
[[nodiscard]]
bool appendScopeNote(BytecodeEmitter* bce);
[[nodiscard]]
bool clearFrameSlotRange(BytecodeEmitter* bce, JSOp opcode,
uint32_t slotStart,
uint32_t slotEnd)
const;
[[nodiscard]]
bool deadZoneFrameSlotRange(BytecodeEmitter* bce,
uint32_t slotStart,
uint32_t slotEnd)
const {
return clearFrameSlotRange(bce, JSOp::Uninitialized, slotStart, slotEnd);
}
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
void setHasDisposables(BytecodeEmitter* bce) {
if (!usingEmitter_.isSome()) {
usingEmitter_.emplace(bce);
}
}
#endif
public:
explicit EmitterScope(BytecodeEmitter* bce);
void dump(BytecodeEmitter* bce);
[[nodiscard]]
bool enterLexical(BytecodeEmitter* bce, ScopeKind kind,
LexicalScope::ParserData* bindings
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
,
BlockKind blockKind = BlockKind::Other
#endif
);
[[nodiscard]]
bool enterClassBody(BytecodeEmitter* bce, ScopeKind kind,
ClassBodyScope::ParserData* bindings);
[[nodiscard]]
bool enterNamedLambda(BytecodeEmitter* bce,
FunctionBox* funbox);
[[nodiscard]]
bool enterFunction(BytecodeEmitter* bce, FunctionBox* funbox);
[[nodiscard]]
bool enterFunctionExtraBodyVar(BytecodeEmitter* bce,
FunctionBox* funbox);
[[nodiscard]]
bool enterGlobal(BytecodeEmitter* bce,
GlobalSharedContext* globalsc);
[[nodiscard]]
bool enterEval(BytecodeEmitter* bce, EvalSharedContext* evalsc);
[[nodiscard]]
bool enterModule(BytecodeEmitter* module,
ModuleSharedContext* modulesc);
[[nodiscard]]
bool enterWith(BytecodeEmitter* bce);
[[nodiscard]]
bool deadZoneFrameSlots(BytecodeEmitter* bce)
const;
[[nodiscard]]
bool leave(BytecodeEmitter* bce,
bool nonLocal =
false);
GCThingIndex index()
const {
MOZ_ASSERT(scopeIndex_ != ScopeNote::NoScopeIndex,
"Did you forget to intern a Scope?");
return scopeIndex_;
}
uint32_t noteIndex()
const {
return noteIndex_; }
AbstractScopePtr scope(
const BytecodeEmitter* bce)
const;
mozilla::Maybe<ScopeIndex> scopeIndex(
const BytecodeEmitter* bce)
const;
bool hasEnvironment()
const {
return hasEnvironment_; }
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
private:
// Disposable Scope here refers to any scope
// with using bindings in it for now that is
// a lexical scope and a module scope.
[[nodiscard]]
bool prepareForDisposableScopeBody(BytecodeEmitter* bce);
[[nodiscard]]
bool emitSwitchBlockEndForDisposableScopeBodyEnd(
BytecodeEmitter* bce);
[[nodiscard]]
bool emitDisposableScopeBodyEnd(BytecodeEmitter* bce);
[[nodiscard]]
bool emitDisposableScopeBodyEndForNonLocalJump(
BytecodeEmitter* bce);
public:
[[nodiscard]]
bool prepareForModuleDisposableScopeBody(BytecodeEmitter* bce);
[[nodiscard]]
bool emitModuleDisposableScopeBodyEnd(BytecodeEmitter* bce);
[[nodiscard]]
bool prepareForDisposableAssignment(UsingHint hint);
bool hasDisposables()
const {
return usingEmitter_.isSome(); }
bool hasAsyncDisposables()
const {
return hasDisposables() && usingEmitter_->hasAwaitUsing();
}
#endif
// The first frame slot used.
uint32_t frameSlotStart()
const {
if (EmitterScope* inFrame = enclosingInFrame()) {
return inFrame->nextFrameSlot_;
}
return 0;
}
// The last frame slot used + 1.
uint32_t frameSlotEnd()
const {
return nextFrameSlot_; }
EmitterScope* enclosingInFrame()
const {
return Nestable<EmitterScope>::enclosing();
}
NameLocation lookup(BytecodeEmitter* bce, TaggedParserAtomIndex name);
// Find both the slot associated with a private name and the location of the
// corresponding `.privateBrand` binding.
//
// Simply doing two separate lookups, one for `name` and another for
// `.privateBrand`, would give the wrong answer in this case:
//
// class Outer {
// #outerMethod() { reutrn "ok"; }
//
// test() {
// class Inner {
// #innerMethod() {}
// test(outer) {
// return outer.#outerMethod();
// }
// }
// return new Inner().test(this);
// }
// }
//
// new Outer().test(); // should return "ok"
//
// At the point in Inner.test where `#outerMethod` is called, we need to
// check for the private brand of `Outer`, not `Inner`; but both class bodies
// have `.privateBrand` bindings. In a normal `lookup`, the inner binding
// would shadow the outer one.
//
// This method instead sets `brandLoc` to the location of the `.privateBrand`
// binding in the same class body as the private name `name`, ignoring
// shadowing. If `name` refers to a name that is actually stamped onto the
// target object (anything other than a non-static private method), then
// `brandLoc` is set to Nothing.
void lookupPrivate(BytecodeEmitter* bce, TaggedParserAtomIndex name,
NameLocation& loc, mozilla::Maybe<NameLocation>& brandLoc);
mozilla::Maybe<NameLocation> locationBoundInScope(TaggedParserAtomIndex name,
EmitterScope* target);
// For a given emitter scope, return the number of enclosing environments in
// the current compilation (this excludes environments that could enclose the
// compilation, like would happen for an eval copmilation).
static uint32_t CountEnclosingCompilationEnvironments(
BytecodeEmitter* bce, EmitterScope* emitterScope);
};
}
/* namespace frontend */
}
/* namespace js */
#endif /* frontend_EmitterScope_h */