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
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef vm_EnvironmentObject_h
#define vm_EnvironmentObject_h
#include <type_traits>
#include "frontend/NameAnalysisTypes.h"
#include "gc/Barrier.h"
#include "gc/WeakMap.h"
#include "js/GCHashTable.h"
#include "vm/ArgumentsObject.h"
#include "vm/GlobalObject.h"
#include "vm/JSObject.h"
#include "vm/ProxyObject.h"
#include "vm/Scope.h"
#include "vm/ScopeKind.h" // ScopeKind
namespace js {
class AbstractGeneratorObject;
class IndirectBindingMap;
class ModuleObject;
// Return the name being accessed by the given ALIASEDVAR op. This function is
// relatively slow so it should not be used on hot paths.
extern PropertyName* EnvironmentCoordinateNameSlow(JSScript* script,
jsbytecode* pc);
/*** Environment objects ****************************************************/
// clang-format off
/*
* [SMDOC] Environment Objects
*
* About environments
* ------------------
*
*
* Scoping in ES is specified in terms of "Environment Records". There's a
* global Environment Record per realm, and a new Environment Record is created
* whenever control enters a function, block, or other scope.
*
* A "Lexical Environment" is a list of nested Environment Records, innermost
* first: everything that's in scope. Throughout SpiderMonkey, "environment"
* means a Lexical Environment.
*
* N.B.: "Scope" means something different: a static scope, the compile-time
* analogue of an environment. See Scope.h.
*
* How SpiderMonkey represents environments
* ----------------------------------------
*
* Some environments are stored as JSObjects. Several kinds of objects
* represent environments:
*
* JSObject
* |
* +--NativeObject
* | |
* | +--EnvironmentObject Engine-internal environment
* | | |
* | | +--CallObject Environment of entire function
* | | |
* | | +--VarEnvironmentObject See VarScope in Scope.h.
* | | |
* | | +--(DisposableEnvironmentObject)
* | | | | Environment for `using x = ...`
* | | | | (exists only when
* | | | | ENABLE_EXPLICIT_RESOURCE_MANAGEMENT is
* | | | | defined)
* | | | |
* | | | +--ModuleEnvironmentObject
* | | | |
* | | | +--LexicalEnvironmentObject
* | | | |
* | | | +--ScopedLexicalEnvironmentObject
* | | | | | Non-extensible lexical environment
* | | | | |
* | | | | +--BlockLexicalEnvironmentObject
* | | | | | | Blocks and such: syntactic,
* | | | | | | non-extensible
* | | | | | |
* | | | | | +--NamedLambdaObject
* | | | | | Environment for `(function f(){...})`
* | | | | | containing only a binding for `f`
* | | | | |
* | | | | +--ClassBodyLexicalEnvironmentObject
* | | | | Environment for class body, containing
* | | | | private names, private brands, and
* | | | | static initializers list
* | | | |
* | | | +--ExtensibleLexicalEnvironmentObject
* | | | |
* | | | +--GlobalLexicalEnvironmentObject
* | | | | Top-level let/const/class in scripts
* | | | |
* | | | +--NonSyntacticLexicalEnvironmentObject
* | | | See "Non-syntactic environments" below
* | | |
* | | +--NonSyntacticVariablesObject
* | | | See "Non-syntactic environments" below
* | | |
* | | +--WithEnvironmentObject Presents object properties as bindings
* | | |
* | | +--RuntimeLexicalErrorObject
* | | Special value represents uninitialized
* | | lexical slots
* | |
* | +--GlobalObject The global environment (dynamically
* | presents its properties as bindings)
* +--ProxyObject
* |
* +--DebugEnvironmentProxy Environment for debugger eval-in-frame
*
* EnvironmentObjects are technically real JSObjects but only belong on the
* environment chain (that is, fp->environmentChain() or fun->environment()).
* They are never exposed to scripts.
*
* Note that reserved slots in any base classes shown above are fixed for all
* derived classes. So e.g. EnvironmentObject::enclosingEnvironment() can
* simply access a fixed slot without further dynamic type information.
*
* When the current environment is represented by an object, the stack frame
* has a pointer to that object (see AbstractFramePtr::environmentChain()).
* However, that isn't always the case. Where possible, we store binding values
* in JS stack slots. For block and function scopes where all bindings can be
* stored in stack slots, nothing is allocated in the heap; there is no
* environment object.
*
* Full information about the environment chain is always recoverable:
* EnvironmentIter can do it, and we construct a fake environment for debugger
* eval-in-frame (see "Debug environment objects" below).
*
* Syntactic Environments
* ----------------------
*
* Environments may be syntactic, i.e., corresponding to source text, or
* non-syntactic, i.e., specially created by embedding. The distinction is
* necessary to maintain invariants about the environment chain: non-syntactic
* environments may not occur in arbitrary positions in the chain.
*
* CallObject, ModuleEnvironmentObject, BlockLexicalEnvironmentObject, and
* GlobalLexicalEnvironmentObject always represent syntactic
* environments. (CallObject is considered syntactic even when it's used as the
* scope of strict eval code.) WithEnvironmentObject is syntactic when it's
* used to represent the scope of a `with` block.
*
*
* Non-syntactic Environments
* --------------------------
*
* A non-syntactic environment is one that was not created due to JS source
* code. On the scope chain, a single NonSyntactic GlobalScope maps to 0+
* non-syntactic environment objects. This is contrasted with syntactic
* environments, where each scope corresponds to 0 or 1 environment object.
*
* There are 3 kinds of dynamic environment objects:
*
* 1. WithEnvironmentObject
*
* When the embedding compiles or executes a script, it has the option to
* pass in a vector of objects to be used as the initial env chain, ordered
* from outermost env to innermost env. Each of those objects is wrapped by
* a WithEnvironmentObject.
*
* The innermost object passed in by the embedding becomes a qualified
* variables object that captures 'var' bindings. That is, it wraps the
* holder object of 'var' bindings.
*
* Does not hold 'let' or 'const' bindings.
*
* 2. NonSyntacticVariablesObject
*
* When the embedding wants qualified 'var' bindings and unqualified
* bareword assignments to go on a different object than the global
* object. While any object can be made into a qualified variables object,
* only the GlobalObject and NonSyntacticVariablesObject are considered
* unqualified variables objects.
*
* Unlike WithEnvironmentObjects that delegate to the object they wrap,
* this object is itself the holder of 'var' bindings.
*
* Does not hold 'let' or 'const' bindings.
*
* 3. NonSyntacticLexicalEnvironmentObject
*
* Each non-syntactic object used as a qualified variables object needs to
* enclose a non-syntactic lexical environment to hold 'let' and 'const'
* bindings. There is a bijection per realm between the non-syntactic
* variables objects and their non-syntactic LexicalEnvironmentObjects.
*
* Does not hold 'var' bindings.
*
* The embedding (Gecko) and debugger uses non-syntactic envs for various
* things, all of which are detailed below. All env chain listings below are,
* from top to bottom, outermost to innermost.
*
* A. JSM loading
*
* Most JSMs are loaded into a shared system global in order to save the memory
* consumption and avoid CCWs. To support this, a NonSyntacticVariablesObject
* is used for each JSM to provide a basic form of isolation.
* NonSyntacticLexicalEnvironmentObject and
* NonSyntacticVariablesObject are allocated for each JSM, and
* NonSyntacticLexicalEnvironmentObject holds lexical variables and
* NonSyntacticVariablesObject holds qualified variables. JSMs cannot have
* unqualified names, but if unqualified names are used by subscript, they
* goes to NonSyntacticVariablesObject (see B.3 and B.4).
* They have the following env chain:
*
* BackstagePass global
* |
* GlobalLexicalEnvironmentObject[this=global]
* |
* NonSyntacticVariablesObject (qualified 'var's (and unqualified names))
* |
* NonSyntacticLexicalEnvironmentObject[this=nsvo] (lexical vars)
*
* B.1 Subscript loading into a target object
*
* Subscripts may be loaded into a target object and it's associated global.
* NonSyntacticLexicalEnvironmentObject holds lexical variables and
* WithEnvironmentObject holds qualified variables. Unqualified names goes
* to the target object's global.
* They have the following env chain:
*
* Target object's global (unqualified names)
* |
* GlobalLexicalEnvironmentObject[this=global]
* |
* WithEnvironmentObject wrapping target (qualified 'var's)
* |
* NonSyntacticLexicalEnvironmentObject[this=target] (lexical vars)
*
* B.2 Subscript loading into global this
*
* Subscript may be loaded into global this. In this case no extra environment
* object is created.
*
* global (qualified 'var's and unqualified names)
* |
* GlobalLexicalEnvironmentObject[this=global] (lexical vars)
*
* B.3 Subscript loading into a target object in JSM
*
* The target object of a subscript load may be in a JSM, in which case we will
* also have the NonSyntacticVariablesObject on the chain.
* NonSyntacticLexicalEnvironmentObject for target object holds lexical
* variables and WithEnvironmentObject holds qualified variables.
* Unqualified names goes to NonSyntacticVariablesObject.
*
* BackstagePass global
* |
* GlobalLexicalEnvironmentObject[this=global]
* |
* NonSyntacticVariablesObject (unqualified names)
* |
* NonSyntacticLexicalEnvironmentObject[this=nsvo]
* |
* WithEnvironmentObject wrapping target (qualified 'var's)
* |
* NonSyntacticLexicalEnvironmentObject[this=target] (lexical vars)
*
* B.4 Subscript loading into per-JSM this
*
* Subscript may be loaded into global this. In this case no extra environment
* object is created.
*
* BackstagePass global
* |
* GlobalLexicalEnvironmentObject[this=global]
* |
* NonSyntacticVariablesObject (qualified 'var's and unqualified names)
* |
* NonSyntacticLexicalEnvironmentObject[this=nsvo] (lexical vars)
*
* C.1. Frame scripts with unique scope
*
* XUL frame scripts with unique scope are loaded in the same global as
* JSMs, with a NonSyntacticVariablesObject as a "polluting global" for
* both qualified 'var' variables and unqualified names, and a with
* environment wrapping a message manager object, and
* NonSyntacticLexicalEnvironmentObject holding the message manager as `this`,
* that holds lexical variables.
* These environment objects except for globals are created for each run and
* not shared across multiple runs. This is done exclusively in
* js::ExecuteInFrameScriptEnvironment.
*
* BackstagePass global
* |
* GlobalLexicalEnvironmentObject[this=global]
* |
* NonSyntacticVariablesObject (qualified 'var's and unqualified names)
* |
* WithEnvironmentObject wrapping messageManager
* |
* NonSyntacticLexicalEnvironmentObject[this=messageManager] (lexical vars)
*
* C.2. Frame scripts without unique scope
*
* XUL frame scripts without unique scope are loaded in the same global as
* JSMs, with a with environment wrapping a message manager object for
* qualified 'var' variables, and NonSyntacticLexicalEnvironmentObject holding
* the message manager as `this`, that holds lexical variables.
* The environment chain is associated with the message manager object
* and cached for subsequent runs.
*
* BackstagePass global (unqualified names)
* |
* GlobalLexicalEnvironmentObject[this=global]
* |
* WithEnvironmentObject wrapping messageManager (qualified 'var's)
* |
* NonSyntacticLexicalEnvironmentObject[this=messageManager] (lexical vars)
*
* D.1. DOM event handlers without direct eval
*
* DOM event handlers are compiled as functions with HTML elements on the
* environment chain. For a chain of elements e0, e1, ..., eN, where innerrmost
* element is the target element, enclosing elements are such as forms, and the
* outermost one is the document.
* If the DOM event handlers don't have direct eval, the function's scopes are
* optimized and frame slots are used for qualified 'var's and lexical vars.
* NonSyntacticLexicalEnvironmentObject's `this` value is not used, given
* the function's `this` value is used instead:
*
* global (unqualified names)
* |
* GlobalLexicalEnvironmentObject[this=global]
* |
* WithEnvironmentObject wrapping eN
* |
* ...
* |
* WithEnvironmentObject wrapping e1
* |
* WithEnvironmentObject wrapping e0
* |
* NonSyntacticLexicalEnvironmentObject [this=*unused*]
*
* D.2. DOM event handlers with direct eval
*
* If DOM event handlers have direct eval, the function's scopes are allocated
* as environment object:
*
* global (unqualified names)
* |
* GlobalLexicalEnvironmentObject[this=global]
* |
* ...
* |
* WithEnvironmentObject wrapping e1
* |
* WithEnvironmentObject wrapping e0
* |
* NonSyntacticLexicalEnvironmentObject [this=*unused*]
* |
* CallObject (qualified 'var's)
* |
* BlockLexicalEnvironmentObject (lexical vars)
*
* E.1. Debugger.Frame.prototype.evalWithBindings
*
* Debugger.Frame.prototype.evalWithBindings uses WithEnvironmentObject for
* given bindings, and the frame's enclosing scope.
*
* If qualified 'var's or unqualified names conflict with the bindings object's
* properties, they go to the WithEnvironmentObject.
*
* If the frame is function, it has the following env chain.
* lexical variables are optimized and uses frame slots, regardless of the name
* conflicts with bindings:
*
* global (unqualified names)
* |
* [DebugProxy] GlobalLexicalEnvironmentObject[this=global]
* |
* [DebugProxy] CallObject (qualified 'var's)
* |
* WithEnvironmentObject wrapping bindings (conflicting 'var's and names)
*
* If the script has direct eval, BlockLexicalEnvironmentObject is created for
* it:
*
* global (unqualified names)
* |
* [DebugProxy] GlobalLexicalEnvironmentObject[this=global]
* |
* [DebugProxy] CallObject (qualified 'var's)
* |
* WithEnvironmentObject wrapping bindings (conflicting 'var's and names)
* |
* BlockLexicalEnvironmentObject (lexical vars, and conflicting lexical vars)
*
* NOTE: Debugger.Frame.prototype.eval uses the frame's enclosing scope only,
* and it doesn't use any dynamic environment, but still uses
* non-syntactic scope to perform `eval` operation.
*
* E.2. Debugger.Object.prototype.executeInGlobalWithBindings
*
* Debugger.Object.prototype.executeInGlobalWithBindings uses
* WithEnvironmentObject for given bindings, and the object's global scope.
*
* If `options.useInnerBindings` is not true, if bindings conflict with
* qualified 'var's or global lexicals, those bindings are shadowed and not
* stored into the bindings object wrapped by WithEnvironmentObject.
*
* global (qualified 'var's and unqualified names)
* |
* GlobalLexicalEnvironmentObject[this=global] (lexical vars)
* |
* WithEnvironmentObject wrapping object with not-conflicting bindings
*
* If `options.useInnerBindings` is true, all bindings are stored into the
* bindings object wrapped by WithEnvironmentObject, and they shadow globals
*
* global (qualified 'var's and unqualified names)
* |
* GlobalLexicalEnvironmentObject[this=global] (lexical vars)
* |
* WithEnvironmentObject wrapping object with all bindings
*
* NOTE: If `options.useInnerBindings` is true, and if lexical variable names
* conflict with the bindings object's properties, the write on them
* within declarations is done for the GlobalLexicalEnvironmentObject,
* but the write within assignments and the read on lexicals are done
* from the WithEnvironmentObject (bug 1841964 and bug 1847219).
*
* // bindings = { x: 10, y: 20 };
*
* let x = 11; // written to GlobalLexicalEnvironmentObject
* x; // read from WithEnvironmentObject
* let y;
* y = 21; // written to WithEnvironmentObject
* y; // read from WithEnvironmentObject
*
* NOTE: Debugger.Object.prototype.executeInGlobal uses the object's global
* scope only, and it doesn't use any dynamic environment or
* non-syntactic scope.
* NOTE: If no extra bindings are used by script,
* Debugger.Object.prototype.executeInGlobalWithBindings uses the object's
* global scope only, and it doesn't use any dynamic environment or
* non-syntactic scope.
*
*/
// clang-format on
class EnvironmentObject : public NativeObject {
protected:
// The enclosing environment. Either another EnvironmentObject, a
// GlobalObject, or a non-syntactic environment object.
static const uint32_t ENCLOSING_ENV_SLOT = 0;
inline void setAliasedBinding(uint32_t slot, const Value& v);
public:
// Since every env chain terminates with a global object, whether
// GlobalObject or a non-syntactic one, and since those objects do not
// derive EnvironmentObject (they have completely different layouts), the
// enclosing environment of an EnvironmentObject is necessarily non-null.
JSObject& enclosingEnvironment() const {
return getReservedSlot(ENCLOSING_ENV_SLOT).toObject();
}
void initEnclosingEnvironment(JSObject* enclosing) {
initReservedSlot(ENCLOSING_ENV_SLOT, ObjectOrNullValue(enclosing));
}
static bool nonExtensibleIsFixedSlot(EnvironmentCoordinate ec) {
// For non-extensible environment objects isFixedSlot(slot) is equivalent to
// slot < MAX_FIXED_SLOTS.
return ec.slot() < MAX_FIXED_SLOTS;
}
static size_t nonExtensibleDynamicSlotIndex(EnvironmentCoordinate ec) {
MOZ_ASSERT(!nonExtensibleIsFixedSlot(ec));
return ec.slot() - MAX_FIXED_SLOTS;
}
// Get or set a name contained in this environment.
inline const Value& aliasedBinding(EnvironmentCoordinate ec);
const Value& aliasedBinding(const BindingIter& bi) {
MOZ_ASSERT(bi.location().kind() == BindingLocation::Kind::Environment);
return getSlot(bi.location().slot());
}
inline void setAliasedBinding(EnvironmentCoordinate ec, const Value& v);
inline void setAliasedBinding(const BindingIter& bi, const Value& v);
// For JITs.
static size_t offsetOfEnclosingEnvironment() {
return getFixedSlotOffset(ENCLOSING_ENV_SLOT);
}
static uint32_t enclosingEnvironmentSlot() { return ENCLOSING_ENV_SLOT; }
const char* typeString() const;
#if defined(DEBUG) || defined(JS_JITSPEW)
void dump();
#endif /* defined(DEBUG) || defined(JS_JITSPEW) */
};
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
class DisposableEnvironmentObject : public EnvironmentObject {
protected:
static constexpr uint32_t DISPOSABLE_RESOURCE_STACK_SLOT = 1;
public:
static constexpr uint32_t RESERVED_SLOTS = 2;
ArrayObject* getOrCreateDisposeCapability(JSContext* cx);
// Used to get the Disposable objects within the
// lexical scope, it returns a ArrayObject if there
// is a non empty list of Disposables, else
// UndefinedValue.
JS::Value getDisposables();
void clearDisposables();
// For JITs
static size_t offsetOfDisposeCapability() {
return getFixedSlotOffset(DISPOSABLE_RESOURCE_STACK_SLOT);
}
};
#endif
class CallObject : public EnvironmentObject {
protected:
static constexpr uint32_t CALLEE_SLOT = 1;
static CallObject* create(JSContext* cx, HandleScript script,
HandleObject enclosing, gc::Heap heap);
public:
static const JSClass class_;
static constexpr uint32_t RESERVED_SLOTS = 2;
static constexpr ObjectFlags OBJECT_FLAGS = {ObjectFlag::QualifiedVarObj};
/* These functions are internal and are exposed only for JITs. */
/*
* Construct a bare-bones call object given a shape.
* The call object must be further initialized to be usable.
*/
static CallObject* createWithShape(JSContext* cx, Handle<SharedShape*> shape);
static CallObject* createTemplateObject(JSContext* cx, HandleScript script,
HandleObject enclosing);
static CallObject* create(JSContext* cx, AbstractFramePtr frame);
static CallObject* createHollowForDebug(JSContext* cx, HandleFunction callee);
// If `env` or any enclosing environment is a CallObject, return that
// CallObject; else null.
//
// `env` may be a DebugEnvironmentProxy, but not a hollow environment.
static CallObject* find(JSObject* env);
/*
* When an aliased formal (var accessed by nested closures) is also
* aliased by the arguments object, it must of course exist in one
* canonical location and that location is always the CallObject. For this
* to work, the ArgumentsObject stores special MagicValue in its array for
* forwarded-to-CallObject variables. This MagicValue's payload is the
* slot of the CallObject to access.
*/
const Value& aliasedFormalFromArguments(const Value& argsValue) {
return getSlot(ArgumentsObject::SlotFromMagicScopeSlotValue(argsValue));
}
inline void setAliasedFormalFromArguments(const Value& argsValue,
const Value& v);
JSFunction& callee() const {
return getReservedSlot(CALLEE_SLOT).toObject().as<JSFunction>();
}
/* For jit access. */
static size_t offsetOfCallee() { return getFixedSlotOffset(CALLEE_SLOT); }
static size_t calleeSlot() { return CALLEE_SLOT; }
};
class VarEnvironmentObject : public EnvironmentObject {
static constexpr uint32_t SCOPE_SLOT = 1;
static VarEnvironmentObject* createInternal(JSContext* cx,
Handle<SharedShape*> shape,
HandleObject enclosing,
gc::Heap heap);
static VarEnvironmentObject* create(JSContext* cx, Handle<Scope*> scope,
HandleObject enclosing, gc::Heap heap);
void initScope(Scope* scope) {
initReservedSlot(SCOPE_SLOT, PrivateGCThingValue(scope));
}
public:
static const JSClass class_;
static constexpr uint32_t RESERVED_SLOTS = 2;
static constexpr ObjectFlags OBJECT_FLAGS = {ObjectFlag::QualifiedVarObj};
static VarEnvironmentObject* createForFrame(JSContext* cx,
Handle<Scope*> scope,
AbstractFramePtr frame);
static VarEnvironmentObject* createHollowForDebug(JSContext* cx,
Handle<Scope*> scope);
static VarEnvironmentObject* createTemplateObject(JSContext* cx,
Handle<VarScope*> scope);
static VarEnvironmentObject* createWithoutEnclosing(JSContext* cx,
Handle<VarScope*> scope);
Scope& scope() const {
Value v = getReservedSlot(SCOPE_SLOT);
MOZ_ASSERT(v.isPrivateGCThing());
Scope& s = *static_cast<Scope*>(v.toGCThing());
MOZ_ASSERT(s.is<VarScope>() || s.is<EvalScope>());
return s;
}
bool isForEval() const { return scope().is<EvalScope>(); }
bool isForNonStrictEval() const { return scope().kind() == ScopeKind::Eval; }
};
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
class ModuleEnvironmentObject : public DisposableEnvironmentObject {
#else
class ModuleEnvironmentObject : public EnvironmentObject {
#endif
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
static constexpr uint32_t MODULE_SLOT =
DisposableEnvironmentObject::RESERVED_SLOTS;
#else
static constexpr uint32_t MODULE_SLOT = 1;
#endif
static const ObjectOps objectOps_;
static const JSClassOps classOps_;
public:
using EnvironmentObject::setAliasedBinding;
static const JSClass class_;
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
// While there are only 3 reserved slots, this needs to be set to 4, given
// there are some code expect the number of fixed slot to be same as the
// number of reserved slots for the lexical environments (bug 1913864).
static constexpr uint32_t RESERVED_SLOTS =
DisposableEnvironmentObject::RESERVED_SLOTS + 2;
#else
static constexpr uint32_t RESERVED_SLOTS = 2;
#endif
static constexpr ObjectFlags OBJECT_FLAGS = {ObjectFlag::NotExtensible,
ObjectFlag::QualifiedVarObj};
static ModuleEnvironmentObject* create(JSContext* cx,
Handle<ModuleObject*> module);
static ModuleEnvironmentObject* createSynthetic(JSContext* cx,
Handle<ModuleObject*> module);
ModuleObject& module() const;
IndirectBindingMap& importBindings() const;
bool createImportBinding(JSContext* cx, Handle<JSAtom*> importName,
Handle<ModuleObject*> module,
Handle<JSAtom*> exportName);
bool hasImportBinding(Handle<PropertyName*> name);
bool lookupImport(jsid name, ModuleEnvironmentObject** envOut,
mozilla::Maybe<PropertyInfo>* propOut);
// If `env` or any enclosing environment is a ModuleEnvironmentObject,
// return that ModuleEnvironmentObject; else null.
//
// `env` may be a DebugEnvironmentProxy, but not a hollow environment.
static ModuleEnvironmentObject* find(JSObject* env);
uint32_t firstSyntheticValueSlot() { return RESERVED_SLOTS; }
private:
static bool lookupProperty(JSContext* cx, HandleObject obj, HandleId id,
MutableHandleObject objp, PropertyResult* propp);
static bool hasProperty(JSContext* cx, HandleObject obj, HandleId id,
bool* foundp);
static bool getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
HandleId id, MutableHandleValue vp);
static bool setProperty(JSContext* cx, HandleObject obj, HandleId id,
HandleValue v, HandleValue receiver,
JS::ObjectOpResult& result);
static bool getOwnPropertyDescriptor(
JSContext* cx, HandleObject obj, HandleId id,
MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc);
static bool deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
ObjectOpResult& result);
static bool newEnumerate(JSContext* cx, HandleObject obj,
MutableHandleIdVector properties,
bool enumerableOnly);
};
class WasmInstanceEnvironmentObject : public EnvironmentObject {
// Currently WasmInstanceScopes do not use their scopes in a
// meaningful way. However, it is an invariant of DebugEnvironments that
// environments kept in those maps have live scopes, thus this strong
// reference.
static constexpr uint32_t SCOPE_SLOT = 1;
public:
static const JSClass class_;
static constexpr uint32_t RESERVED_SLOTS = 2;
static constexpr ObjectFlags OBJECT_FLAGS = {ObjectFlag::NotExtensible};
static WasmInstanceEnvironmentObject* createHollowForDebug(
JSContext* cx, Handle<WasmInstanceScope*> scope);
WasmInstanceScope& scope() const {
Value v = getReservedSlot(SCOPE_SLOT);
MOZ_ASSERT(v.isPrivateGCThing());
return *static_cast<WasmInstanceScope*>(v.toGCThing());
}
};
class WasmFunctionCallObject : public EnvironmentObject {
// Currently WasmFunctionCallObjects do not use their scopes in a
// meaningful way. However, it is an invariant of DebugEnvironments that
// environments kept in those maps have live scopes, thus this strong
// reference.
static constexpr uint32_t SCOPE_SLOT = 1;
public:
static const JSClass class_;
// TODO Check what Debugger behavior should be when it evaluates a
// var declaration.
static constexpr uint32_t RESERVED_SLOTS = 2;
static constexpr ObjectFlags OBJECT_FLAGS = {ObjectFlag::NotExtensible};
static WasmFunctionCallObject* createHollowForDebug(
JSContext* cx, HandleObject enclosing, Handle<WasmFunctionScope*> scope);
WasmFunctionScope& scope() const {
Value v = getReservedSlot(SCOPE_SLOT);
MOZ_ASSERT(v.isPrivateGCThing());
return *static_cast<WasmFunctionScope*>(v.toGCThing());
}
};
// Abstract base class for environments that can contain let/const bindings,
// plus a few other kinds of environments, such as `catch` blocks, that have
// similar behavior.
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
class LexicalEnvironmentObject : public DisposableEnvironmentObject {
#else
class LexicalEnvironmentObject : public EnvironmentObject {
#endif
protected:
// Global and non-syntactic lexical environments need to store a 'this'
// object and all other lexical environments have a fixed shape and store a
// backpointer to the LexicalScope.
//
// Since the two sets are disjoint, we only use one slot to save space.
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
static constexpr uint32_t THIS_VALUE_OR_SCOPE_SLOT =
DisposableEnvironmentObject::RESERVED_SLOTS;
#else
static constexpr uint32_t THIS_VALUE_OR_SCOPE_SLOT = 1;
#endif
public:
static const JSClass class_;
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
// See comment on RESERVED_SLOTS in ModuleEnvironmentObject.
static constexpr uint32_t RESERVED_SLOTS =
DisposableEnvironmentObject::RESERVED_SLOTS + 2;
#else
static constexpr uint32_t RESERVED_SLOTS = 2;
#endif
protected:
static LexicalEnvironmentObject* create(JSContext* cx,
Handle<SharedShape*> shape,
HandleObject enclosing,
gc::Heap heap);
public:
// Is this the global lexical scope?
bool isGlobal() const { return enclosingEnvironment().is<GlobalObject>(); }
// Global and non-syntactic lexical scopes are extensible. All other
// lexical scopes are not.
bool isExtensible() const;
// Is this a syntactic (i.e. corresponds to a source text) lexical
// environment?
bool isSyntactic() const { return !isExtensible() || isGlobal(); }
};
// A non-extensible lexical environment.
//
// Used for blocks (ScopeKind::Lexical) and several other scope kinds,
// including Catch, NamedLambda, FunctionLexical, and ClassBody.
class ScopedLexicalEnvironmentObject : public LexicalEnvironmentObject {
public:
static constexpr ObjectFlags OBJECT_FLAGS = {ObjectFlag::NotExtensible};
Scope& scope() const {
Value v = getReservedSlot(THIS_VALUE_OR_SCOPE_SLOT);
MOZ_ASSERT(!isExtensible() && v.isPrivateGCThing());
return *static_cast<Scope*>(v.toGCThing());
}
bool isClassBody() const { return scope().kind() == ScopeKind::ClassBody; }
void initScope(Scope* scope) {
initReservedSlot(THIS_VALUE_OR_SCOPE_SLOT, PrivateGCThingValue(scope));
}
};
class BlockLexicalEnvironmentObject : public ScopedLexicalEnvironmentObject {
protected:
static BlockLexicalEnvironmentObject* create(JSContext* cx,
Handle<LexicalScope*> scope,
HandleObject enclosing,
gc::Heap heap);
public:
static constexpr ObjectFlags OBJECT_FLAGS = {ObjectFlag::NotExtensible};
static BlockLexicalEnvironmentObject* createForFrame(
JSContext* cx, Handle<LexicalScope*> scope, AbstractFramePtr frame);
static BlockLexicalEnvironmentObject* createHollowForDebug(
JSContext* cx, Handle<LexicalScope*> scope);
static BlockLexicalEnvironmentObject* createTemplateObject(
JSContext* cx, Handle<LexicalScope*> scope);
static BlockLexicalEnvironmentObject* createWithoutEnclosing(
JSContext* cx, Handle<LexicalScope*> scope);
// Create a new BlockLexicalEnvironmentObject with the same enclosing env and
// variable values as this.
static BlockLexicalEnvironmentObject* clone(
JSContext* cx, Handle<BlockLexicalEnvironmentObject*> env);
// Create a new BlockLexicalEnvironmentObject with the same enclosing env as
// this, with all variables uninitialized.
static BlockLexicalEnvironmentObject* recreate(
JSContext* cx, Handle<BlockLexicalEnvironmentObject*> env);
// The LexicalScope that created this environment.
LexicalScope& scope() const {
return ScopedLexicalEnvironmentObject::scope().as<LexicalScope>();
}
};
class NamedLambdaObject : public BlockLexicalEnvironmentObject {
static NamedLambdaObject* create(JSContext* cx, HandleFunction callee,
HandleObject enclosing, gc::Heap heap);
public:
static NamedLambdaObject* createTemplateObject(JSContext* cx,
HandleFunction callee);
static NamedLambdaObject* createWithoutEnclosing(JSContext* cx,
HandleFunction callee);
static NamedLambdaObject* create(JSContext* cx, AbstractFramePtr frame);
// For JITs.
static size_t lambdaSlot();
static size_t offsetOfLambdaSlot() {
return getFixedSlotOffset(lambdaSlot());
}
};
class ClassBodyLexicalEnvironmentObject
: public ScopedLexicalEnvironmentObject {
static ClassBodyLexicalEnvironmentObject* create(
JSContext* cx, Handle<ClassBodyScope*> scope, HandleObject enclosing,
gc::Heap heap);
public:
static ClassBodyLexicalEnvironmentObject* createForFrame(
JSContext* cx, Handle<ClassBodyScope*> scope, AbstractFramePtr frame);
static ClassBodyLexicalEnvironmentObject* createTemplateObject(
JSContext* cx, Handle<ClassBodyScope*> scope);
static ClassBodyLexicalEnvironmentObject* createWithoutEnclosing(
JSContext* cx, Handle<ClassBodyScope*> scope);
// The ClassBodyScope that created this environment.
ClassBodyScope& scope() const {
return ScopedLexicalEnvironmentObject::scope().as<ClassBodyScope>();
}
static uint32_t privateBrandSlot() { return JSSLOT_FREE(&class_); }
};
/*
* Prepare a |this| object to be returned to script. This includes replacing
* Windows with their corresponding WindowProxy.
*/
JSObject* GetThisObject(JSObject* obj);
// Global and non-syntactic lexical environments are extensible.
class ExtensibleLexicalEnvironmentObject : public LexicalEnvironmentObject {
public:
JSObject* thisObject() const;
// For a given global object or JSMEnvironment `obj`, return the associated
// global lexical or non-syntactic lexical environment, where top-level `let`
// bindings are added.
static ExtensibleLexicalEnvironmentObject* forVarEnvironment(JSObject* obj);
protected:
void initThisObject(JSObject* obj) {
MOZ_ASSERT(isGlobal() || !isSyntactic());
JSObject* thisObj = GetThisObject(obj);
initReservedSlot(THIS_VALUE_OR_SCOPE_SLOT, ObjectValue(*thisObj));
}
};
// The global lexical environment, where global let/const/class bindings are
// added.
class GlobalLexicalEnvironmentObject
: public ExtensibleLexicalEnvironmentObject {
public:
static GlobalLexicalEnvironmentObject* create(JSContext* cx,
Handle<GlobalObject*> global);
GlobalObject& global() const {
return enclosingEnvironment().as<GlobalObject>();
}
void setWindowProxyThisObject(JSObject* obj);
static constexpr size_t offsetOfThisValueSlot() {
return getFixedSlotOffset(THIS_VALUE_OR_SCOPE_SLOT);
}
};
// Non-standard. See "Non-syntactic Environments" above.
class NonSyntacticLexicalEnvironmentObject
: public ExtensibleLexicalEnvironmentObject {
public:
static NonSyntacticLexicalEnvironmentObject* create(JSContext* cx,
HandleObject enclosing,
HandleObject thisv);
};
// A non-syntactic dynamic scope object that captures non-lexical
// bindings. That is, a scope object that captures both qualified var
// assignments and unqualified bareword assignments. Its parent is always the
// global lexical environment.
//
// See the long "Non-syntactic Environments" comment above.
class NonSyntacticVariablesObject : public EnvironmentObject {
public:
static const JSClass class_;
static constexpr uint32_t RESERVED_SLOTS = 1;
static constexpr ObjectFlags OBJECT_FLAGS = {ObjectFlag::QualifiedVarObj};
static NonSyntacticVariablesObject* create(JSContext* cx);
};
NonSyntacticLexicalEnvironmentObject* CreateNonSyntacticEnvironmentChain(
JSContext* cx, JS::HandleObjectVector envChain);
// With environment objects on the run-time environment chain.
class WithEnvironmentObject : public EnvironmentObject {
static constexpr uint32_t OBJECT_SLOT = 1;
static constexpr uint32_t THIS_SLOT = 2;
static constexpr uint32_t SCOPE_SLOT = 3;
public:
static const JSClass class_;
static constexpr uint32_t RESERVED_SLOTS = 4;
static constexpr ObjectFlags OBJECT_FLAGS = {};
static WithEnvironmentObject* create(JSContext* cx, HandleObject object,
HandleObject enclosing,
Handle<WithScope*> scope);
static WithEnvironmentObject* createNonSyntactic(JSContext* cx,
HandleObject object,
HandleObject enclosing);
/* Return the 'o' in 'with (o)'. */
JSObject& object() const;
/* Return object for GetThisValue. */
JSObject* withThis() const;
/*
* Return whether this object is a syntactic with object. If not, this is
* a With object we inserted between the outermost syntactic scope and the
* global object to wrap the environment chain someone explicitly passed
* via JSAPI to CompileFunction or script evaluation.
*/
bool isSyntactic() const;
// For syntactic with environment objects, the with scope.
WithScope& scope() const;
static constexpr size_t objectSlot() { return OBJECT_SLOT; }
static constexpr size_t thisSlot() { return THIS_SLOT; }
// For JITs.
static constexpr size_t offsetOfThisSlot() {
return getFixedSlotOffset(THIS_SLOT);
}
};
// Internal environment object used by JSOp::BindUnqualifiedName upon
// encountering an uninitialized lexical slot or an assignment to a 'const'
// binding.
//
// ES6 lexical bindings cannot be accessed in any way (throwing
// ReferenceErrors) until initialized. Normally, NAME operations
// unconditionally check for uninitialized lexical slots. When getting or
// looking up names, this can be done without slowing down normal operations
// on the return value. When setting names, however, we do not want to pollute
// all set-property paths with uninitialized lexical checks. For setting names
// (i.e. JSOp::SetName), we emit an accompanying, preceding
// JSOp::BindUnqualifiedName which finds the right scope on which to set the
// name. Moreover, when the name on the scope is an uninitialized lexical, we
// cannot throw eagerly, as the spec demands that the error be thrown after
// evaluating the RHS of assignments. Instead, this sentinel scope object is
// pushed on the stack. Attempting to access anything on this scope throws the
// appropriate ReferenceError.
//
// ES6 'const' bindings induce a runtime error when assigned to outside
// of initialization, regardless of strictness.
class RuntimeLexicalErrorObject : public EnvironmentObject {
static const unsigned ERROR_SLOT = 1;
public:
static const unsigned RESERVED_SLOTS = 2;
static const JSClass class_;
static RuntimeLexicalErrorObject* create(JSContext* cx,
HandleObject enclosing,
unsigned errorNumber);
unsigned errorNumber() { return getReservedSlot(ERROR_SLOT).toInt32(); }
};
/****************************************************************************/
// A environment iterator describes the active environments starting from an
// environment, scope pair. This pair may be derived from the current point of
// execution in a frame. If derived in such a fashion, the EnvironmentIter
// tracks whether the current scope is within the extent of this initial
// frame. Here, "frame" means a single activation of: a function, eval, or
// global code.
class MOZ_RAII EnvironmentIter {
Rooted<ScopeIter> si_;
RootedObject env_;
AbstractFramePtr frame_;
void incrementScopeIter();
void settle();
// No value semantics.
EnvironmentIter(const EnvironmentIter& ei) = delete;
public:
// Constructing from a copy of an existing EnvironmentIter.
EnvironmentIter(JSContext* cx, const EnvironmentIter& ei);
// Constructing from an environment, scope pair. All environments
// considered not to be withinInitialFrame, since no frame is given.
EnvironmentIter(JSContext* cx, JSObject* env, Scope* scope);
// Constructing from a frame. Places the EnvironmentIter on the innermost
// environment at pc.
EnvironmentIter(JSContext* cx, AbstractFramePtr frame, const jsbytecode* pc);
// Constructing from an environment, scope and frame. The frame is given
// to initialize to proper enclosing environment/scope.
EnvironmentIter(JSContext* cx, JSObject* env, Scope* scope,
AbstractFramePtr frame);
bool done() const { return si_.done(); }
explicit operator bool() const { return !done(); }
void operator++(int) {
if (hasAnyEnvironmentObject()) {
env_ = &env_->as<EnvironmentObject>().enclosingEnvironment();
}
incrementScopeIter();
settle();
}
EnvironmentIter& operator++() {
operator++(1);
return *this;
}
// If done():
JSObject& enclosingEnvironment() const;
// If !done():
bool hasNonSyntacticEnvironmentObject() const;
bool hasSyntacticEnvironment() const { return si_.hasSyntacticEnvironment(); }
bool hasAnyEnvironmentObject() const {
return hasNonSyntacticEnvironmentObject() || hasSyntacticEnvironment();
}
EnvironmentObject& environment() const {
MOZ_ASSERT(hasAnyEnvironmentObject());
return env_->as<EnvironmentObject>();
}
Scope& scope() const { return *si_.scope(); }
Scope* maybeScope() const {
if (si_) {
return si_.scope();
}
return nullptr;
}
JSFunction& callee() const { return env_->as<CallObject>().callee(); }
bool withinInitialFrame() const { return !!frame_; }
AbstractFramePtr initialFrame() const {
MOZ_ASSERT(withinInitialFrame());
return frame_;
}
AbstractFramePtr maybeInitialFrame() const { return frame_; }
};
// The key in MissingEnvironmentMap. For live frames, maps live frames to
// their synthesized environments. For completely optimized-out environments,
// maps the Scope to their synthesized environments. The env we synthesize for
// Scopes are read-only, and we never use their parent links, so they don't
// need to be distinct.
//
// That is, completely optimized out environments can't be distinguished by
// frame. Note that even if the frame corresponding to the Scope is live on
// the stack, it is unsound to synthesize an environment from that live
// frame. In other words, the provenance of the environment chain is from
// allocated closures (i.e., allocation sites) and is irrecoverable from
// simple stack inspection (i.e., call sites).
class MissingEnvironmentKey {
friend class LiveEnvironmentVal;
AbstractFramePtr frame_;
Scope* scope_;
public:
explicit MissingEnvironmentKey(const EnvironmentIter& ei)
: frame_(ei.maybeInitialFrame()), scope_(ei.maybeScope()) {}
MissingEnvironmentKey(AbstractFramePtr frame, Scope* scope)
: frame_(frame), scope_(scope) {}
AbstractFramePtr frame() const { return frame_; }
Scope* scope() const { return scope_; }
void updateScope(Scope* scope) { scope_ = scope; }
void updateFrame(AbstractFramePtr frame) { frame_ = frame; }
// For use as hash policy.
using Lookup = MissingEnvironmentKey;
static HashNumber hash(MissingEnvironmentKey sk);
static bool match(MissingEnvironmentKey sk1, MissingEnvironmentKey sk2);
bool operator!=(const MissingEnvironmentKey& other) const {
return frame_ != other.frame_ || scope_ != other.scope_;
}
static void rekey(MissingEnvironmentKey& k,
const MissingEnvironmentKey& newKey) {
k = newKey;
}
};
// The value in LiveEnvironmentMap, mapped from by live environment objects.
class LiveEnvironmentVal {
friend class DebugEnvironments;
friend class MissingEnvironmentKey;
AbstractFramePtr frame_;
HeapPtr<Scope*> scope_;
static void staticAsserts();
public:
explicit LiveEnvironmentVal(const EnvironmentIter& ei)
: frame_(ei.initialFrame()), scope_(ei.maybeScope()) {}
AbstractFramePtr frame() const { return frame_; }
void updateFrame(AbstractFramePtr frame) { frame_ = frame; }
bool traceWeak(JSTracer* trc);
};
/****************************************************************************/
/*
* [SMDOC] Debug environment objects
*
* The frontend optimizes unaliased variables into stack slots and can optimize
* away whole EnvironmentObjects. So when the debugger wants to perform an
* unexpected eval-in-frame (or otherwise access the environment),
* `fp->environmentChain` is often incomplete. This is a problem: a major use
* case for eval-in-frame is to access the local variables in debuggee code.
*
* Even when all EnvironmentObjects exist, giving complete information for all
* bindings, stack and heap, there's another issue: eval-in-frame code can
* create closures that capture stack locals. The variable slots go away when
* the frame is popped, but the closure, which uses them, may survive.
*
* To solve both problems, eval-in-frame code is compiled and run against a
* "debug environment chain" of DebugEnvironmentProxy objects rather than real
* EnvironmentObjects. The `GetDebugEnvironmentFor` functions below create
* these proxies, one to sit in front of each existing EnvironmentObject. They
* also create bogus "hollow" EnvironmentObjects to stand in for environments
* that were optimized away; and proxies for those. The frontend sees these
* environments as something like `with` scopes, and emits deoptimized bytecode
* instructions for all variable accesses.
*
* When eval-in-frame code runs, `fp->environmentChain` points to this chain of
* proxies. On each variable access, the proxy laboriously figures out what to
* do. See e.g. `DebuggerEnvironmentProxyHandler::handleUnaliasedAccess`.
*
* There's a limit to what the proxies can manage, since they're proxying
* environments that are already optimized. Some debugger operations, like
* redefining a lexical binding, can fail when a true direct eval would
* succeed. Even plain variable accesses can throw, if the variable has been
* optimized away.
*
* To support accessing stack variables after they've gone out of scope, we
* copy the variables to the heap as they leave scope. See
* `DebugEnvironments::onPopCall` and `onPopLexical`.
*
* `GetDebugEnvironmentFor*` guarantees that the same DebugEnvironmentProxy is
* always produced for the same underlying environment (optimized or not!).
* This is maintained by some bookkeeping information stored in
* `DebugEnvironments`.
*/
extern JSObject* GetDebugEnvironmentForFunction(JSContext* cx,
HandleFunction fun);
extern JSObject* GetDebugEnvironmentForSuspendedGenerator(
JSContext* cx, JSScript* script, AbstractGeneratorObject& genObj);
extern JSObject* GetDebugEnvironmentForFrame(JSContext* cx,
AbstractFramePtr frame,
jsbytecode* pc);
extern JSObject* GetDebugEnvironmentForGlobalLexicalEnvironment(JSContext* cx);
extern Scope* GetEnvironmentScope(const JSObject& env);
/* Provides debugger access to a environment. */
class DebugEnvironmentProxy : public ProxyObject {
/*
* The enclosing environment on the dynamic environment chain. This slot is
* analogous to the ENCLOSING_ENV_SLOT of a EnvironmentObject.
*/
static const unsigned ENCLOSING_SLOT = 0;
/*
* NullValue or a dense array holding the unaliased variables of a function
* frame that has been popped.
*/
static const unsigned SNAPSHOT_SLOT = 1;
public:
static DebugEnvironmentProxy* create(JSContext* cx, EnvironmentObject& env,
HandleObject enclosing);
// NOTE: The environment may be a debug hollow with invalid
// enclosingEnvironment. Always use the enclosingEnvironment accessor on
// the DebugEnvironmentProxy in order to walk the environment chain.
EnvironmentObject& environment() const;
JSObject& enclosingEnvironment() const;
// May only be called for proxies to function call objects or modules
// with top-level-await.
ArrayObject* maybeSnapshot() const;
void initSnapshot(ArrayObject& snapshot);
// Currently, the 'declarative' environments are function, module, and
// lexical environments.
bool isForDeclarative() const;
// Get a property by 'id', but returns sentinel values instead of throwing
// on exceptional cases.
static bool getMaybeSentinelValue(JSContext* cx,
Handle<DebugEnvironmentProxy*> env,
HandleId id, MutableHandleValue vp);
// Returns true iff this is a function environment with its own this-binding
// (all functions except arrow functions).
bool isFunctionEnvironmentWithThis();
// Does this debug environment not have a real counterpart or was never
// live (and thus does not have a synthesized EnvironmentObject or a
// snapshot)?
bool isOptimizedOut() const;
#if defined(DEBUG) || defined(JS_JITSPEW)
void dump();
#endif /* defined(DEBUG) || defined(JS_JITSPEW) */
};
/* Maintains per-realm debug environment bookkeeping information. */
class DebugEnvironments {
Zone* zone_;
/* The map from (non-debug) environments to debug environments. */
ObjectWeakMap proxiedEnvs;
/*
* The map from live frames which have optimized-away environments to the
* corresponding debug environments.
*/
using MissingEnvironmentMap =
HashMap<MissingEnvironmentKey, WeakHeapPtr<DebugEnvironmentProxy*>,
MissingEnvironmentKey, ZoneAllocPolicy>;
MissingEnvironmentMap missingEnvs;
/*
* The map from environment objects of live frames to the live frame. This
* map updated lazily whenever the debugger needs the information. In
* between two lazy updates, liveEnvs becomes incomplete (but not invalid,
* onPop* removes environments as they are popped). Thus, two consecutive
* debugger lazy updates of liveEnvs need only fill in the new
* environments.
*/
using LiveEnvironmentMap =
GCHashMap<WeakHeapPtr<JSObject*>, LiveEnvironmentVal,
StableCellHasher<WeakHeapPtr<JSObject*>>, ZoneAllocPolicy>;
LiveEnvironmentMap liveEnvs;
public:
DebugEnvironments(JSContext* cx, Zone* zone);
~DebugEnvironments();
Zone* zone() const { return zone_; }
private:
static DebugEnvironments* ensureRealmData(JSContext* cx);
template <typename Environment, typename Scope>
static void onPopGeneric(JSContext* cx, const EnvironmentIter& ei);
public:
void trace(JSTracer* trc);
void traceWeak(JSTracer* trc);
void finish();
#ifdef JS_GC_ZEAL
void checkHashTablesAfterMovingGC();
#endif
// If a live frame has a synthesized entry in missingEnvs, make sure it's not
// collected.
void traceLiveFrame(JSTracer* trc, AbstractFramePtr frame);
static DebugEnvironmentProxy* hasDebugEnvironment(JSContext* cx,
EnvironmentObject& env);
static bool addDebugEnvironment(JSContext* cx, Handle<EnvironmentObject*> env,
Handle<DebugEnvironmentProxy*> debugEnv);
static DebugEnvironmentProxy* hasDebugEnvironment(JSContext* cx,
const EnvironmentIter& ei);
static bool addDebugEnvironment(JSContext* cx, const EnvironmentIter& ei,
Handle<DebugEnvironmentProxy*> debugEnv);
static bool updateLiveEnvironments(JSContext* cx);
static LiveEnvironmentVal* hasLiveEnvironment(EnvironmentObject& env);
static void unsetPrevUpToDateUntil(JSContext* cx, AbstractFramePtr frame);
// When a frame bails out from Ion to Baseline, there might be missing
// envs keyed on, and live envs containing, the old
// RematerializedFrame. Forward those values to the new BaselineFrame.
static void forwardLiveFrame(JSContext* cx, AbstractFramePtr from,
AbstractFramePtr to);
// When an environment is popped, we store a snapshot of its bindings that
// live on the frame.
//
// This is done during frame unwinding, which cannot handle errors
// gracefully. Errors result in no snapshot being set on the
// DebugEnvironmentProxy.
static void takeFrameSnapshot(JSContext* cx,
Handle<DebugEnvironmentProxy*> debugEnv,
AbstractFramePtr frame);
// In debug-mode, these must be called whenever exiting a scope that might
// have stack-allocated locals.
static void onPopCall(JSContext* cx, AbstractFramePtr frame);
static void onPopVar(JSContext* cx, const EnvironmentIter& ei);
static void onPopLexical(JSContext* cx, const EnvironmentIter& ei);
static void onPopLexical(JSContext* cx, AbstractFramePtr frame,
const jsbytecode* pc);
static void onPopWith(AbstractFramePtr frame);
static void onPopModule(JSContext* cx, const EnvironmentIter& ei);
static void onRealmUnsetIsDebuggee(Realm* realm);
};
} /* namespace js */
template <>
inline bool JSObject::is<js::EnvironmentObject>() const {
return is<js::CallObject>() || is<js::VarEnvironmentObject>() ||
is<js::ModuleEnvironmentObject>() ||
is<js::WasmInstanceEnvironmentObject>() ||
is<js::WasmFunctionCallObject>() ||
is<js::LexicalEnvironmentObject>() ||
is<js::WithEnvironmentObject>() ||
is<js::NonSyntacticVariablesObject>() ||
is<js::RuntimeLexicalErrorObject>();
}
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
template <>
inline bool JSObject::is<js::DisposableEnvironmentObject>() const {
return is<js::LexicalEnvironmentObject>() ||
is<js::ModuleEnvironmentObject>();
}
#endif
template <>
inline bool JSObject::is<js::ScopedLexicalEnvironmentObject>() const {
return is<js::LexicalEnvironmentObject>() &&
!as<js::LexicalEnvironmentObject>().isExtensible();
}
template <>
inline bool JSObject::is<js::BlockLexicalEnvironmentObject>() const {
return is<js::ScopedLexicalEnvironmentObject>() &&
!as<js::ScopedLexicalEnvironmentObject>().isClassBody();
}
template <>
inline bool JSObject::is<js::ClassBodyLexicalEnvironmentObject>() const {
return is<js::ScopedLexicalEnvironmentObject>() &&
as<js::ScopedLexicalEnvironmentObject>().isClassBody();
}
template <>
inline bool JSObject::is<js::ExtensibleLexicalEnvironmentObject>() const {
return is<js::LexicalEnvironmentObject>() &&
as<js::LexicalEnvironmentObject>().isExtensible();
}
template <>
inline bool JSObject::is<js::GlobalLexicalEnvironmentObject>() const {
return is<js::LexicalEnvironmentObject>() &&
as<js::LexicalEnvironmentObject>().isGlobal();
}
template <>
inline bool JSObject::is<js::NonSyntacticLexicalEnvironmentObject>() const {
return is<js::LexicalEnvironmentObject>() &&
!as<js::LexicalEnvironmentObject>().isSyntactic();
}
template <>
inline bool JSObject::is<js::NamedLambdaObject>() const {
return is<js::BlockLexicalEnvironmentObject>() &&
as<js::BlockLexicalEnvironmentObject>().scope().isNamedLambda();
}
template <>
bool JSObject::is<js::DebugEnvironmentProxy>() const;
namespace js {
inline bool IsSyntacticEnvironment(JSObject* env) {
if (!env->is<EnvironmentObject>()) {
return false;
}
if (env->is<WithEnvironmentObject>()) {
return env->as<WithEnvironmentObject>().isSyntactic();
}
if (env->is<LexicalEnvironmentObject>()) {
return env->as<LexicalEnvironmentObject>().isSyntactic();
}
if (env->is<NonSyntacticVariablesObject>()) {
return false;
}
return true;
}
inline JSObject* MaybeUnwrapWithEnvironment(JSObject* env) {
if (env->is<WithEnvironmentObject>()) {
return &env->as<WithEnvironmentObject>().object();
}
return env;
}
template <typename SpecificEnvironment>
inline bool IsFrameInitialEnvironment(AbstractFramePtr frame,
SpecificEnvironment& env) {
// A frame's initial environment is the innermost environment
// corresponding to the scope chain from frame.script()->bodyScope() to
// frame.script()->outermostScope(). This environment must be on the chain
// for the frame to be considered initialized. That is, it must be on the
// chain for the environment chain to fully match the scope chain at the
// start of execution in the frame.
//
// This logic must be in sync with the HAS_INITIAL_ENV logic in
// BaselineStackBuilder::buildBaselineFrame.
// A function frame's CallObject, if present, is always the initial
// environment.
if constexpr (std::is_same_v<SpecificEnvironment, CallObject>) {
return true;
}
// For an eval frame, the VarEnvironmentObject, if present, is always the
// initial environment.
if constexpr (std::is_same_v<SpecificEnvironment, VarEnvironmentObject>) {
if (frame.isEvalFrame()) {
return true;
}
}
// For named lambda frames without CallObjects (i.e., no binding in the
// body of the function was closed over), the NamedLambdaObject
// corresponding to the named lambda scope is the initial environment.
if constexpr (std::is_same_v<SpecificEnvironment, NamedLambdaObject>) {
if (frame.isFunctionFrame() &&
frame.callee()->needsNamedLambdaEnvironment() &&
!frame.callee()->needsCallObject()) {
LexicalScope* namedLambdaScope = frame.script()->maybeNamedLambdaScope();
return &env.scope() == namedLambdaScope;
}
}
return false;
}
WithEnvironmentObject* CreateObjectsForEnvironmentChain(
JSContext* cx, HandleObjectVector chain, HandleObject terminatingEnv);
ModuleObject* GetModuleObjectForScript(JSScript* script);
ModuleEnvironmentObject* GetModuleEnvironmentForScript(JSScript* script);
[[nodiscard]] bool GetThisValueForDebuggerFrameMaybeOptimizedOut(
JSContext* cx, AbstractFramePtr frame, const jsbytecode* pc,
MutableHandleValue res);
[[nodiscard]] bool GetThisValueForDebuggerSuspendedGeneratorMaybeOptimizedOut(
JSContext* cx, AbstractGeneratorObject& genObj, JSScript* script,
MutableHandleValue res);
[[nodiscard]] bool GlobalOrEvalDeclInstantiation(JSContext* cx,
HandleObject envChain,
HandleScript script,
GCThingIndex lastFun);
[[nodiscard]] bool InitFunctionEnvironmentObjects(JSContext* cx,
AbstractFramePtr frame);
[[nodiscard]] bool PushVarEnvironmentObject(JSContext* cx, Handle<Scope*> scope,
AbstractFramePtr frame);
#ifdef DEBUG
bool AnalyzeEntrainedVariables(JSContext* cx, HandleScript script);
#endif
extern JSObject* MaybeOptimizeBindUnqualifiedGlobalName(
JSContext* cx, Handle<GlobalObject*> global, Handle<PropertyName*> name);
} // namespace js
#endif /* vm_EnvironmentObject_h */