Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/js/src/vm/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 146 kB image not shown  

Quelle  EnvironmentObject.cpp   Sprache: C

 
/* -*- 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/Array.h"
#include "builtin/ModuleObject.h"
#include "js/EnvironmentChain.h"  // JS::EnvironmentChain
#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/Stack-inl.h"

using namespace js;

/*****************************************************************************/

/*
 * Return a shape representing the static scope containing the variable
 * accessed by the ALIASEDVAR op at 'pc'.
 */

static SharedShape* 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::Heap 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);

  return NativeObject::create<T>(cx, allocKind, heap, shape);
}

// 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::Heap 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::Heap 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::Heap::Tenured);
}

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::Heap heap = gc::Heap::Default;
  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::Heap 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::Heap 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::Heap::Default);
}

/* 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::Heap::Default));
  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::Heap::Tenured);
}

/* static */
VarEnvironmentObject* VarEnvironmentObject::createWithoutEnclosing(
    JSContext* cx, Handle<VarScope*> scope) {
  return create(cx, scope, nullptr, gc::Heap::Default);
}

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

#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
  env->initSlot(ModuleEnvironmentObject::DISPOSABLE_RESOURCE_STACK_SLOT,
                UndefinedValue());
#endif

  return env;
}

#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
static ArrayObject* initialiseAndSetDisposeCapabilityHelper(
    JSContext* cx, JS::Handle<EnvironmentObject*> env, uint32_t slot) {
  JS::Value slotData = env->getReservedSlot(slot);
  ArrayObject* disposablesList = nullptr;
  if (slotData.isUndefined()) {
    disposablesList = NewDenseEmptyArray(cx);
    if (!disposablesList) {
      return nullptr;
    }
    env->setReservedSlot(slot, ObjectValue(*disposablesList));
  } else {
    disposablesList = &slotData.toObject().as<ArrayObject>();
  }
  return disposablesList;
}

ArrayObject* DisposableEnvironmentObject::getOrCreateDisposeCapability(
    JSContext* cx) {
  Rooted<DisposableEnvironmentObject*> env(cx, this);
  return initialiseAndSetDisposeCapabilityHelper(
      cx, env, DISPOSABLE_RESOURCE_STACK_SLOT);
}

// TODO: The get & clear disposables function can be merged. (bug 1907736)
JS::Value DisposableEnvironmentObject::getDisposables() {
  return getReservedSlot(DISPOSABLE_RESOURCE_STACK_SLOT);
}

void DisposableEnvironmentObject::clearDisposables() {
  setReservedSlot(DISPOSABLE_RESOURCE_STACK_SLOT, UndefinedValue());
}
#endif

/* static */
ModuleEnvironmentObject* ModuleEnvironmentObject::createSynthetic(
    JSContext* cx, Handle<ModuleObject*> module) {
  Rooted<SharedShape*> shape(cx,
                             CreateEnvironmentShapeForSyntheticModule(
                                 cx, &class_, JSSLOT_FREE(&class_), module));
  if (!shape) {
    return nullptr;
  }

  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());

  // 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;
}

/*****************************************************************************/

JSObject* js::GetThisObject(JSObject* obj) {
  // Use the WindowProxy if the global is a Window, as Window must never be
  // exposed to script.
  if (obj->is<GlobalObject>()) {
    return ToWindowProxyIfWindow(obj);
  }

  // We should not expose any environments except NSVOs to script. The NSVO is
  // pretending to be the global object in this case.
  MOZ_ASSERT_IF(obj->is<EnvironmentObject>(),
                obj->is<NonSyntacticVariablesObject>());

  return obj;
}

WithEnvironmentObject* WithEnvironmentObject::create(
    JSContext* cx, HandleObject object, HandleObject enclosing,
    Handle<WithScope*> scope, JS::SupportUnscopables supportUnscopables) {
  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) {
    MOZ_ASSERT(supportUnscopables == JS::SupportUnscopables::Yes,
               "with-statements must support Symbol.unscopables");
    obj->initReservedSlot(SCOPE_OR_SUPPORT_UNSCOPABLES_SLOT,
                          PrivateGCThingValue(scope));
  } else {
    Value v = BooleanValue(supportUnscopables == JS::SupportUnscopables::Yes);
    obj->initReservedSlot(SCOPE_OR_SUPPORT_UNSCOPABLES_SLOT, v);
  }

  return obj;
}

