Source code
Revision control
Copy as Markdown
Other Tools
/* -*- 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
#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);
[[nodiscard]] bool prepareForForOfLoopIteration(BytecodeEmitter* bce,
bool hasAwaitUsing);
[[nodiscard]] bool prepareForForOfIteratorCloseOnThrow();
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 */