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/. */
#include "vm/EnvironmentObject-inl.h"
#include "mozilla/Maybe.h"
#include "builtin/ModuleObject.h"
#include "js/Exception.h"
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
#include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit
#include "js/friend/WindowProxy.h" // js::IsWindow, js::IsWindowProxy
#include "js/PropertyAndElement.h" // JS_DefineProperty, JS_DefinePropertyById, JS_HasProperty, JS_HasPropertyById
#include "vm/ArgumentsObject.h"
#include "vm/BytecodeIterator.h"
#include "vm/BytecodeLocation.h"
#include "vm/GeneratorObject.h" // js::GetGeneratorObjectForEnvironment
#include "vm/GlobalObject.h"
#include "vm/JSObject.h"
#include "vm/ProxyObject.h"
#include "vm/Realm.h"
#include "vm/Scope.h"
#include "vm/Shape.h"
#include "wasm/WasmDebug.h"
#include "wasm/WasmDebugFrame.h"
#include "wasm/WasmInstance.h"
#include "gc/Marking-inl.h"
#include "gc/StableCellHasher-inl.h"
#include "vm/BytecodeIterator-inl.h"
#include "vm/NativeObject-inl.h"
#include "vm/Stack-inl.h"
using namespace js;
using RootedArgumentsObject = Rooted<ArgumentsObject*>;
using MutableHandleArgumentsObject = MutableHandle<ArgumentsObject*>;
/*****************************************************************************/
SharedShape* js::EnvironmentCoordinateToEnvironmentShape(JSScript* script,
jsbytecode* pc) {
MOZ_ASSERT(JOF_OPTYPE(JSOp(*pc)) == JOF_ENVCOORD);
ScopeIter si(script->innermostScope(pc));
uint32_t hops = EnvironmentCoordinate(pc).hops();
while (true) {
MOZ_ASSERT(!si.done());
if (si.hasSyntacticEnvironment()) {
if (!hops) {
break;
}
hops--;
}
si++;
}
return si.environmentShape();
}
PropertyName* js::EnvironmentCoordinateNameSlow(JSScript* script,
jsbytecode* pc) {
SharedShape* shape = EnvironmentCoordinateToEnvironmentShape(script, pc);
EnvironmentCoordinate ec(pc);
SharedShapePropertyIter<NoGC> iter(shape);
while (iter->slot() != ec.slot()) {
iter++;
}
jsid id = iter->key();
/* Beware nameless destructuring formal. */
if (!id.isAtom()) {
return script->runtimeFromAnyThread()->commonNames->empty;
}
return id.toAtom()->asPropertyName();
}
/*****************************************************************************/
template <typename T>
static T* CreateEnvironmentObject(JSContext* cx, Handle<SharedShape*> shape,
gc::InitialHeap heap) {
static_assert(std::is_base_of_v<EnvironmentObject, T>,
"T must be an EnvironmentObject");
// All environment objects can be background-finalized.
gc::AllocKind allocKind = gc::GetGCObjectKind(shape->numFixedSlots());
MOZ_ASSERT(CanChangeToBackgroundAllocKind(allocKind, &T::class_));
allocKind = gc::ForegroundToBackgroundAllocKind(allocKind);
JSObject* obj = NativeObject::create(cx, allocKind, heap, shape);
if (!obj) {
return nullptr;
}
return &obj->as<T>();
}
// Helper function for simple environment objects that don't need the overloads
// above.
template <typename T>
static T* CreateEnvironmentObject(JSContext* cx, Handle<SharedShape*> shape,
NewObjectKind newKind = GenericObject) {
gc::InitialHeap heap = GetInitialHeap(newKind, &T::class_);
return CreateEnvironmentObject<T>(cx, shape, heap);
}
CallObject* CallObject::createWithShape(JSContext* cx,
Handle<SharedShape*> shape) {
return CreateEnvironmentObject<CallObject>(cx, shape);
}
/*
* Create a CallObject for a JSScript that is not initialized to any particular
* callsite. This object can either be initialized (with an enclosing scope and
* callee) or used as a template for jit compilation.
*/
CallObject* CallObject::create(JSContext* cx, HandleScript script,
HandleObject enclosing, gc::InitialHeap heap) {
Rooted<SharedShape*> shape(
cx, script->bodyScope()->as<FunctionScope>().environmentShape());
MOZ_ASSERT(shape->getObjectClass() == &class_);
// The JITs assume the result is nursery allocated unless we collected the
// nursery, so don't change |heap| here.
auto* callObj = CreateEnvironmentObject<CallObject>(cx, shape, heap);
if (!callObj) {
return nullptr;
}
if (enclosing) {
callObj->initEnclosingEnvironment(enclosing);
}
return callObj;
}
CallObject* CallObject::createTemplateObject(JSContext* cx, HandleScript script,
HandleObject enclosing) {
return create(cx, script, enclosing, gc::TenuredHeap);
}
CallObject* CallObject::create(JSContext* cx, AbstractFramePtr frame) {
MOZ_ASSERT(frame.isFunctionFrame());
cx->check(frame);
RootedObject envChain(cx, frame.environmentChain());
RootedFunction callee(cx, frame.callee());
RootedScript script(cx, callee->nonLazyScript());
gc::InitialHeap heap = gc::DefaultHeap;
CallObject* callobj = create(cx, script, envChain, heap);
if (!callobj) {
return nullptr;
}
callobj->initFixedSlot(CALLEE_SLOT, ObjectValue(*callee));
return callobj;
}
template <class EnvT>
EnvT* FindEnclosingEnv(JSObject* env) {
for (;;) {
if (env->is<EnvT>()) {
break;
} else if (env->is<EnvironmentObject>()) {
env = &env->as<EnvironmentObject>().enclosingEnvironment();
} else if (env->is<DebugEnvironmentProxy>()) {
EnvironmentObject& unwrapped =
env->as<DebugEnvironmentProxy>().environment();
if (unwrapped.is<EnvT>()) {
env = &unwrapped;
break;
}
env = &env->as<DebugEnvironmentProxy>().enclosingEnvironment();
} else {
MOZ_ASSERT(env->is<GlobalObject>());
return nullptr;
}
}
return &env->as<EnvT>();
}
CallObject* CallObject::find(JSObject* env) {
return FindEnclosingEnv<CallObject>(env);
}
ModuleEnvironmentObject* ModuleEnvironmentObject::find(JSObject* env) {
return FindEnclosingEnv<ModuleEnvironmentObject>(env);
}
CallObject* CallObject::createHollowForDebug(JSContext* cx,
HandleFunction callee) {
MOZ_ASSERT(!callee->needsCallObject());
RootedScript script(cx, callee->nonLazyScript());
Rooted<FunctionScope*> scope(cx, &script->bodyScope()->as<FunctionScope>());
Rooted<SharedShape*> shape(cx, EmptyEnvironmentShape<CallObject>(cx));
if (!shape) {
return nullptr;
}
Rooted<CallObject*> callobj(cx, createWithShape(cx, shape));
if (!callobj) {
return nullptr;
}
// This environment's enclosing link is never used: the
// DebugEnvironmentProxy that refers to this scope carries its own
// enclosing link, which is what Debugger uses to construct the tree of
// Debugger.Environment objects.
callobj->initEnclosingEnvironment(&cx->global()->lexicalEnvironment());
callobj->initFixedSlot(CALLEE_SLOT, ObjectValue(*callee));
RootedValue optimizedOut(cx, MagicValue(JS_OPTIMIZED_OUT));
RootedId id(cx);
for (Rooted<BindingIter> bi(cx, BindingIter(script)); bi; bi++) {
id = NameToId(bi.name()->asPropertyName());
if (!SetProperty(cx, callobj, id, optimizedOut)) {
return nullptr;
}
}
return callobj;
}
const JSClass CallObject::class_ = {
"Call", JSCLASS_HAS_RESERVED_SLOTS(CallObject::RESERVED_SLOTS)};
/*****************************************************************************/
/* static */
VarEnvironmentObject* VarEnvironmentObject::createInternal(
JSContext* cx, Handle<SharedShape*> shape, HandleObject enclosing,
gc::InitialHeap heap) {
MOZ_ASSERT(shape->getObjectClass() == &class_);
auto* env = CreateEnvironmentObject<VarEnvironmentObject>(cx, shape, heap);
if (!env) {
return nullptr;
}
MOZ_ASSERT(!env->inDictionaryMode());
if (enclosing) {
env->initEnclosingEnvironment(enclosing);
}
return env;
}
/* static */
VarEnvironmentObject* VarEnvironmentObject::create(JSContext* cx,
Handle<Scope*> scope,
HandleObject enclosing,
gc::InitialHeap heap) {
MOZ_ASSERT(scope->is<EvalScope>() || scope->is<VarScope>());
Rooted<SharedShape*> shape(cx, scope->environmentShape());
auto* env = createInternal(cx, shape, enclosing, heap);
if (!env) {
return nullptr;
}
env->initScope(scope);
return env;
}
/* static */
VarEnvironmentObject* VarEnvironmentObject::createForFrame(
JSContext* cx, Handle<Scope*> scope, AbstractFramePtr frame) {
#ifdef DEBUG
if (frame.isEvalFrame()) {
MOZ_ASSERT(scope->is<EvalScope>() && scope == frame.script()->bodyScope());
MOZ_ASSERT_IF(frame.isInterpreterFrame(),
cx->interpreterFrame() == frame.asInterpreterFrame());
MOZ_ASSERT_IF(frame.isInterpreterFrame(),
cx->interpreterRegs().pc == frame.script()->code());
} else {
MOZ_ASSERT(frame.environmentChain());
MOZ_ASSERT_IF(
frame.callee()->needsCallObject(),
&frame.environmentChain()->as<CallObject>().callee() == frame.callee());
}
#endif
RootedObject envChain(cx, frame.environmentChain());
return create(cx, scope, envChain, gc::DefaultHeap);
}
/* static */
VarEnvironmentObject* VarEnvironmentObject::createHollowForDebug(
JSContext* cx, Handle<Scope*> scope) {
MOZ_ASSERT(scope->is<VarScope>() || scope->kind() == ScopeKind::StrictEval);
MOZ_ASSERT(!scope->hasEnvironment());
Rooted<SharedShape*> shape(cx,
EmptyEnvironmentShape<VarEnvironmentObject>(cx));
if (!shape) {
return nullptr;
}
// This environment's enclosing link is never used: the
// DebugEnvironmentProxy that refers to this scope carries its own
// enclosing link, which is what Debugger uses to construct the tree of
// Debugger.Environment objects.
RootedObject enclosingEnv(cx, &cx->global()->lexicalEnvironment());
Rooted<VarEnvironmentObject*> env(
cx, createInternal(cx, shape, enclosingEnv, gc::DefaultHeap));
if (!env) {
return nullptr;
}
RootedValue optimizedOut(cx, MagicValue(JS_OPTIMIZED_OUT));
RootedId id(cx);
for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++) {
id = NameToId(bi.name()->asPropertyName());
if (!SetProperty(cx, env, id, optimizedOut)) {
return nullptr;
}
}
env->initScope(scope);
return env;
}
/* static */
VarEnvironmentObject* VarEnvironmentObject::createTemplateObject(
JSContext* cx, Handle<VarScope*> scope) {
return create(cx, scope, nullptr, gc::TenuredHeap);
}
/* static */
VarEnvironmentObject* VarEnvironmentObject::createWithoutEnclosing(
JSContext* cx, Handle<VarScope*> scope) {
return create(cx, scope, nullptr, gc::DefaultHeap);
}
const JSClass VarEnvironmentObject::class_ = {
"Var", JSCLASS_HAS_RESERVED_SLOTS(VarEnvironmentObject::RESERVED_SLOTS)};
/*****************************************************************************/
const ObjectOps ModuleEnvironmentObject::objectOps_ = {
ModuleEnvironmentObject::lookupProperty, // lookupProperty
nullptr, // defineProperty
ModuleEnvironmentObject::hasProperty, // hasProperty
ModuleEnvironmentObject::getProperty, // getProperty
ModuleEnvironmentObject::setProperty, // setProperty
ModuleEnvironmentObject::
getOwnPropertyDescriptor, // getOwnPropertyDescriptor
ModuleEnvironmentObject::deleteProperty, // deleteProperty
nullptr, // getElements
nullptr, // funToString
};
const JSClassOps ModuleEnvironmentObject::classOps_ = {
nullptr, // addProperty
nullptr, // delProperty
nullptr, // enumerate
ModuleEnvironmentObject::newEnumerate, // newEnumerate
nullptr, // resolve
nullptr, // mayResolve
nullptr, // finalize
nullptr, // call
nullptr, // construct
nullptr, // trace
};
const JSClass ModuleEnvironmentObject::class_ = {
"ModuleEnvironmentObject",
JSCLASS_HAS_RESERVED_SLOTS(ModuleEnvironmentObject::RESERVED_SLOTS),
&ModuleEnvironmentObject::classOps_,
JS_NULL_CLASS_SPEC,
JS_NULL_CLASS_EXT,
&ModuleEnvironmentObject::objectOps_};
/* static */
ModuleEnvironmentObject* ModuleEnvironmentObject::create(
JSContext* cx, Handle<ModuleObject*> module) {
RootedScript script(cx, module->script());
Rooted<SharedShape*> shape(
cx, script->bodyScope()->as<ModuleScope>().environmentShape());
MOZ_ASSERT(shape->getObjectClass() == &class_);
Rooted<ModuleEnvironmentObject*> env(
cx, CreateEnvironmentObject<ModuleEnvironmentObject>(cx, shape,
TenuredObject));
if (!env) {
return nullptr;
}
env->initReservedSlot(MODULE_SLOT, ObjectValue(*module));
// Initialize this early so that we can manipulate the env object without
// causing assertions.
env->initEnclosingEnvironment(&cx->global()->lexicalEnvironment());
// Initialize all lexical bindings and imports as uninitialized. Imports
// get uninitialized because they have a special TDZ for cyclic imports.
for (BindingIter bi(script); bi; bi++) {
BindingLocation loc = bi.location();
if (loc.kind() == BindingLocation::Kind::Environment &&
BindingKindIsLexical(bi.kind())) {
env->initSlot(loc.slot(), MagicValue(JS_UNINITIALIZED_LEXICAL));
}
}
// It is not be possible to add or remove bindings from a module environment
// after this point as module code is always strict.
#ifdef DEBUG
for (ShapePropertyIter<NoGC> iter(env->shape()); !iter.done(); iter++) {
MOZ_ASSERT(!iter->configurable());
}
MOZ_ASSERT(env->hasFlag(ObjectFlag::NotExtensible));
MOZ_ASSERT(!env->inDictionaryMode());
#endif
return env;
}
ModuleObject& ModuleEnvironmentObject::module() const {
return getReservedSlot(MODULE_SLOT).toObject().as<ModuleObject>();
}
IndirectBindingMap& ModuleEnvironmentObject::importBindings() const {
return module().importBindings();
}
bool ModuleEnvironmentObject::createImportBinding(JSContext* cx,
Handle<JSAtom*> importName,
Handle<ModuleObject*> module,
Handle<JSAtom*> localName) {
RootedId importNameId(cx, AtomToId(importName));
RootedId localNameId(cx, AtomToId(localName));
Rooted<ModuleEnvironmentObject*> env(cx, &module->initialEnvironment());
if (!importBindings().put(cx, importNameId, env, localNameId)) {
return false;
}
return true;
}
bool ModuleEnvironmentObject::hasImportBinding(Handle<PropertyName*> name) {
return importBindings().has(NameToId(name));
}
bool ModuleEnvironmentObject::lookupImport(
jsid name, ModuleEnvironmentObject** envOut,
mozilla::Maybe<PropertyInfo>* propOut) {
return importBindings().lookup(name, envOut, propOut);
}
/* static */
bool ModuleEnvironmentObject::lookupProperty(JSContext* cx, HandleObject obj,
HandleId id,
MutableHandleObject objp,
PropertyResult* propp) {
const IndirectBindingMap& bindings =
obj->as<ModuleEnvironmentObject>().importBindings();
mozilla::Maybe<PropertyInfo> propInfo;
ModuleEnvironmentObject* env;
if (bindings.lookup(id, &env, &propInfo)) {
objp.set(env);
propp->setNativeProperty(*propInfo);
return true;
}
Rooted<NativeObject*> target(cx, &obj->as<NativeObject>());
if (!NativeLookupOwnProperty<CanGC>(cx, target, id, propp)) {
return false;
}
objp.set(obj);
return true;
}
/* static */
bool ModuleEnvironmentObject::hasProperty(JSContext* cx, HandleObject obj,
HandleId id, bool* foundp) {
if (obj->as<ModuleEnvironmentObject>().importBindings().has(id)) {
*foundp = true;
return true;
}
Rooted<NativeObject*> self(cx, &obj->as<NativeObject>());
return NativeHasProperty(cx, self, id, foundp);
}
/* static */
bool ModuleEnvironmentObject::getProperty(JSContext* cx, HandleObject obj,
HandleValue receiver, HandleId id,
MutableHandleValue vp) {
const IndirectBindingMap& bindings =
obj->as<ModuleEnvironmentObject>().importBindings();
mozilla::Maybe<PropertyInfo> prop;
ModuleEnvironmentObject* env;
if (bindings.lookup(id, &env, &prop)) {
vp.set(env->getSlot(prop->slot()));
return true;
}
Rooted<NativeObject*> self(cx, &obj->as<NativeObject>());
return NativeGetProperty(cx, self, receiver, id, vp);
}
/* static */
bool ModuleEnvironmentObject::setProperty(JSContext* cx, HandleObject obj,
HandleId id, HandleValue v,
HandleValue receiver,
JS::ObjectOpResult& result) {
Rooted<ModuleEnvironmentObject*> self(cx,
&obj->as<ModuleEnvironmentObject>());
if (self->importBindings().has(id)) {
return result.failReadOnly();
}
return NativeSetProperty<Qualified>(cx, self, id, v, receiver, result);
}
/* static */
bool ModuleEnvironmentObject::getOwnPropertyDescriptor(
JSContext* cx, HandleObject obj, HandleId id,
MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) {
const IndirectBindingMap& bindings =
obj->as<ModuleEnvironmentObject>().importBindings();
mozilla::Maybe<PropertyInfo> prop;
ModuleEnvironmentObject* env;
if (bindings.lookup(id, &env, &prop)) {
desc.set(mozilla::Some(PropertyDescriptor::Data(
env->getSlot(prop->slot()),
{JS::PropertyAttribute::Enumerable, JS::PropertyAttribute::Writable})));
return true;
}
Rooted<NativeObject*> self(cx, &obj->as<NativeObject>());
return NativeGetOwnPropertyDescriptor(cx, self, id, desc);
}
/* static */
bool ModuleEnvironmentObject::deleteProperty(JSContext* cx, HandleObject obj,
HandleId id,
ObjectOpResult& result) {
return result.failCantDelete();
}
/* static */
bool ModuleEnvironmentObject::newEnumerate(JSContext* cx, HandleObject obj,
MutableHandleIdVector properties,
bool enumerableOnly) {
Rooted<ModuleEnvironmentObject*> self(cx,
&obj->as<ModuleEnvironmentObject>());
const IndirectBindingMap& bs(self->importBindings());
MOZ_ASSERT(properties.length() == 0);
size_t count = bs.count() + self->slotSpan() - RESERVED_SLOTS;
if (!properties.reserve(count)) {
ReportOutOfMemory(cx);
return false;
}
bs.forEachExportedName([&](jsid name) { properties.infallibleAppend(name); });
for (ShapePropertyIter<NoGC> iter(self->shape()); !iter.done(); iter++) {
properties.infallibleAppend(iter->key());
}
MOZ_ASSERT(properties.length() == count);
return true;
}
/*****************************************************************************/
const JSClass WasmInstanceEnvironmentObject::class_ = {
"WasmInstance",
JSCLASS_HAS_RESERVED_SLOTS(WasmInstanceEnvironmentObject::RESERVED_SLOTS)};
/* static */
WasmInstanceEnvironmentObject*
WasmInstanceEnvironmentObject::createHollowForDebug(
JSContext* cx, Handle<WasmInstanceScope*> scope) {
Rooted<SharedShape*> shape(
cx, EmptyEnvironmentShape<WasmInstanceEnvironmentObject>(cx));
if (!shape) {
return nullptr;
}
auto* env = CreateEnvironmentObject<WasmInstanceEnvironmentObject>(cx, shape);
if (!env) {
return nullptr;
}
env->initEnclosingEnvironment(&cx->global()->lexicalEnvironment());
env->initReservedSlot(SCOPE_SLOT, PrivateGCThingValue(scope));
return env;
}
/*****************************************************************************/
const JSClass WasmFunctionCallObject::class_ = {
"WasmCall",
JSCLASS_HAS_RESERVED_SLOTS(WasmFunctionCallObject::RESERVED_SLOTS)};
/* static */
WasmFunctionCallObject* WasmFunctionCallObject::createHollowForDebug(
JSContext* cx, HandleObject enclosing, Handle<WasmFunctionScope*> scope) {
Rooted<SharedShape*> shape(cx,
EmptyEnvironmentShape<WasmFunctionCallObject>(cx));
if (!shape) {
return nullptr;
}
auto* callobj = CreateEnvironmentObject<WasmFunctionCallObject>(cx, shape);
if (!callobj) {
return nullptr;
}
callobj->initEnclosingEnvironment(enclosing);
callobj->initReservedSlot(SCOPE_SLOT, PrivateGCThingValue(scope));
return callobj;
}
/*****************************************************************************/
WithEnvironmentObject* WithEnvironmentObject::create(JSContext* cx,
HandleObject object,
HandleObject enclosing,
Handle<WithScope*> scope) {
Rooted<SharedShape*> shape(cx,
EmptyEnvironmentShape<WithEnvironmentObject>(cx));
if (!shape) {
return nullptr;
}
auto* obj = CreateEnvironmentObject<WithEnvironmentObject>(cx, shape);
if (!obj) {
return nullptr;
}
JSObject* thisObj = GetThisObject(object);
obj->initEnclosingEnvironment(enclosing);
obj->initReservedSlot(OBJECT_SLOT, ObjectValue(*object));
obj->initReservedSlot(THIS_SLOT, ObjectValue(*thisObj));
if (scope) {
obj->initReservedSlot(SCOPE_SLOT, PrivateGCThingValue(scope));
} else {
obj->initReservedSlot(SCOPE_SLOT, NullValue());
}
return obj;
}
WithEnvironmentObject* WithEnvironmentObject::createNonSyntactic(
JSContext* cx, HandleObject object, HandleObject enclosing) {
return create(cx, object, enclosing, nullptr);
}
static inline bool IsUnscopableDotName(JSContext* cx, HandleId id) {
return id.isAtom(cx->names().dotThis) || id.isAtom(cx->names().dotNewTarget);
}
#ifdef DEBUG
static bool IsInternalDotName(JSContext* cx, HandleId id) {
return id.isAtom(cx->names().dotThis) ||
id.isAtom(cx->names().dotGenerator) ||
id.isAtom(cx->names().dotInitializers) ||
id.isAtom(cx->names().dotFieldKeys) ||
id.isAtom(cx->names().dotStaticInitializers) ||
id.isAtom(cx->names().dotStaticFieldKeys) ||
id.isAtom(cx->names().dotArgs) ||
id.isAtom(cx->names().dotNewTarget) ||
id.isAtom(cx->names().starNamespaceStar);
}
#endif
/* Implements ES6 8.1.1.2.1 HasBinding steps 7-9. */
static bool CheckUnscopables(JSContext* cx, HandleObject obj, HandleId id,
bool* scopable) {
RootedId unscopablesId(
cx, PropertyKey::Symbol(cx->wellKnownSymbols().unscopables));
RootedValue v(cx);
if (!GetProperty(cx, obj, obj, unscopablesId, &v)) {
return false;
}
if (v.isObject()) {
RootedObject unscopablesObj(cx, &v.toObject());
if (!GetProperty(cx, unscopablesObj, unscopablesObj, id, &v)) {
return false;
}
*scopable = !ToBoolean(v);
} else {
*scopable = true;
}
return true;
}
static bool with_LookupProperty(JSContext* cx, HandleObject obj, HandleId id,
MutableHandleObject objp,
PropertyResult* propp) {
// SpiderMonkey-specific: consider the internal '.this' and '.newTarget' names
// to be unscopable.
if (IsUnscopableDotName(cx, id)) {
objp.set(nullptr);
propp->setNotFound();
return true;
}
// Other internal dot-names shouldn't even end up in with-environments.
MOZ_ASSERT(!IsInternalDotName(cx, id));
RootedObject actual(cx, &obj->as<WithEnvironmentObject>().object());
if (!LookupProperty(cx, actual, id, objp, propp)) {
return false;
}
if (propp->isFound()) {
bool scopable;
if (!CheckUnscopables(cx, actual, id, &scopable)) {
return false;
}
if (!scopable) {
objp.set(nullptr);
propp->setNotFound();
}
}
return true;
}
static bool with_DefineProperty(JSContext* cx, HandleObject obj, HandleId id,
Handle<PropertyDescriptor> desc,
ObjectOpResult& result) {
MOZ_ASSERT(!IsInternalDotName(cx, id));
RootedObject actual(cx, &obj->as<WithEnvironmentObject>().object());
return DefineProperty(cx, actual, id, desc, result);
}
static bool with_HasProperty(JSContext* cx, HandleObject obj, HandleId id,
bool* foundp) {
MOZ_ASSERT(!IsInternalDotName(cx, id));
RootedObject actual(cx, &obj->as<WithEnvironmentObject>().object());
// ES 8.1.1.2.1 step 3-5.
if (!HasProperty(cx, actual, id, foundp)) {
return false;
}
if (!*foundp) {
return true;
}
// Steps 7-10. (Step 6 is a no-op.)
return CheckUnscopables(cx, actual, id, foundp);
}
static bool with_GetProperty(JSContext* cx, HandleObject obj,
HandleValue receiver, HandleId id,
MutableHandleValue vp) {
MOZ_ASSERT(!IsInternalDotName(cx, id));
RootedObject actual(cx, &obj->as<WithEnvironmentObject>().object());
RootedValue actualReceiver(cx, receiver);
if (receiver.isObject() && &receiver.toObject() == obj) {
actualReceiver.setObject(*actual);
}
return GetProperty(cx, actual, actualReceiver, id, vp);
}
static bool with_SetProperty(JSContext* cx, HandleObject obj, HandleId id,
HandleValue v, HandleValue receiver,
ObjectOpResult& result) {
MOZ_ASSERT(!IsInternalDotName(cx, id));
RootedObject actual(cx, &obj->as<WithEnvironmentObject>().object());
RootedValue actualReceiver(cx, receiver);
if (receiver.isObject() && &receiver.toObject() == obj) {
actualReceiver.setObject(*actual);
}
return SetProperty(cx, actual, id, v, actualReceiver, result);
}
static bool with_GetOwnPropertyDescriptor(
JSContext* cx, HandleObject obj, HandleId id,
MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) {
MOZ_ASSERT(!IsInternalDotName(cx, id));
RootedObject actual(cx, &obj->as<WithEnvironmentObject>().object());
return GetOwnPropertyDescriptor(cx, actual, id, desc);
}
static bool with_DeleteProperty(JSContext* cx, HandleObject obj, HandleId id,
ObjectOpResult& result) {
MOZ_ASSERT(!IsInternalDotName(cx, id));
RootedObject actual(cx, &obj->as<WithEnvironmentObject>().object());
return DeleteProperty(cx, actual, id, result);
}
static const ObjectOps WithEnvironmentObjectOps = {
with_LookupProperty, // lookupProperty
with_DefineProperty, // defineProperty
with_HasProperty, // hasProperty
with_GetProperty, // getProperty
with_SetProperty, // setProperty
with_GetOwnPropertyDescriptor, // getOwnPropertyDescriptor
with_DeleteProperty, // deleteProperty
nullptr, // getElements
nullptr, // funToString
};
const JSClass WithEnvironmentObject::class_ = {
"With",
JSCLASS_HAS_RESERVED_SLOTS(WithEnvironmentObject::RESERVED_SLOTS),
JS_NULL_CLASS_OPS,
JS_NULL_CLASS_SPEC,
JS_NULL_CLASS_EXT,
&WithEnvironmentObjectOps};
/* static */
NonSyntacticVariablesObject* NonSyntacticVariablesObject::create(
JSContext* cx) {
Rooted<SharedShape*> shape(
cx, EmptyEnvironmentShape<NonSyntacticVariablesObject>(cx));
if (!shape) {
return nullptr;
}
Rooted<NonSyntacticVariablesObject*> obj(
cx, CreateEnvironmentObject<NonSyntacticVariablesObject>(cx, shape,
TenuredObject));
if (!obj) {
return nullptr;
}
MOZ_ASSERT(obj->isUnqualifiedVarObj());
if (!JSObject::setQualifiedVarObj(cx, obj)) {
return nullptr;
}
obj->initEnclosingEnvironment(&cx->global()->lexicalEnvironment());
return obj;
}
const JSClass NonSyntacticVariablesObject::class_ = {
"NonSyntacticVariablesObject",
JSCLASS_HAS_RESERVED_SLOTS(NonSyntacticVariablesObject::RESERVED_SLOTS)};
bool js::CreateNonSyntacticEnvironmentChain(JSContext* cx,
HandleObjectVector envChain,
MutableHandleObject env) {
// Callers are responsible for segregating the NonSyntactic case from simple
// compilation cases.
MOZ_RELEASE_ASSERT(!envChain.empty());
RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment());
if (!CreateObjectsForEnvironmentChain(cx, envChain, globalLexical, env)) {
return false;
}
// The XPConnect subscript loader, which may pass in its own
// environments to load scripts in, expects the environment chain to
// be the holder of "var" declarations. In SpiderMonkey, such objects
// are called "qualified varobjs", the "qualified" part meaning the
// declaration was qualified by "var". There is only sadness.
//
// See JSObject::isQualifiedVarObj.
if (!JSObject::setQualifiedVarObj(cx, env)) {
return false;
}
// Also get a non-syntactic lexical environment to capture 'let' and
// 'const' bindings. To persist lexical bindings, we have a 1-1
// mapping with the final unwrapped environment object (the
// environment that stores the 'var' bindings) and the lexical
// environment.
//
// TODOshu: disallow the subscript loader from using non-distinguished
// objects as dynamic scopes.
env.set(
ObjectRealm::get(env).getOrCreateNonSyntacticLexicalEnvironment(cx, env));
return !!env;
}
/*****************************************************************************/
const JSClass LexicalEnvironmentObject::class_ = {
"LexicalEnvironment",
JSCLASS_HAS_RESERVED_SLOTS(LexicalEnvironmentObject::RESERVED_SLOTS),
JS_NULL_CLASS_OPS,
JS_NULL_CLASS_SPEC,
JS_NULL_CLASS_EXT,
JS_NULL_OBJECT_OPS};
/* static */
LexicalEnvironmentObject* LexicalEnvironmentObject::create(
JSContext* cx, Handle<SharedShape*> shape, HandleObject enclosing,
gc::InitialHeap heap) {
MOZ_ASSERT(shape->getObjectClass() == &LexicalEnvironmentObject::class_);
// The JITs assume the result is nursery allocated unless we collected the
// nursery, so don't change |heap| here.
auto* env =
CreateEnvironmentObject<LexicalEnvironmentObject>(cx, shape, heap);
if (!env) {
return nullptr;
}
MOZ_ASSERT(!env->inDictionaryMode());
if (enclosing) {
env->initEnclosingEnvironment(enclosing);
}
return env;
}
bool LexicalEnvironmentObject::isExtensible() const {
return NativeObject::isExtensible();
}
/* static */
BlockLexicalEnvironmentObject* BlockLexicalEnvironmentObject::create(
JSContext* cx, Handle<LexicalScope*> scope, HandleObject enclosing,
gc::InitialHeap heap) {
cx->check(enclosing);
MOZ_ASSERT(scope->hasEnvironment());
Rooted<SharedShape*> shape(cx, scope->environmentShape());
auto* env = static_cast<BlockLexicalEnvironmentObject*>(
LexicalEnvironmentObject::create(cx, shape, enclosing, heap));
if (!env) {
return nullptr;
}
// All lexical bindings start off uninitialized for TDZ.
uint32_t lastSlot = env->getLastProperty().slot();
for (uint32_t slot = JSSLOT_FREE(&class_); slot <= lastSlot; slot++) {
env->initSlot(slot, MagicValue(JS_UNINITIALIZED_LEXICAL));
}
env->initScope(scope);
return env;
}
/* static */
BlockLexicalEnvironmentObject* BlockLexicalEnvironmentObject::createForFrame(
JSContext* cx, Handle<LexicalScope*> scope, AbstractFramePtr frame) {
RootedObject enclosing(cx, frame.environmentChain());
return create(cx, scope, enclosing, gc::DefaultHeap);
}
/* static */
BlockLexicalEnvironmentObject*
BlockLexicalEnvironmentObject::createHollowForDebug(
JSContext* cx, Handle<LexicalScope*> scope) {
MOZ_ASSERT(!scope->hasEnvironment());
Rooted<SharedShape*> shape(
cx, LexicalScope::getEmptyExtensibleEnvironmentShape(cx));
if (!shape) {
return nullptr;
}
// This environment's enclosing link is never used: the
// DebugEnvironmentProxy that refers to this scope carries its own
// enclosing link, which is what Debugger uses to construct the tree of
// Debugger.Environment objects.
RootedObject enclosingEnv(cx, &cx->global()->lexicalEnvironment());
Rooted<LexicalEnvironmentObject*> env(
cx, LexicalEnvironmentObject::create(cx, shape, enclosingEnv,
gc::TenuredHeap));
if (!env) {
return nullptr;
}
RootedValue optimizedOut(cx, MagicValue(JS_OPTIMIZED_OUT));
RootedId id(cx);
for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++) {
id = NameToId(bi.name()->asPropertyName());
if (!SetProperty(cx, env, id, optimizedOut)) {
return nullptr;
}
}
if (!JSObject::setFlag(cx, env, ObjectFlag::NotExtensible)) {
return nullptr;
}
env->as<ScopedLexicalEnvironmentObject>().initScope(scope);
return &env->as<BlockLexicalEnvironmentObject>();
}
/* static */
BlockLexicalEnvironmentObject*
BlockLexicalEnvironmentObject::createTemplateObject(
JSContext* cx, Handle<LexicalScope*> scope) {
return create(cx, scope, nullptr, gc::TenuredHeap);
}
/* static */
BlockLexicalEnvironmentObject*
BlockLexicalEnvironmentObject::createWithoutEnclosing(
JSContext* cx, Handle<LexicalScope*> scope) {
return create(cx, scope, nullptr, gc::DefaultHeap);
}
/* static */
BlockLexicalEnvironmentObject* BlockLexicalEnvironmentObject::clone(
JSContext* cx, Handle<BlockLexicalEnvironmentObject*> env) {
Rooted<LexicalScope*> scope(cx, &env->scope());
RootedObject enclosing(cx, &env->enclosingEnvironment());
Rooted<BlockLexicalEnvironmentObject*> copy(
cx, create(cx, scope, enclosing, gc::DefaultHeap));
if (!copy) {
return nullptr;
}
MOZ_ASSERT(env->shape() == copy->shape());
for (uint32_t i = JSSLOT_FREE(&class_); i < copy->slotSpan(); i++) {
copy->setSlot(i, env->getSlot(i));
}
return copy;
}
/* static */
BlockLexicalEnvironmentObject* BlockLexicalEnvironmentObject::recreate(
JSContext* cx, Handle<BlockLexicalEnvironmentObject*> env) {
Rooted<LexicalScope*> scope(cx, &env->scope());
RootedObject enclosing(cx, &env->enclosingEnvironment());
return create(cx, scope, enclosing, gc::DefaultHeap);
}
/* static */
NamedLambdaObject* NamedLambdaObject::create(JSContext* cx,
HandleFunction callee,
HandleObject enclosing,
gc::InitialHeap heap) {
MOZ_ASSERT(callee->isNamedLambda());
Rooted<Scope*> scope(cx, callee->nonLazyScript()->maybeNamedLambdaScope());
MOZ_ASSERT(scope && scope->environmentShape());
#ifdef DEBUG
{
// Named lambda objects have one (non-writable) property.
SharedShapePropertyIter<NoGC> iter(scope->environmentShape());
MOZ_ASSERT(iter->slot() == lambdaSlot());
MOZ_ASSERT(!iter->writable());
iter++;
MOZ_ASSERT(iter.done());
// There should be exactly one binding in the named lambda scope.
BindingIter bi(scope);
bi++;
MOZ_ASSERT(bi.done());
}
#endif
BlockLexicalEnvironmentObject* obj = BlockLexicalEnvironmentObject::create(
cx, scope.as<LexicalScope>(), enclosing, heap);
if (!obj) {
return nullptr;
}
obj->initFixedSlot(lambdaSlot(), ObjectValue(*callee));
return static_cast<NamedLambdaObject*>(obj);
}
/* static */
NamedLambdaObject* NamedLambdaObject::createTemplateObject(
JSContext* cx, HandleFunction callee) {
return create(cx, callee, nullptr, gc::TenuredHeap);
}
/* static */
NamedLambdaObject* NamedLambdaObject::createWithoutEnclosing(
JSContext* cx, HandleFunction callee) {
return create(cx, callee, nullptr, gc::DefaultHeap);
}
/* static */
NamedLambdaObject* NamedLambdaObject::create(JSContext* cx,
AbstractFramePtr frame) {
RootedFunction fun(cx, frame.callee());
RootedObject enclosing(cx, frame.environmentChain());
return create(cx, fun, enclosing, gc::DefaultHeap);
}
/* static */
size_t NamedLambdaObject::lambdaSlot() {
// Named lambda environments have exactly one name.
return JSSLOT_FREE(&LexicalEnvironmentObject::class_);
}
/* static */
ClassBodyLexicalEnvironmentObject* ClassBodyLexicalEnvironmentObject::create(
JSContext* cx, Handle<ClassBodyScope*> scope, HandleObject enclosing,
gc::InitialHeap heap) {
cx->check(enclosing);
MOZ_ASSERT(scope->hasEnvironment());
Rooted<SharedShape*> shape(cx, scope->environmentShape());
auto* env = static_cast<ClassBodyLexicalEnvironmentObject*>(
LexicalEnvironmentObject::create(cx, shape, enclosing, heap));
if (!env) {
return nullptr;
}
env->initScope(scope);
return env;
}
/* static */
ClassBodyLexicalEnvironmentObject*
ClassBodyLexicalEnvironmentObject::createForFrame(JSContext* cx,
Handle<ClassBodyScope*> scope,
AbstractFramePtr frame) {
RootedObject enclosing(cx, frame.environmentChain());
return create(cx, scope, enclosing, gc::DefaultHeap);
}
/* static */
ClassBodyLexicalEnvironmentObject*
ClassBodyLexicalEnvironmentObject::createTemplateObject(
JSContext* cx, Handle<ClassBodyScope*> scope) {
return create(cx, scope, nullptr, gc::TenuredHeap);
}
/* static */
ClassBodyLexicalEnvironmentObject*
ClassBodyLexicalEnvironmentObject::createWithoutEnclosing(
JSContext* cx, Handle<ClassBodyScope*> scope) {
return create(cx, scope, nullptr, gc::DefaultHeap);
}
JSObject* ExtensibleLexicalEnvironmentObject::thisObject() const {
JSObject* obj = &getReservedSlot(THIS_VALUE_OR_SCOPE_SLOT).toObject();
// Windows must never be exposed to script. setWindowProxyThisValue should
// have set this to the WindowProxy.
MOZ_ASSERT(!IsWindow(obj));
// WarpBuilder relies on the return value not being nursery-allocated for the
// global lexical environment.
MOZ_ASSERT_IF(isGlobal(), obj->isTenured());
return obj;
}
/* static */
ExtensibleLexicalEnvironmentObject*
ExtensibleLexicalEnvironmentObject::forVarEnvironment(JSObject* obj) {
ExtensibleLexicalEnvironmentObject* lexical = nullptr;
if (obj->is<GlobalObject>()) {
lexical = &obj->as<GlobalObject>().lexicalEnvironment();
} else {
lexical = ObjectRealm::get(obj).getNonSyntacticLexicalEnvironment(obj);
}
MOZ_ASSERT(lexical);
return lexical;
}
/* static */
GlobalLexicalEnvironmentObject* GlobalLexicalEnvironmentObject::create(
JSContext* cx, Handle<GlobalObject*> global) {
MOZ_ASSERT(global);
Rooted<SharedShape*> shape(
cx, LexicalScope::getEmptyExtensibleEnvironmentShape(cx));
if (!shape) {
return nullptr;
}
auto* env = static_cast<GlobalLexicalEnvironmentObject*>(
LexicalEnvironmentObject::create(cx, shape, global, gc::TenuredHeap));
if (!env) {
return nullptr;
}
env->initThisObject(global);
return env;
}
void GlobalLexicalEnvironmentObject::setWindowProxyThisObject(JSObject* obj) {
MOZ_ASSERT(IsWindowProxy(obj));
setReservedSlot(THIS_VALUE_OR_SCOPE_SLOT, ObjectValue(*obj));
}
/* static */
NonSyntacticLexicalEnvironmentObject*
NonSyntacticLexicalEnvironmentObject::create(JSContext* cx,
HandleObject enclosing,
HandleObject thisv) {
MOZ_ASSERT(enclosing);
MOZ_ASSERT(!IsSyntacticEnvironment(enclosing));
Rooted<SharedShape*> shape(
cx, LexicalScope::getEmptyExtensibleEnvironmentShape(cx));
if (!shape) {
return nullptr;
}
auto* env = static_cast<NonSyntacticLexicalEnvironmentObject*>(
LexicalEnvironmentObject::create(cx, shape, enclosing, gc::TenuredHeap));
if (!env) {
return nullptr;
}
env->initThisObject(thisv);
return env;
}
/* static */
RuntimeLexicalErrorObject* RuntimeLexicalErrorObject::create(
JSContext* cx, HandleObject enclosing, unsigned errorNumber) {
Rooted<SharedShape*> shape(
cx,
EmptyEnvironmentShape(cx, &class_, JSSLOT_FREE(&class_), ObjectFlags()));
if (!shape) {
return nullptr;
}
auto* obj = CreateEnvironmentObject<RuntimeLexicalErrorObject>(cx, shape);
if (!obj) {
return nullptr;
}
obj->initEnclosingEnvironment(enclosing);
obj->initReservedSlot(ERROR_SLOT, Int32Value(int32_t(errorNumber)));
return obj;
}
static void ReportRuntimeLexicalErrorId(JSContext* cx, unsigned errorNumber,
HandleId id) {
if (id.isAtom()) {
Rooted<PropertyName*> name(cx, id.toAtom()->asPropertyName());
ReportRuntimeLexicalError(cx, errorNumber, name);
return;
}
MOZ_CRASH(
"RuntimeLexicalErrorObject should only be used with property names");
}
static bool lexicalError_LookupProperty(JSContext* cx, HandleObject obj,
HandleId id, MutableHandleObject objp,
PropertyResult* propp) {
ReportRuntimeLexicalErrorId(
cx, obj->as<RuntimeLexicalErrorObject>().errorNumber(), id);
return false;
}
static bool lexicalError_HasProperty(JSContext* cx, HandleObject obj,
HandleId id, bool* foundp) {
ReportRuntimeLexicalErrorId(
cx, obj->as<RuntimeLexicalErrorObject>().errorNumber(), id);
return false;
}
static bool lexicalError_GetProperty(JSContext* cx, HandleObject obj,
HandleValue receiver, HandleId id,
MutableHandleValue vp) {
ReportRuntimeLexicalErrorId(
cx, obj->as<RuntimeLexicalErrorObject>().errorNumber(), id);
return false;
}
static bool lexicalError_SetProperty(JSContext* cx, HandleObject obj,
HandleId id, HandleValue v,
HandleValue receiver,
ObjectOpResult& result) {
ReportRuntimeLexicalErrorId(
cx, obj->as<RuntimeLexicalErrorObject>().errorNumber(), id);
return false;
}
static bool lexicalError_GetOwnPropertyDescriptor(
JSContext* cx, HandleObject obj, HandleId id,
MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) {
ReportRuntimeLexicalErrorId(
cx, obj->as<RuntimeLexicalErrorObject>().errorNumber(), id);
return false;
}
static bool lexicalError_DeleteProperty(JSContext* cx, HandleObject obj,
HandleId id, ObjectOpResult& result) {
ReportRuntimeLexicalErrorId(
cx, obj->as<RuntimeLexicalErrorObject>().errorNumber(), id);
return false;
}
static const ObjectOps RuntimeLexicalErrorObjectObjectOps = {
lexicalError_LookupProperty, // lookupProperty
nullptr, // defineProperty
lexicalError_HasProperty, // hasProperty
lexicalError_GetProperty, // getProperty
lexicalError_SetProperty, // setProperty
lexicalError_GetOwnPropertyDescriptor, // getOwnPropertyDescriptor
lexicalError_DeleteProperty, // deleteProperty
nullptr, // getElements
nullptr, // funToString
};
const JSClass RuntimeLexicalErrorObject::class_ = {
"RuntimeLexicalError",
JSCLASS_HAS_RESERVED_SLOTS(RuntimeLexicalErrorObject::RESERVED_SLOTS),
JS_NULL_CLASS_OPS,
JS_NULL_CLASS_SPEC,
JS_NULL_CLASS_EXT,
&RuntimeLexicalErrorObjectObjectOps};
/*****************************************************************************/
EnvironmentIter::EnvironmentIter(JSContext* cx, const EnvironmentIter& ei)
: si_(cx, ei.si_.get()), env_(cx, ei.env_), frame_(ei.frame_) {}
EnvironmentIter::EnvironmentIter(JSContext* cx, JSObject* env, Scope* scope)
: si_(cx, ScopeIter(scope)), env_(cx, env), frame_(NullFramePtr()) {
settle();
}
EnvironmentIter::EnvironmentIter(JSContext* cx, AbstractFramePtr frame,
const jsbytecode* pc)
: si_(cx, frame.script()->innermostScope(pc)),
env_(cx, frame.environmentChain()),
frame_(frame) {
cx->check(frame);
settle();
}
EnvironmentIter::EnvironmentIter(JSContext* cx, JSObject* env, Scope* scope,
AbstractFramePtr frame)
: si_(cx, ScopeIter(scope)), env_(cx, env), frame_(frame) {
cx->check(frame);
settle();
}
void EnvironmentIter::incrementScopeIter() {
if (si_.scope()->is<GlobalScope>()) {
// GlobalScopes may be syntactic or non-syntactic. Non-syntactic
// GlobalScopes correspond to zero or more non-syntactic
// EnvironmentsObjects followed by the global lexical scope, then the
// GlobalObject or another non-EnvironmentObject object.
if (!env_->is<EnvironmentObject>()) {
si_++;
}
} else {
si_++;
}
}
void EnvironmentIter::settle() {
// Check for trying to iterate a function or eval frame before the prologue
// has created the CallObject, in which case we have to skip.
if (frame_ && frame_.hasScript() &&
frame_.script()->initialEnvironmentShape() &&
!frame_.hasInitialEnvironment()) {
// Skip until we're at the enclosing scope of the script.
while (si_.scope() != frame_.script()->enclosingScope()) {
if (env_->is<BlockLexicalEnvironmentObject>() &&
&env_->as<BlockLexicalEnvironmentObject>().scope() == si_.scope()) {
MOZ_ASSERT(si_.kind() == ScopeKind::NamedLambda ||
si_.kind() == ScopeKind::StrictNamedLambda);
env_ =
&env_->as<BlockLexicalEnvironmentObject>().enclosingEnvironment();
}
incrementScopeIter();
}
}
// Check if we have left the extent of the initial frame after we've
// settled on a static scope.
if (frame_ &&
(!si_ ||
(frame_.hasScript() &&
si_.scope() == frame_.script()->enclosingScope()) ||
(frame_.isWasmDebugFrame() && !si_.scope()->is<WasmFunctionScope>()))) {
frame_ = NullFramePtr();
}
#ifdef DEBUG
if (si_) {
if (hasSyntacticEnvironment()) {
Scope* scope = si_.scope();
if (scope->is<LexicalScope>()) {
MOZ_ASSERT(scope == &env_->as<BlockLexicalEnvironmentObject>().scope());
} else if (scope->is<FunctionScope>()) {
MOZ_ASSERT(scope->as<FunctionScope>().script() ==
env_->as<CallObject>()
.callee()
.maybeCanonicalFunction()
->baseScript());
} else if (scope->is<VarScope>()) {
MOZ_ASSERT(scope == &env_->as<VarEnvironmentObject>().scope());
} else if (scope->is<WithScope>()) {
MOZ_ASSERT(scope == &env_->as<WithEnvironmentObject>().scope());
} else if (scope->is<EvalScope>()) {
MOZ_ASSERT(scope == &env_->as<VarEnvironmentObject>().scope());
} else if (scope->is<GlobalScope>()) {
MOZ_ASSERT(env_->is<GlobalObject>() ||
IsGlobalLexicalEnvironment(env_));
}
} else if (hasNonSyntacticEnvironmentObject()) {
if (env_->is<LexicalEnvironmentObject>()) {
// The global lexical environment still encloses non-syntactic
// environment objects.
MOZ_ASSERT(env_->is<NonSyntacticLexicalEnvironmentObject>() ||
env_->is<GlobalLexicalEnvironmentObject>());
} else if (env_->is<WithEnvironmentObject>()) {
MOZ_ASSERT(!env_->as<WithEnvironmentObject>().isSyntactic());
} else {
MOZ_ASSERT(env_->is<NonSyntacticVariablesObject>());
}
}
}
#endif
}
JSObject& EnvironmentIter::enclosingEnvironment() const {
// As an engine invariant (maintained internally and asserted by Execute),
// EnvironmentObjects and non-EnvironmentObjects cannot be interleaved on
// the scope chain; every scope chain must start with zero or more
// EnvironmentObjects and terminate with one or more
// non-EnvironmentObjects (viz., GlobalObject).
MOZ_ASSERT(done());
MOZ_ASSERT(!env_->is<EnvironmentObject>());
return *env_;
}
bool EnvironmentIter::hasNonSyntacticEnvironmentObject() const {
// The case we're worrying about here is a NonSyntactic static scope which
// has 0+ corresponding non-syntactic WithEnvironmentObject scopes, a
// NonSyntacticVariablesObject, or a NonSyntacticLexicalEnvironmentObject.
if (si_.kind() == ScopeKind::NonSyntactic) {
MOZ_ASSERT_IF(env_->is<WithEnvironmentObject>(),
!env_->as<WithEnvironmentObject>().isSyntactic());
return env_->is<EnvironmentObject>();
}
return false;
}
/* static */
HashNumber MissingEnvironmentKey::hash(MissingEnvironmentKey ek) {
return size_t(ek.frame_.raw()) ^ size_t(ek.scope_);
}
/* static */
bool MissingEnvironmentKey::match(MissingEnvironmentKey ek1,
MissingEnvironmentKey ek2) {
return ek1.frame_ == ek2.frame_ && ek1.scope_ == ek2.scope_;
}
bool LiveEnvironmentVal::traceWeak(JSTracer* trc) {
return TraceWeakEdge(trc, &scope_, "LiveEnvironmentVal::scope_");
}
// Live EnvironmentIter values may be added to DebugEnvironments::liveEnvs, as
// LiveEnvironmentVal instances. They need to have write barriers when they are
// added to the hash table, but no barriers when rehashing inside GC. It's a
// nasty hack, but the important thing is that LiveEnvironmentVal and
// MissingEnvironmentKey need to alias each other.
void LiveEnvironmentVal::staticAsserts() {
static_assert(
sizeof(LiveEnvironmentVal) == sizeof(MissingEnvironmentKey),
"LiveEnvironmentVal must be same size of MissingEnvironmentKey");
static_assert(
offsetof(LiveEnvironmentVal, scope_) ==
offsetof(MissingEnvironmentKey, scope_),
"LiveEnvironmentVal.scope_ must alias MissingEnvironmentKey.scope_");
}
/*****************************************************************************/
namespace {
/*
* DebugEnvironmentProxy is the handler for DebugEnvironmentProxy proxy
* objects. Having a custom handler (rather than trying to reuse js::Wrapper)
* gives us several important abilities:
* - We want to pass the EnvironmentObject as the receiver to forwarded scope
* property ops on aliased variables so that Call/Block/With ops do not all
* require a 'normalization' step.
* - The debug scope proxy can directly manipulate the stack frame to allow
* the debugger to read/write args/locals that were otherwise unaliased.