WithEnvironmentObject* WithEnvironmentObject::createNonSyntactic(
    JSContext* cx, HandleObject object, HandleObject enclosing,
    JS::SupportUnscopables supportUnscopables) {
  return create(cx, object, enclosing, nullptr, supportUnscopables);
}

static inline bool IsUnscopableDotName(JSContext* cx, HandleId id) {
  return id.isAtom(cx->names().dot_this_) ||
         id.isAtom(cx->names().dot_newTarget_);
}

#ifdef DEBUG
static bool IsInternalDotName(JSContext* cx, HandleId id) {
  return id.isAtom(cx->names().dot_this_) ||
         id.isAtom(cx->names().dot_generator_) ||
         id.isAtom(cx->names().dot_initializers_) ||
         id.isAtom(cx->names().dot_fieldKeys_) ||
         id.isAtom(cx->names().dot_staticInitializers_) ||
         id.isAtom(cx->names().dot_staticFieldKeys_) ||
         id.isAtom(cx->names().dot_args_) ||
         id.isAtom(cx->names().dot_newTarget_) ||
         id.isAtom(cx->names().star_namespace_star_);
}
#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 = true;
    if (obj->as<WithEnvironmentObject>().supportUnscopables() &&
        !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 || !obj->as<WithEnvironmentObject>().supportUnscopables()) {
    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;
  }

  // An NVSO holds both variables qualified with `var` and those that are not.
  MOZ_ASSERT(obj->isUnqualifiedVarObj());
  MOZ_ASSERT(obj->isQualifiedVarObj());

  obj->initEnclosingEnvironment(&cx->global()->lexicalEnvironment());
  return obj;
}

const JSClass NonSyntacticVariablesObject::class_ = {
    "NonSyntacticVariablesObject",
    JSCLASS_HAS_RESERVED_SLOTS(NonSyntacticVariablesObject::RESERVED_SLOTS),
};

NonSyntacticLexicalEnvironmentObject* js::CreateNonSyntacticEnvironmentChain(
    JSContext* cx, const JS::EnvironmentChain& envChain) {
  // Callers are responsible for segregating the NonSyntactic case from simple
  // compilation cases.
  MOZ_RELEASE_ASSERT(!envChain.empty());

  RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment());
  Rooted<WithEnvironmentObject*> env(
      cx, CreateObjectsForEnvironmentChain(cx, envChain, globalLexical));
  if (!env) {
    return nullptr;
  }

  // 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 nullptr;
  }

  // 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.
  return ObjectRealm::get(env).getOrCreateNonSyntacticLexicalEnvironment(cx,
                                                                         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::Heap 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);
  }

#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
  env->initSlot(LexicalEnvironmentObject::DISPOSABLE_RESOURCE_STACK_SLOT,
                UndefinedValue());
#endif

  return env;
}

bool LexicalEnvironmentObject::isExtensible() const {
  return NativeObject::isExtensible();
}

/* static */
BlockLexicalEnvironmentObject* BlockLexicalEnvironmentObject::create(
    JSContext* cx, Handle<LexicalScope*> scope, HandleObject enclosing,
    gc::Heap 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::Heap::Default);
}

/* 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::Heap::Tenured));
  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::Heap::Tenured);
}

/* static */
BlockLexicalEnvironmentObject*
BlockLexicalEnvironmentObject::createWithoutEnclosing(
    JSContext* cx, Handle<LexicalScope*> scope) {
  return create(cx, scope, nullptr, gc::Heap::Default);
}

/* 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::Heap::Default));
  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::Heap::Default);
}

/* static */
NamedLambdaObject* NamedLambdaObject::create(JSContext* cx,
                                             HandleFunction callee,
                                             HandleObject enclosing,
                                             gc::Heap 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::Heap::Tenured);
}

/* static */
NamedLambdaObject* NamedLambdaObject::createWithoutEnclosing(
    JSContext* cx, HandleFunction callee) {
  return create(cx, callee, nullptr, gc::Heap::Default);
}

/* static */
NamedLambdaObject* NamedLambdaObject::create(JSContext* cx,
                                             AbstractFramePtr frame) {
  RootedFunction fun(cx, frame.callee());
  RootedObject enclosing(cx, frame.environmentChain());
  return create(cx, fun, enclosing, gc::Heap::Default);
}

/* 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::Heap 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::Heap::Default);
}

/* static */
ClassBodyLexicalEnvironmentObject*
ClassBodyLexicalEnvironmentObject::createTemplateObject(
    JSContext* cx, Handle<ClassBodyScope*> scope) {
  return create(cx, scope, nullptr, gc::Heap::Tenured);
}

/* static */
ClassBodyLexicalEnvironmentObject*
ClassBodyLexicalEnvironmentObject::createWithoutEnclosing(
    JSContext* cx, Handle<ClassBodyScope*> scope) {
  return create(cx, scope, nullptr, gc::Heap::Default);
}

JSObject* ExtensibleLexicalEnvironmentObject::thisObject() const {
  JSObject* obj = &getReservedSlot(THIS_VALUE_OR_SCOPE_SLOT).toObject();

  // Windows must never be exposed to script. initThisObject 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::Heap::Tenured));
  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::Heap::Tenured));
  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>() ||
                   env_->is<GlobalLexicalEnvironmentObject>());
      }
    } 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.
 *  - The debug scope proxy can store unaliased variables after the stack frame
 *    is popped so that they may still be read/written by the debugger.
 *  - The engine has made certain assumptions about the possible reads/writes
 *    in a scope. DebugEnvironmentProxy allows us to prevent the debugger from
 *    breaking those assumptions.
 *  - The engine makes optimizations that are observable to the debugger. The
 *    proxy can either hide these optimizations or make the situation more
 *    clear to the debugger. An example is 'arguments'.
 */

class DebugEnvironmentProxyHandler : public NurseryAllocableProxyHandler {
  enum Action { SET, GET };

  enum AccessResult { ACCESS_UNALIASED, ACCESS_GENERIC, ACCESS_LOST };

  /*
   * This function handles access to unaliased locals/formals. Since they
   * are unaliased, the values of these variables are not stored in the
   * slots of the normal CallObject and BlockLexicalEnvironmentObject
   * environments and thus must be recovered from somewhere else:
   *  + if the invocation for which the env was created is still executing,
   *    there is a JS frame live on the stack holding the values;
   *  + if the invocation for which the env was created finished executing:
   *     - and there was a DebugEnvironmentProxy associated with env, then
   *       the DebugEnvironments::onPop(Call|Lexical) handler copied out the
   *       unaliased variables. In both cases, a dense array is created in
   *       onPop(Call|Lexical) to hold the unaliased values and attached to
   *       the DebugEnvironmentProxy;
   *     - and there was not a DebugEnvironmentProxy yet associated with the
   *       scope, then the unaliased values are lost and not recoverable.
   *
   * Callers should check accessResult for non-failure results:
   *  - ACCESS_UNALIASED if the access was unaliased and completed
   *  - ACCESS_GENERIC   if the access was aliased or the property not found
   *  - ACCESS_LOST      if the value has been lost to the debugger and the
   *                     action is GET; if the action is SET, we assign to the
   *                     name of the variable on the environment object
   */

  bool handleUnaliasedAccess(JSContext* cx,
                             Handle<DebugEnvironmentProxy*> debugEnv,
                             Handle<EnvironmentObject*> env, HandleId id,
                             Action action, MutableHandleValue vp,
                             AccessResult* accessResult) const {
    MOZ_ASSERT(&debugEnv->environment() == env);
    MOZ_ASSERT_IF(action == SET, !debugEnv->isOptimizedOut());
    *accessResult = ACCESS_GENERIC;
    LiveEnvironmentVal* maybeLiveEnv =
        DebugEnvironments::hasLiveEnvironment(*env);

    // Handle unaliased formals, vars, lets, and consts at function or module
    // scope.
    if (env->is<CallObject>() || env->is<ModuleEnvironmentObject>()) {
      RootedScript script(cx);
      if (env->is<CallObject>()) {
        CallObject& callobj = env->as<CallObject>();
        RootedFunction fun(cx, &callobj.callee());
        script = JSFunction::getOrCreateScript(cx, fun);
      } else {
        script = env->as<ModuleEnvironmentObject>().module().maybeScript();
        if (!script) {
          return true;
        }
      }

      BindingIter bi(script);
      while (bi && NameToId(bi.name()->asPropertyName()) != id) {
        bi++;
      }
      if (!bi) {
        return true;
      }

      if (action == SET && bi.kind() == BindingKind::Const) {
        ReportRuntimeLexicalError(cx, JSMSG_BAD_CONST_ASSIGN, id);
        return false;
      }

      if (bi.location().kind() == BindingLocation::Kind::Import) {
        return true;
      }

      if (!bi.hasArgumentSlot()) {
        if (bi.closedOver()) {
          return true;
        }

        uint32_t i = bi.location().slot();
        if (maybeLiveEnv) {
          AbstractFramePtr frame = maybeLiveEnv->frame();
          if (action == GET) {
            vp.set(frame.unaliasedLocal(i));
          } else {
            frame.unaliasedLocal(i) = vp;
          }
        } else if (AbstractGeneratorObject* genObj =
                       GetGeneratorObjectForEnvironment(cx, env);
                   genObj && genObj->isSuspended() &&
                   genObj->hasStackStorage()) {
          if (action == GET) {
            vp.set(genObj->getUnaliasedLocal(i));
          } else {
            genObj->setUnaliasedLocal(i, vp);
          }
        } else if (NativeObject* snapshot = debugEnv->maybeSnapshot()) {
          if (action == GET) {
            vp.set(snapshot->getDenseElement(script->numArgs() + i));
          } else {
            snapshot->setDenseElement(script->numArgs() + i, vp);
          }
        } else {
          /* The unaliased value has been lost to the debugger. */
          if (action == GET) {
            *accessResult = ACCESS_LOST;
            return true;
          }
        }
      } else {
        unsigned i = bi.argumentSlot();
        if (bi.closedOver()) {
          return true;
        }

        if (maybeLiveEnv) {
          AbstractFramePtr frame = maybeLiveEnv->frame();
          if (script->argsObjAliasesFormals() && frame.hasArgsObj()) {
            if (action == GET) {
              vp.set(frame.argsObj().arg(i));
            } else {
              frame.argsObj().setArg(i, vp);
            }
          } else {
            if (action == GET) {
              vp.set(frame.unaliasedFormal(i, DONT_CHECK_ALIASING));
            } else {
              frame.unaliasedFormal(i, DONT_CHECK_ALIASING) = vp;
            }
          }
        } else if (NativeObject* snapshot = debugEnv->maybeSnapshot()) {
          if (action == GET) {
            vp.set(snapshot->getDenseElement(i));
          } else {
            snapshot->setDenseElement(i, vp);
          }
        } else {
          /* The unaliased value has been lost to the debugger. */
          if (action == GET) {
            *accessResult = ACCESS_LOST;
            return true;
          }
        }
      }

      // It is possible that an optimized out value flows to this
      // location due to Debugger.Frame.prototype.eval operating on a
      // live bailed-out Baseline frame. In that case, treat the access
      // as lost.
      if (vp.isMagic() && vp.whyMagic() == JS_OPTIMIZED_OUT) {
        *accessResult = ACCESS_LOST;
      } else {
        *accessResult = ACCESS_UNALIASED;
      }

      return true;
    }

    /*
     * Handle unaliased vars in functions with parameter expressions and
     * lexical bindings at block scope.
     */

    if (env->is<LexicalEnvironmentObject>() ||
        env->is<VarEnvironmentObject>()) {
      // Currently consider all non-syntactic top-level lexical bindings to be
      // aliased.
      if (env->is<LexicalEnvironmentObject>() &&
          !env->is<GlobalLexicalEnvironmentObject>() &&
          env->as<LexicalEnvironmentObject>().isExtensible()) {
        MOZ_ASSERT(!IsSyntacticEnvironment(env));
        return true;
      }

      // Currently all vars inside non-strict eval var environments are aliased.
      if (env->is<VarEnvironmentObject>() &&
          env->as<VarEnvironmentObject>().isForNonStrictEval()) {
        return true;
      }

      Rooted<Scope*> scope(cx, getEnvironmentScope(*env));
      uint32_t firstFrameSlot = scope->firstFrameSlot();

      BindingIter bi(scope);
      while (bi && NameToId(bi.name()->asPropertyName()) != id) {
        bi++;
      }
      if (!bi) {
        return true;
      }

      if (action == SET && bi.kind() == BindingKind::Const) {
        ReportRuntimeLexicalError(cx, JSMSG_BAD_CONST_ASSIGN, id);
        return false;
      }

      BindingLocation loc = bi.location();
      if (loc.kind() == BindingLocation::Kind::Environment) {
        return true;
      }

      // Named lambdas that are not closed over are lost.
      if (loc.kind() == BindingLocation::Kind::NamedLambdaCallee) {
        if (action == GET) {
          *accessResult = ACCESS_LOST;
        }
        return true;
      }

      MOZ_ASSERT(loc.kind() == BindingLocation::Kind::Frame);

      if (maybeLiveEnv) {
        AbstractFramePtr frame = maybeLiveEnv->frame();
        uint32_t local = loc.slot();
        MOZ_ASSERT(local < frame.script()->nfixed());
        Value& localVal = frame.unaliasedLocal(local);
        if (action == GET) {
          vp.set(localVal);
        } else {
          // Note: localVal could also be JS_OPTIMIZED_OUT.
          if (localVal.isMagic() &&
              localVal.whyMagic() == JS_UNINITIALIZED_LEXICAL) {
            ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, id);
            return false;
          }

          localVal = vp;
        }
      } else if (AbstractGeneratorObject* genObj =
                     GetGeneratorObjectForEnvironment(cx, debugEnv);
                 genObj && genObj->isSuspended() && genObj->hasStackStorage()) {
        if (action == GET) {
          vp.set(genObj->getUnaliasedLocal(loc.slot()));
        } else {
          genObj->setUnaliasedLocal(loc.slot(), vp);
        }
      } else if (NativeObject* snapshot = debugEnv->maybeSnapshot()) {
        // Indices in the frame snapshot are offset by the first frame
        // slot. See DebugEnvironments::takeFrameSnapshot.
        MOZ_ASSERT(loc.slot() >= firstFrameSlot);
        uint32_t snapshotIndex = loc.slot() - firstFrameSlot;
        if (action == GET) {
          vp.set(snapshot->getDenseElement(snapshotIndex));
        } else {
          snapshot->setDenseElement(snapshotIndex, vp);
        }
      } else {
        if (action == GET) {
          // A {Lexical,Var}EnvironmentObject whose static scope
          // does not have an environment shape at all is a "hollow"
          // block object reflected for missing block scopes. Their
          // slot values are lost.
          if (!scope->hasEnvironment()) {
            *accessResult = ACCESS_LOST;
            return true;
          }

          if (!GetProperty(cx, env, env, id, vp)) {
            return false;
          }
        } else {
          if (!SetProperty(cx, env, id, vp)) {
            return false;
          }
        }
      }

      // See comment above in analogous CallObject case.
      if (vp.isMagic() && vp.whyMagic() == JS_OPTIMIZED_OUT) {
        *accessResult = ACCESS_LOST;
      } else {
        *accessResult = ACCESS_UNALIASED;
      }

      return true;
    }

    if (env->is<WasmFunctionCallObject>()) {
      if (maybeLiveEnv) {
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=89 H=97 G=93

¤ Dauer der Verarbeitung: 0.24 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.