tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

EnvironmentObject.cpp (156351B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "vm/EnvironmentObject-inl.h"
      8 
      9 #include "mozilla/Maybe.h"
     10 
     11 #include "builtin/Array.h"
     12 #include "builtin/ModuleObject.h"
     13 #include "js/EnvironmentChain.h"  // JS::EnvironmentChain
     14 #include "js/Exception.h"
     15 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
     16 #include "js/friend/StackLimits.h"    // js::AutoCheckRecursionLimit
     17 #include "js/friend/WindowProxy.h"    // js::IsWindow, js::IsWindowProxy
     18 #include "js/PropertyAndElement.h"  // JS_DefineProperty, JS_DefinePropertyById, JS_HasProperty, JS_HasPropertyById
     19 #include "vm/ArgumentsObject.h"
     20 #include "vm/BytecodeIterator.h"
     21 #include "vm/BytecodeLocation.h"
     22 #include "vm/GeneratorObject.h"  // js::GetGeneratorObjectForEnvironment
     23 #include "vm/GlobalObject.h"
     24 #include "vm/JSObject.h"
     25 #include "vm/ProxyObject.h"
     26 #include "vm/Realm.h"
     27 #include "vm/Scope.h"
     28 #include "vm/Shape.h"
     29 #include "wasm/WasmDebug.h"
     30 #include "wasm/WasmDebugFrame.h"
     31 #include "wasm/WasmInstance.h"
     32 
     33 #include "gc/Marking-inl.h"
     34 #include "gc/StableCellHasher-inl.h"
     35 #include "vm/BytecodeIterator-inl.h"
     36 #include "vm/Stack-inl.h"
     37 
     38 using namespace js;
     39 
     40 /*****************************************************************************/
     41 
     42 /*
     43 * Return a shape representing the static scope containing the variable
     44 * accessed by the ALIASEDVAR op at 'pc'.
     45 */
     46 static SharedShape* EnvironmentCoordinateToEnvironmentShape(JSScript* script,
     47                                                            jsbytecode* pc) {
     48  MOZ_ASSERT(JOF_OPTYPE(JSOp(*pc)) == JOF_ENVCOORD);
     49  ScopeIter si(script->innermostScope(pc));
     50  uint32_t hops = EnvironmentCoordinate(pc).hops();
     51  while (true) {
     52    MOZ_ASSERT(!si.done());
     53    if (si.hasSyntacticEnvironment()) {
     54      if (!hops) {
     55        break;
     56      }
     57      hops--;
     58    }
     59    si++;
     60  }
     61  return si.environmentShape();
     62 }
     63 
     64 PropertyName* js::EnvironmentCoordinateNameSlow(JSScript* script,
     65                                                jsbytecode* pc) {
     66  SharedShape* shape = EnvironmentCoordinateToEnvironmentShape(script, pc);
     67  EnvironmentCoordinate ec(pc);
     68 
     69  SharedShapePropertyIter<NoGC> iter(shape);
     70  while (iter->slot() != ec.slot()) {
     71    iter++;
     72  }
     73  jsid id = iter->key();
     74 
     75  /* Beware nameless destructuring formal. */
     76  if (!id.isAtom()) {
     77    return script->runtimeFromAnyThread()->commonNames->empty_;
     78  }
     79  return id.toAtom()->asPropertyName();
     80 }
     81 
     82 /*****************************************************************************/
     83 
     84 template <typename T>
     85 static T* CreateEnvironmentObject(JSContext* cx, Handle<SharedShape*> shape,
     86                                  gc::Heap heap,
     87                                  gc::AllocSite* site = nullptr) {
     88  static_assert(std::is_base_of_v<EnvironmentObject, T>,
     89                "T must be an EnvironmentObject");
     90 
     91  // Environment objects do not have finalizers.
     92  gc::AllocKind allocKind = gc::GetGCObjectKind(shape->numFixedSlots());
     93  MOZ_ASSERT(gc::GetObjectFinalizeKind(&T::class_) == gc::FinalizeKind::None);
     94  MOZ_ASSERT(!gc::IsFinalizedKind(allocKind));
     95 
     96  return NativeObject::create<T>(cx, allocKind, heap, shape, site);
     97 }
     98 
     99 // Helper function for simple environment objects that don't need the overloads
    100 // above.
    101 template <typename T>
    102 static T* CreateEnvironmentObject(JSContext* cx, Handle<SharedShape*> shape,
    103                                  NewObjectKind newKind = GenericObject) {
    104  gc::Heap heap = GetInitialHeap(newKind, &T::class_);
    105  return CreateEnvironmentObject<T>(cx, shape, heap);
    106 }
    107 
    108 CallObject* CallObject::createWithShape(JSContext* cx,
    109                                        Handle<SharedShape*> shape,
    110                                        gc::Heap heap) {
    111  return CreateEnvironmentObject<CallObject>(cx, shape, heap);
    112 }
    113 
    114 /*
    115 * Create a CallObject for a JSScript that is not initialized to any particular
    116 * callsite. This object can either be initialized (with an enclosing scope and
    117 * callee) or used as a template for jit compilation.
    118 */
    119 CallObject* CallObject::create(JSContext* cx, HandleScript script,
    120                               HandleObject enclosing, gc::Heap heap,
    121                               gc::AllocSite* site) {
    122  Rooted<SharedShape*> shape(
    123      cx, script->bodyScope()->as<FunctionScope>().environmentShape());
    124  MOZ_ASSERT(shape->getObjectClass() == &class_);
    125 
    126  // The JITs assume the result is nursery allocated unless we collected the
    127  // nursery, so don't change |heap| here.
    128 
    129  auto* callObj = CreateEnvironmentObject<CallObject>(cx, shape, heap, site);
    130  if (!callObj) {
    131    return nullptr;
    132  }
    133 
    134  if (enclosing) {
    135    callObj->initEnclosingEnvironment(enclosing);
    136  }
    137 
    138  return callObj;
    139 }
    140 
    141 CallObject* CallObject::createTemplateObject(JSContext* cx, HandleScript script,
    142                                             HandleObject enclosing) {
    143  return create(cx, script, enclosing, gc::Heap::Tenured);
    144 }
    145 
    146 CallObject* CallObject::createForFrame(JSContext* cx, AbstractFramePtr frame,
    147                                       gc::AllocSite* site) {
    148  MOZ_ASSERT(frame.isFunctionFrame());
    149  cx->check(frame);
    150 
    151  RootedObject envChain(cx, frame.environmentChain());
    152  RootedFunction callee(cx, frame.callee());
    153  RootedScript script(cx, callee->nonLazyScript());
    154  gc::Heap heap = site ? site->initialHeap() : gc::Heap::Default;
    155 
    156  CallObject* callobj = create(cx, script, envChain, heap, site);
    157  if (!callobj) {
    158    return nullptr;
    159  }
    160 
    161  callobj->initFixedSlot(CALLEE_SLOT, ObjectValue(*callee));
    162 
    163  return callobj;
    164 }
    165 
    166 template <class EnvT>
    167 EnvT* FindEnclosingEnv(JSObject* env) {
    168  for (;;) {
    169    if (env->is<EnvT>()) {
    170      break;
    171    } else if (env->is<EnvironmentObject>()) {
    172      env = &env->as<EnvironmentObject>().enclosingEnvironment();
    173    } else if (env->is<DebugEnvironmentProxy>()) {
    174      EnvironmentObject& unwrapped =
    175          env->as<DebugEnvironmentProxy>().environment();
    176      if (unwrapped.is<EnvT>()) {
    177        env = &unwrapped;
    178        break;
    179      }
    180      env = &env->as<DebugEnvironmentProxy>().enclosingEnvironment();
    181    } else {
    182      MOZ_ASSERT(env->is<GlobalObject>());
    183      return nullptr;
    184    }
    185  }
    186  return &env->as<EnvT>();
    187 }
    188 
    189 CallObject* CallObject::find(JSObject* env) {
    190  return FindEnclosingEnv<CallObject>(env);
    191 }
    192 
    193 ModuleEnvironmentObject* ModuleEnvironmentObject::find(JSObject* env) {
    194  return FindEnclosingEnv<ModuleEnvironmentObject>(env);
    195 }
    196 
    197 CallObject* CallObject::createHollowForDebug(JSContext* cx,
    198                                             HandleFunction callee) {
    199  MOZ_ASSERT(!callee->needsCallObject());
    200 
    201  RootedScript script(cx, callee->nonLazyScript());
    202  Rooted<FunctionScope*> scope(cx, &script->bodyScope()->as<FunctionScope>());
    203  Rooted<SharedShape*> shape(cx, EmptyEnvironmentShape<CallObject>(cx));
    204  if (!shape) {
    205    return nullptr;
    206  }
    207  Rooted<CallObject*> callobj(cx, createWithShape(cx, shape));
    208  if (!callobj) {
    209    return nullptr;
    210  }
    211 
    212  // This environment's enclosing link is never used: the
    213  // DebugEnvironmentProxy that refers to this scope carries its own
    214  // enclosing link, which is what Debugger uses to construct the tree of
    215  // Debugger.Environment objects.
    216  callobj->initEnclosingEnvironment(&cx->global()->lexicalEnvironment());
    217  callobj->initFixedSlot(CALLEE_SLOT, ObjectValue(*callee));
    218 
    219  RootedValue optimizedOut(cx, MagicValue(JS_OPTIMIZED_OUT));
    220  RootedId id(cx);
    221  for (Rooted<BindingIter> bi(cx, BindingIter(script)); bi; bi++) {
    222    id = NameToId(bi.name()->asPropertyName());
    223    if (!SetProperty(cx, callobj, id, optimizedOut)) {
    224      return nullptr;
    225    }
    226  }
    227 
    228  return callobj;
    229 }
    230 
    231 const JSClass CallObject::class_ = {
    232    "Call",
    233    JSCLASS_HAS_RESERVED_SLOTS(CallObject::RESERVED_SLOTS),
    234 };
    235 
    236 /*****************************************************************************/
    237 
    238 /* static */
    239 VarEnvironmentObject* VarEnvironmentObject::createInternal(
    240    JSContext* cx, Handle<SharedShape*> shape, HandleObject enclosing,
    241    gc::Heap heap) {
    242  MOZ_ASSERT(shape->getObjectClass() == &class_);
    243 
    244  auto* env = CreateEnvironmentObject<VarEnvironmentObject>(cx, shape, heap);
    245  if (!env) {
    246    return nullptr;
    247  }
    248 
    249  MOZ_ASSERT(!env->inDictionaryMode());
    250 
    251  if (enclosing) {
    252    env->initEnclosingEnvironment(enclosing);
    253  }
    254 
    255  return env;
    256 }
    257 
    258 /* static */
    259 VarEnvironmentObject* VarEnvironmentObject::create(JSContext* cx,
    260                                                   Handle<Scope*> scope,
    261                                                   HandleObject enclosing,
    262                                                   gc::Heap heap) {
    263  MOZ_ASSERT(scope->is<EvalScope>() || scope->is<VarScope>());
    264 
    265  Rooted<SharedShape*> shape(cx, scope->environmentShape());
    266  auto* env = createInternal(cx, shape, enclosing, heap);
    267  if (!env) {
    268    return nullptr;
    269  }
    270  env->initScope(scope);
    271  return env;
    272 }
    273 
    274 /* static */
    275 VarEnvironmentObject* VarEnvironmentObject::createForFrame(
    276    JSContext* cx, Handle<Scope*> scope, AbstractFramePtr frame) {
    277 #ifdef DEBUG
    278  if (frame.isEvalFrame()) {
    279    MOZ_ASSERT(scope->is<EvalScope>() && scope == frame.script()->bodyScope());
    280    MOZ_ASSERT_IF(frame.isInterpreterFrame(),
    281                  cx->interpreterFrame() == frame.asInterpreterFrame());
    282    MOZ_ASSERT_IF(frame.isInterpreterFrame(),
    283                  cx->interpreterRegs().pc == frame.script()->code());
    284  } else {
    285    MOZ_ASSERT(frame.environmentChain());
    286    MOZ_ASSERT_IF(
    287        frame.callee()->needsCallObject(),
    288        &frame.environmentChain()->as<CallObject>().callee() == frame.callee());
    289  }
    290 #endif
    291 
    292  RootedObject envChain(cx, frame.environmentChain());
    293  return create(cx, scope, envChain, gc::Heap::Default);
    294 }
    295 
    296 /* static */
    297 VarEnvironmentObject* VarEnvironmentObject::createHollowForDebug(
    298    JSContext* cx, Handle<Scope*> scope) {
    299  MOZ_ASSERT(scope->is<VarScope>() || scope->kind() == ScopeKind::StrictEval);
    300  MOZ_ASSERT(!scope->hasEnvironment());
    301 
    302  Rooted<SharedShape*> shape(cx,
    303                             EmptyEnvironmentShape<VarEnvironmentObject>(cx));
    304  if (!shape) {
    305    return nullptr;
    306  }
    307 
    308  // This environment's enclosing link is never used: the
    309  // DebugEnvironmentProxy that refers to this scope carries its own
    310  // enclosing link, which is what Debugger uses to construct the tree of
    311  // Debugger.Environment objects.
    312  RootedObject enclosingEnv(cx, &cx->global()->lexicalEnvironment());
    313  Rooted<VarEnvironmentObject*> env(
    314      cx, createInternal(cx, shape, enclosingEnv, gc::Heap::Default));
    315  if (!env) {
    316    return nullptr;
    317  }
    318 
    319  RootedValue optimizedOut(cx, MagicValue(JS_OPTIMIZED_OUT));
    320  RootedId id(cx);
    321  for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++) {
    322    id = NameToId(bi.name()->asPropertyName());
    323    if (!SetProperty(cx, env, id, optimizedOut)) {
    324      return nullptr;
    325    }
    326  }
    327 
    328  env->initScope(scope);
    329  return env;
    330 }
    331 
    332 /* static */
    333 VarEnvironmentObject* VarEnvironmentObject::createTemplateObject(
    334    JSContext* cx, Handle<VarScope*> scope) {
    335  return create(cx, scope, nullptr, gc::Heap::Tenured);
    336 }
    337 
    338 /* static */
    339 VarEnvironmentObject* VarEnvironmentObject::createWithoutEnclosing(
    340    JSContext* cx, Handle<VarScope*> scope) {
    341  return create(cx, scope, nullptr, gc::Heap::Default);
    342 }
    343 
    344 const JSClass VarEnvironmentObject::class_ = {
    345    "Var",
    346    JSCLASS_HAS_RESERVED_SLOTS(VarEnvironmentObject::RESERVED_SLOTS),
    347 };
    348 
    349 /*****************************************************************************/
    350 
    351 const ObjectOps ModuleEnvironmentObject::objectOps_ = {
    352    ModuleEnvironmentObject::lookupProperty,  // lookupProperty
    353    nullptr,                                  // defineProperty
    354    ModuleEnvironmentObject::hasProperty,     // hasProperty
    355    ModuleEnvironmentObject::getProperty,     // getProperty
    356    ModuleEnvironmentObject::setProperty,     // setProperty
    357    ModuleEnvironmentObject::
    358        getOwnPropertyDescriptor,             // getOwnPropertyDescriptor
    359    ModuleEnvironmentObject::deleteProperty,  // deleteProperty
    360    nullptr,                                  // getElements
    361    nullptr,                                  // funToString
    362 };
    363 
    364 const JSClassOps ModuleEnvironmentObject::classOps_ = {
    365    nullptr,                                // addProperty
    366    nullptr,                                // delProperty
    367    nullptr,                                // enumerate
    368    ModuleEnvironmentObject::newEnumerate,  // newEnumerate
    369    nullptr,                                // resolve
    370    nullptr,                                // mayResolve
    371    nullptr,                                // finalize
    372    nullptr,                                // call
    373    nullptr,                                // construct
    374    nullptr,                                // trace
    375 };
    376 
    377 const JSClass ModuleEnvironmentObject::class_ = {
    378    "ModuleEnvironmentObject",
    379    JSCLASS_HAS_RESERVED_SLOTS(ModuleEnvironmentObject::RESERVED_SLOTS),
    380    &ModuleEnvironmentObject::classOps_,
    381    JS_NULL_CLASS_SPEC,
    382    JS_NULL_CLASS_EXT,
    383    &ModuleEnvironmentObject::objectOps_,
    384 };
    385 
    386 /* static */
    387 ModuleEnvironmentObject* ModuleEnvironmentObject::create(
    388    JSContext* cx, Handle<ModuleObject*> module) {
    389  RootedScript script(cx, module->script());
    390  Rooted<SharedShape*> shape(
    391      cx, script->bodyScope()->as<ModuleScope>().environmentShape());
    392  MOZ_ASSERT(shape->getObjectClass() == &class_);
    393 
    394  ModuleEnvironmentObject* env =
    395      CreateEnvironmentObject<ModuleEnvironmentObject>(cx, shape,
    396                                                       TenuredObject);
    397  if (!env) {
    398    return nullptr;
    399  }
    400 
    401  env->initReservedSlot(MODULE_SLOT, ObjectValue(*module));
    402 
    403  // Initialize this early so that we can manipulate the env object without
    404  // causing assertions.
    405  env->initEnclosingEnvironment(&cx->global()->lexicalEnvironment());
    406 
    407  // Initialize all lexical bindings and imports as uninitialized. Imports
    408  // get uninitialized because they have a special TDZ for cyclic imports.
    409  for (BindingIter bi(script); bi; bi++) {
    410    BindingLocation loc = bi.location();
    411    if (loc.kind() == BindingLocation::Kind::Environment &&
    412        BindingKindIsLexical(bi.kind())) {
    413      env->initSlot(loc.slot(), MagicValue(JS_UNINITIALIZED_LEXICAL));
    414    }
    415  }
    416 
    417  // It is not be possible to add or remove bindings from a module environment
    418  // after this point as module code is always strict.
    419 #ifdef DEBUG
    420  for (ShapePropertyIter<NoGC> iter(env->shape()); !iter.done(); iter++) {
    421    MOZ_ASSERT(!iter->configurable());
    422  }
    423  MOZ_ASSERT(env->hasFlag(ObjectFlag::NotExtensible));
    424  MOZ_ASSERT(!env->inDictionaryMode());
    425 #endif
    426 
    427 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
    428  env->initSlot(ModuleEnvironmentObject::DISPOSABLE_RESOURCE_STACK_SLOT,
    429                UndefinedValue());
    430 #endif
    431 
    432  return env;
    433 }
    434 
    435 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
    436 static ArrayObject* initialiseAndSetDisposeCapabilityHelper(
    437    JSContext* cx, JS::Handle<EnvironmentObject*> env, uint32_t slot) {
    438  JS::Value slotData = env->getReservedSlot(slot);
    439  ArrayObject* disposablesList = nullptr;
    440  if (slotData.isUndefined()) {
    441    disposablesList = NewDenseEmptyArray(cx);
    442    if (!disposablesList) {
    443      return nullptr;
    444    }
    445    env->setReservedSlot(slot, ObjectValue(*disposablesList));
    446  } else {
    447    disposablesList = &slotData.toObject().as<ArrayObject>();
    448  }
    449  return disposablesList;
    450 }
    451 
    452 ArrayObject* DisposableEnvironmentObject::getOrCreateDisposeCapability(
    453    JSContext* cx) {
    454  Rooted<DisposableEnvironmentObject*> env(cx, this);
    455  return initialiseAndSetDisposeCapabilityHelper(
    456      cx, env, DISPOSABLE_RESOURCE_STACK_SLOT);
    457 }
    458 
    459 // TODO: The get & clear disposables function can be merged. (bug 1907736)
    460 JS::Value DisposableEnvironmentObject::getDisposables() {
    461  return getReservedSlot(DISPOSABLE_RESOURCE_STACK_SLOT);
    462 }
    463 
    464 void DisposableEnvironmentObject::clearDisposables() {
    465  setReservedSlot(DISPOSABLE_RESOURCE_STACK_SLOT, UndefinedValue());
    466 }
    467 #endif
    468 
    469 /* static */
    470 ModuleEnvironmentObject* ModuleEnvironmentObject::createSynthetic(
    471    JSContext* cx, Handle<ModuleObject*> module) {
    472  Rooted<SharedShape*> shape(cx,
    473                             CreateEnvironmentShapeForSyntheticModule(
    474                                 cx, &class_, JSSLOT_FREE(&class_), module));
    475  if (!shape) {
    476    return nullptr;
    477  }
    478 
    479  MOZ_ASSERT(shape->getObjectClass() == &class_);
    480 
    481  ModuleEnvironmentObject* env =
    482      CreateEnvironmentObject<ModuleEnvironmentObject>(cx, shape,
    483                                                       TenuredObject);
    484  if (!env) {
    485    return nullptr;
    486  }
    487 
    488  env->initReservedSlot(MODULE_SLOT, ObjectValue(*module));
    489 
    490  // Initialize this early so that we can manipulate the env object without
    491  // causing assertions.
    492  env->initEnclosingEnvironment(&cx->global()->lexicalEnvironment());
    493 
    494  // It is not be possible to add or remove bindings from a module environment
    495  // after this point as module code is always strict.
    496 #ifdef DEBUG
    497  for (ShapePropertyIter<NoGC> iter(env->shape()); !iter.done(); iter++) {
    498    MOZ_ASSERT(!iter->configurable());
    499  }
    500  MOZ_ASSERT(env->hasFlag(ObjectFlag::NotExtensible));
    501  MOZ_ASSERT(!env->inDictionaryMode());
    502 #endif
    503 
    504  return env;
    505 }
    506 
    507 ModuleObject& ModuleEnvironmentObject::module() const {
    508  return getReservedSlot(MODULE_SLOT).toObject().as<ModuleObject>();
    509 }
    510 
    511 IndirectBindingMap& ModuleEnvironmentObject::importBindings() const {
    512  return module().importBindings();
    513 }
    514 
    515 bool ModuleEnvironmentObject::createImportBinding(JSContext* cx,
    516                                                  Handle<JSAtom*> importName,
    517                                                  Handle<ModuleObject*> module,
    518                                                  Handle<JSAtom*> localName) {
    519  RootedId importNameId(cx, AtomToId(importName));
    520  RootedId localNameId(cx, AtomToId(localName));
    521  Rooted<ModuleEnvironmentObject*> env(cx, &module->initialEnvironment());
    522  return importBindings().put(cx, importNameId, env, localNameId);
    523 }
    524 
    525 bool ModuleEnvironmentObject::hasImportBinding(Handle<PropertyName*> name) {
    526  return importBindings().has(NameToId(name));
    527 }
    528 
    529 bool ModuleEnvironmentObject::lookupImport(
    530    jsid name, ModuleEnvironmentObject** envOut,
    531    mozilla::Maybe<PropertyInfo>* propOut) {
    532  return importBindings().lookup(name, envOut, propOut);
    533 }
    534 
    535 /* static */
    536 bool ModuleEnvironmentObject::lookupProperty(JSContext* cx, HandleObject obj,
    537                                             HandleId id,
    538                                             MutableHandleObject objp,
    539                                             PropertyResult* propp) {
    540  const IndirectBindingMap& bindings =
    541      obj->as<ModuleEnvironmentObject>().importBindings();
    542  mozilla::Maybe<PropertyInfo> propInfo;
    543  ModuleEnvironmentObject* env;
    544  if (bindings.lookup(id, &env, &propInfo)) {
    545    objp.set(env);
    546    propp->setNativeProperty(*propInfo);
    547    return true;
    548  }
    549 
    550  Rooted<NativeObject*> target(cx, &obj->as<NativeObject>());
    551  if (!NativeLookupOwnProperty<CanGC>(cx, target, id, propp)) {
    552    return false;
    553  }
    554 
    555  objp.set(obj);
    556  return true;
    557 }
    558 
    559 /* static */
    560 bool ModuleEnvironmentObject::hasProperty(JSContext* cx, HandleObject obj,
    561                                          HandleId id, bool* foundp) {
    562  if (obj->as<ModuleEnvironmentObject>().importBindings().has(id)) {
    563    *foundp = true;
    564    return true;
    565  }
    566 
    567  Rooted<NativeObject*> self(cx, &obj->as<NativeObject>());
    568  return NativeHasProperty(cx, self, id, foundp);
    569 }
    570 
    571 /* static */
    572 bool ModuleEnvironmentObject::getProperty(JSContext* cx, HandleObject obj,
    573                                          HandleValue receiver, HandleId id,
    574                                          MutableHandleValue vp) {
    575  const IndirectBindingMap& bindings =
    576      obj->as<ModuleEnvironmentObject>().importBindings();
    577  mozilla::Maybe<PropertyInfo> prop;
    578  ModuleEnvironmentObject* env;
    579  if (bindings.lookup(id, &env, &prop)) {
    580    vp.set(env->getSlot(prop->slot()));
    581    return true;
    582  }
    583 
    584  Rooted<NativeObject*> self(cx, &obj->as<NativeObject>());
    585  return NativeGetProperty(cx, self, receiver, id, vp);
    586 }
    587 
    588 /* static */
    589 bool ModuleEnvironmentObject::setProperty(JSContext* cx, HandleObject obj,
    590                                          HandleId id, HandleValue v,
    591                                          HandleValue receiver,
    592                                          JS::ObjectOpResult& result) {
    593  Rooted<ModuleEnvironmentObject*> self(cx,
    594                                        &obj->as<ModuleEnvironmentObject>());
    595  if (self->importBindings().has(id)) {
    596    return result.failReadOnly();
    597  }
    598 
    599  return NativeSetProperty<Qualified>(cx, self, id, v, receiver, result);
    600 }
    601 
    602 /* static */
    603 bool ModuleEnvironmentObject::getOwnPropertyDescriptor(
    604    JSContext* cx, HandleObject obj, HandleId id,
    605    MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) {
    606  const IndirectBindingMap& bindings =
    607      obj->as<ModuleEnvironmentObject>().importBindings();
    608  mozilla::Maybe<PropertyInfo> prop;
    609  ModuleEnvironmentObject* env;
    610  if (bindings.lookup(id, &env, &prop)) {
    611    desc.set(mozilla::Some(PropertyDescriptor::Data(
    612        env->getSlot(prop->slot()),
    613        {JS::PropertyAttribute::Enumerable, JS::PropertyAttribute::Writable})));
    614    return true;
    615  }
    616 
    617  Rooted<NativeObject*> self(cx, &obj->as<NativeObject>());
    618  return NativeGetOwnPropertyDescriptor(cx, self, id, desc);
    619 }
    620 
    621 /* static */
    622 bool ModuleEnvironmentObject::deleteProperty(JSContext* cx, HandleObject obj,
    623                                             HandleId id,
    624                                             ObjectOpResult& result) {
    625  return result.failCantDelete();
    626 }
    627 
    628 /* static */
    629 bool ModuleEnvironmentObject::newEnumerate(JSContext* cx, HandleObject obj,
    630                                           MutableHandleIdVector properties,
    631                                           bool enumerableOnly) {
    632  Rooted<ModuleEnvironmentObject*> self(cx,
    633                                        &obj->as<ModuleEnvironmentObject>());
    634  const IndirectBindingMap& bs(self->importBindings());
    635 
    636  MOZ_ASSERT(properties.length() == 0);
    637  size_t count = bs.count() + self->slotSpan() - RESERVED_SLOTS;
    638  if (!properties.reserve(count)) {
    639    ReportOutOfMemory(cx);
    640    return false;
    641  }
    642 
    643  bs.forEachExportedName([&](jsid name) { properties.infallibleAppend(name); });
    644 
    645  for (ShapePropertyIter<NoGC> iter(self->shape()); !iter.done(); iter++) {
    646    properties.infallibleAppend(iter->key());
    647  }
    648 
    649  MOZ_ASSERT(properties.length() == count);
    650  return true;
    651 }
    652 
    653 /*****************************************************************************/
    654 
    655 const JSClass WasmInstanceEnvironmentObject::class_ = {
    656    "WasmInstance",
    657    JSCLASS_HAS_RESERVED_SLOTS(WasmInstanceEnvironmentObject::RESERVED_SLOTS),
    658 };
    659 
    660 /* static */
    661 WasmInstanceEnvironmentObject*
    662 WasmInstanceEnvironmentObject::createHollowForDebug(
    663    JSContext* cx, Handle<WasmInstanceScope*> scope) {
    664  Rooted<SharedShape*> shape(
    665      cx, EmptyEnvironmentShape<WasmInstanceEnvironmentObject>(cx));
    666  if (!shape) {
    667    return nullptr;
    668  }
    669 
    670  auto* env = CreateEnvironmentObject<WasmInstanceEnvironmentObject>(cx, shape);
    671  if (!env) {
    672    return nullptr;
    673  }
    674 
    675  env->initEnclosingEnvironment(&cx->global()->lexicalEnvironment());
    676  env->initReservedSlot(SCOPE_SLOT, PrivateGCThingValue(scope));
    677 
    678  return env;
    679 }
    680 
    681 /*****************************************************************************/
    682 
    683 const JSClass WasmFunctionCallObject::class_ = {
    684    "WasmCall",
    685    JSCLASS_HAS_RESERVED_SLOTS(WasmFunctionCallObject::RESERVED_SLOTS),
    686 };
    687 
    688 /* static */
    689 WasmFunctionCallObject* WasmFunctionCallObject::createHollowForDebug(
    690    JSContext* cx, HandleObject enclosing, Handle<WasmFunctionScope*> scope) {
    691  Rooted<SharedShape*> shape(cx,
    692                             EmptyEnvironmentShape<WasmFunctionCallObject>(cx));
    693  if (!shape) {
    694    return nullptr;
    695  }
    696 
    697  auto* callobj = CreateEnvironmentObject<WasmFunctionCallObject>(cx, shape);
    698  if (!callobj) {
    699    return nullptr;
    700  }
    701 
    702  callobj->initEnclosingEnvironment(enclosing);
    703  callobj->initReservedSlot(SCOPE_SLOT, PrivateGCThingValue(scope));
    704 
    705  return callobj;
    706 }
    707 
    708 /*****************************************************************************/
    709 
    710 JSObject* js::GetThisObject(JSObject* obj) {
    711  // Use the WindowProxy if the global is a Window, as Window must never be
    712  // exposed to script.
    713  if (obj->is<GlobalObject>()) {
    714    return ToWindowProxyIfWindow(obj);
    715  }
    716 
    717  // We should not expose any environments except NSVOs to script. The NSVO is
    718  // pretending to be the global object in this case.
    719  MOZ_ASSERT_IF(obj->is<EnvironmentObject>(),
    720                obj->is<NonSyntacticVariablesObject>());
    721 
    722  return obj;
    723 }
    724 
    725 WithEnvironmentObject* WithEnvironmentObject::create(
    726    JSContext* cx, HandleObject object, HandleObject enclosing,
    727    Handle<WithScope*> scope, JS::SupportUnscopables supportUnscopables) {
    728  Rooted<SharedShape*> shape(cx,
    729                             EmptyEnvironmentShape<WithEnvironmentObject>(cx));
    730  if (!shape) {
    731    return nullptr;
    732  }
    733 
    734  auto* obj = CreateEnvironmentObject<WithEnvironmentObject>(cx, shape);
    735  if (!obj) {
    736    return nullptr;
    737  }
    738 
    739  JSObject* thisObj = GetThisObject(object);
    740 
    741  obj->initEnclosingEnvironment(enclosing);
    742  obj->initReservedSlot(OBJECT_SLOT, ObjectValue(*object));
    743  obj->initReservedSlot(THIS_SLOT, ObjectValue(*thisObj));
    744  if (scope) {
    745    MOZ_ASSERT(supportUnscopables == JS::SupportUnscopables::Yes,
    746               "with-statements must support Symbol.unscopables");
    747    obj->initReservedSlot(SCOPE_OR_SUPPORT_UNSCOPABLES_SLOT,
    748                          PrivateGCThingValue(scope));
    749  } else {
    750    Value v = BooleanValue(supportUnscopables == JS::SupportUnscopables::Yes);
    751    obj->initReservedSlot(SCOPE_OR_SUPPORT_UNSCOPABLES_SLOT, v);
    752  }
    753 
    754  return obj;
    755 }
    756 
    757 WithEnvironmentObject* WithEnvironmentObject::createNonSyntactic(
    758    JSContext* cx, HandleObject object, HandleObject enclosing,
    759    JS::SupportUnscopables supportUnscopables) {
    760  return create(cx, object, enclosing, nullptr, supportUnscopables);
    761 }
    762 
    763 static inline bool IsUnscopableDotName(JSContext* cx, HandleId id) {
    764  return id.isAtom(cx->names().dot_this_) ||
    765         id.isAtom(cx->names().dot_newTarget_);
    766 }
    767 
    768 #ifdef DEBUG
    769 static bool IsInternalDotName(JSContext* cx, HandleId id) {
    770  return id.isAtom(cx->names().dot_this_) ||
    771         id.isAtom(cx->names().dot_generator_) ||
    772         id.isAtom(cx->names().dot_initializers_) ||
    773         id.isAtom(cx->names().dot_fieldKeys_) ||
    774         id.isAtom(cx->names().dot_staticInitializers_) ||
    775         id.isAtom(cx->names().dot_staticFieldKeys_) ||
    776         id.isAtom(cx->names().dot_args_) ||
    777         id.isAtom(cx->names().dot_newTarget_) ||
    778         id.isAtom(cx->names().star_namespace_star_);
    779 }
    780 #endif
    781 
    782 /* Implements ES6 8.1.1.2.1 HasBinding steps 7-9. */
    783 static bool CheckUnscopables(JSContext* cx, HandleObject obj, HandleId id,
    784                             bool* scopable) {
    785  RootedId unscopablesId(
    786      cx, PropertyKey::Symbol(cx->wellKnownSymbols().unscopables));
    787  RootedValue v(cx);
    788  if (!GetProperty(cx, obj, obj, unscopablesId, &v)) {
    789    return false;
    790  }
    791  if (v.isObject()) {
    792    RootedObject unscopablesObj(cx, &v.toObject());
    793    if (!GetProperty(cx, unscopablesObj, unscopablesObj, id, &v)) {
    794      return false;
    795    }
    796    *scopable = !ToBoolean(v);
    797  } else {
    798    *scopable = true;
    799  }
    800  return true;
    801 }
    802 
    803 static bool with_LookupProperty(JSContext* cx, HandleObject obj, HandleId id,
    804                                MutableHandleObject objp,
    805                                PropertyResult* propp) {
    806  // SpiderMonkey-specific: consider the internal '.this' and '.newTarget' names
    807  // to be unscopable.
    808  if (IsUnscopableDotName(cx, id)) {
    809    objp.set(nullptr);
    810    propp->setNotFound();
    811    return true;
    812  }
    813 
    814  // Other internal dot-names shouldn't even end up in with-environments.
    815  MOZ_ASSERT(!IsInternalDotName(cx, id));
    816 
    817  RootedObject actual(cx, &obj->as<WithEnvironmentObject>().object());
    818  if (!LookupProperty(cx, actual, id, objp, propp)) {
    819    return false;
    820  }
    821 
    822  if (propp->isFound()) {
    823    bool scopable = true;
    824    if (obj->as<WithEnvironmentObject>().supportUnscopables() &&
    825        !CheckUnscopables(cx, actual, id, &scopable)) {
    826      return false;
    827    }
    828    if (!scopable) {
    829      objp.set(nullptr);
    830      propp->setNotFound();
    831    }
    832  }
    833  return true;
    834 }
    835 
    836 static bool with_DefineProperty(JSContext* cx, HandleObject obj, HandleId id,
    837                                Handle<PropertyDescriptor> desc,
    838                                ObjectOpResult& result) {
    839  MOZ_ASSERT(!IsInternalDotName(cx, id));
    840  RootedObject actual(cx, &obj->as<WithEnvironmentObject>().object());
    841  return DefineProperty(cx, actual, id, desc, result);
    842 }
    843 
    844 static bool with_HasProperty(JSContext* cx, HandleObject obj, HandleId id,
    845                             bool* foundp) {
    846  MOZ_ASSERT(!IsInternalDotName(cx, id));
    847  RootedObject actual(cx, &obj->as<WithEnvironmentObject>().object());
    848 
    849  // ES 8.1.1.2.1 step 3-5.
    850  if (!HasProperty(cx, actual, id, foundp)) {
    851    return false;
    852  }
    853  if (!*foundp || !obj->as<WithEnvironmentObject>().supportUnscopables()) {
    854    return true;
    855  }
    856 
    857  // Steps 7-10. (Step 6 is a no-op.)
    858  return CheckUnscopables(cx, actual, id, foundp);
    859 }
    860 
    861 static bool with_GetProperty(JSContext* cx, HandleObject obj,
    862                             HandleValue receiver, HandleId id,
    863                             MutableHandleValue vp) {
    864  MOZ_ASSERT(!IsInternalDotName(cx, id));
    865  RootedObject actual(cx, &obj->as<WithEnvironmentObject>().object());
    866  RootedValue actualReceiver(cx, receiver);
    867  if (receiver.isObject() && &receiver.toObject() == obj) {
    868    actualReceiver.setObject(*actual);
    869  }
    870  return GetProperty(cx, actual, actualReceiver, id, vp);
    871 }
    872 
    873 static bool with_SetProperty(JSContext* cx, HandleObject obj, HandleId id,
    874                             HandleValue v, HandleValue receiver,
    875                             ObjectOpResult& result) {
    876  MOZ_ASSERT(!IsInternalDotName(cx, id));
    877  RootedObject actual(cx, &obj->as<WithEnvironmentObject>().object());
    878  RootedValue actualReceiver(cx, receiver);
    879  if (receiver.isObject() && &receiver.toObject() == obj) {
    880    actualReceiver.setObject(*actual);
    881  }
    882  return SetProperty(cx, actual, id, v, actualReceiver, result);
    883 }
    884 
    885 static bool with_GetOwnPropertyDescriptor(
    886    JSContext* cx, HandleObject obj, HandleId id,
    887    MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) {
    888  MOZ_ASSERT(!IsInternalDotName(cx, id));
    889  RootedObject actual(cx, &obj->as<WithEnvironmentObject>().object());
    890  return GetOwnPropertyDescriptor(cx, actual, id, desc);
    891 }
    892 
    893 static bool with_DeleteProperty(JSContext* cx, HandleObject obj, HandleId id,
    894                                ObjectOpResult& result) {
    895  MOZ_ASSERT(!IsInternalDotName(cx, id));
    896  RootedObject actual(cx, &obj->as<WithEnvironmentObject>().object());
    897  return DeleteProperty(cx, actual, id, result);
    898 }
    899 
    900 static const ObjectOps WithEnvironmentObjectOps = {
    901    with_LookupProperty,            // lookupProperty
    902    with_DefineProperty,            // defineProperty
    903    with_HasProperty,               // hasProperty
    904    with_GetProperty,               // getProperty
    905    with_SetProperty,               // setProperty
    906    with_GetOwnPropertyDescriptor,  // getOwnPropertyDescriptor
    907    with_DeleteProperty,            // deleteProperty
    908    nullptr,                        // getElements
    909    nullptr,                        // funToString
    910 };
    911 
    912 const JSClass WithEnvironmentObject::class_ = {
    913    "With",
    914    JSCLASS_HAS_RESERVED_SLOTS(WithEnvironmentObject::RESERVED_SLOTS),
    915    JS_NULL_CLASS_OPS,
    916    JS_NULL_CLASS_SPEC,
    917    JS_NULL_CLASS_EXT,
    918    &WithEnvironmentObjectOps,
    919 };
    920 
    921 /* static */
    922 NonSyntacticVariablesObject* NonSyntacticVariablesObject::create(
    923    JSContext* cx) {
    924  Rooted<SharedShape*> shape(
    925      cx, EmptyEnvironmentShape<NonSyntacticVariablesObject>(cx));
    926  if (!shape) {
    927    return nullptr;
    928  }
    929 
    930  NonSyntacticVariablesObject* obj =
    931      CreateEnvironmentObject<NonSyntacticVariablesObject>(cx, shape,
    932                                                           TenuredObject);
    933  if (!obj) {
    934    return nullptr;
    935  }
    936 
    937  // An NVSO holds both variables qualified with `var` and those that are not.
    938  MOZ_ASSERT(obj->isUnqualifiedVarObj());
    939  MOZ_ASSERT(obj->isQualifiedVarObj());
    940 
    941  obj->initEnclosingEnvironment(&cx->global()->lexicalEnvironment());
    942  return obj;
    943 }
    944 
    945 const JSClass NonSyntacticVariablesObject::class_ = {
    946    "NonSyntacticVariablesObject",
    947    JSCLASS_HAS_RESERVED_SLOTS(NonSyntacticVariablesObject::RESERVED_SLOTS),
    948 };
    949 
    950 NonSyntacticLexicalEnvironmentObject* js::CreateNonSyntacticEnvironmentChain(
    951    JSContext* cx, const JS::EnvironmentChain& envChain) {
    952  // Callers are responsible for segregating the NonSyntactic case from simple
    953  // compilation cases.
    954  MOZ_RELEASE_ASSERT(!envChain.empty());
    955 
    956  RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment());
    957  Rooted<WithEnvironmentObject*> env(
    958      cx, CreateObjectsForEnvironmentChain(cx, envChain, globalLexical));
    959  if (!env) {
    960    return nullptr;
    961  }
    962 
    963  // The XPConnect subscript loader, which may pass in its own
    964  // environments to load scripts in, expects the environment chain to
    965  // be the holder of "var" declarations. In SpiderMonkey, such objects
    966  // are called "qualified varobjs", the "qualified" part meaning the
    967  // declaration was qualified by "var". There is only sadness.
    968  //
    969  // See JSObject::isQualifiedVarObj.
    970  if (!JSObject::setQualifiedVarObj(cx, env)) {
    971    return nullptr;
    972  }
    973 
    974  // Also get a non-syntactic lexical environment to capture 'let' and
    975  // 'const' bindings. To persist lexical bindings, we have a 1-1
    976  // mapping with the final unwrapped environment object (the
    977  // environment that stores the 'var' bindings) and the lexical
    978  // environment.
    979  //
    980  // TODOshu: disallow the subscript loader from using non-distinguished
    981  // objects as dynamic scopes.
    982  return ObjectRealm::get(env).getOrCreateNonSyntacticLexicalEnvironment(cx,
    983                                                                         env);
    984 }
    985 
    986 /*****************************************************************************/
    987 
    988 const JSClass LexicalEnvironmentObject::class_ = {
    989    "LexicalEnvironment",
    990    JSCLASS_HAS_RESERVED_SLOTS(LexicalEnvironmentObject::RESERVED_SLOTS),
    991    JS_NULL_CLASS_OPS,
    992    JS_NULL_CLASS_SPEC,
    993    JS_NULL_CLASS_EXT,
    994    JS_NULL_OBJECT_OPS,
    995 };
    996 
    997 /* static */
    998 LexicalEnvironmentObject* LexicalEnvironmentObject::create(
    999    JSContext* cx, Handle<SharedShape*> shape, HandleObject enclosing,
   1000    gc::Heap heap, gc::AllocSite* site) {
   1001  MOZ_ASSERT(shape->getObjectClass() == &LexicalEnvironmentObject::class_);
   1002 
   1003  // The JITs assume the result is nursery allocated unless we collected the
   1004  // nursery, so don't change |heap| here.
   1005 
   1006  auto* env =
   1007      CreateEnvironmentObject<LexicalEnvironmentObject>(cx, shape, heap, site);
   1008  if (!env) {
   1009    return nullptr;
   1010  }
   1011 
   1012  MOZ_ASSERT(!env->inDictionaryMode());
   1013 
   1014  if (enclosing) {
   1015    env->initEnclosingEnvironment(enclosing);
   1016  }
   1017 
   1018 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
   1019  env->initSlot(LexicalEnvironmentObject::DISPOSABLE_RESOURCE_STACK_SLOT,
   1020                UndefinedValue());
   1021 #endif
   1022 
   1023  return env;
   1024 }
   1025 
   1026 bool LexicalEnvironmentObject::isExtensible() const {
   1027  return NativeObject::isExtensible();
   1028 }
   1029 
   1030 /* static */
   1031 BlockLexicalEnvironmentObject* BlockLexicalEnvironmentObject::create(
   1032    JSContext* cx, Handle<LexicalScope*> scope, HandleObject enclosing,
   1033    gc::Heap heap, gc::AllocSite* site) {
   1034  cx->check(enclosing);
   1035  MOZ_ASSERT(scope->hasEnvironment());
   1036 
   1037  Rooted<SharedShape*> shape(cx, scope->environmentShape());
   1038  auto* env = static_cast<BlockLexicalEnvironmentObject*>(
   1039      LexicalEnvironmentObject::create(cx, shape, enclosing, heap, site));
   1040  if (!env) {
   1041    return nullptr;
   1042  }
   1043 
   1044  // All lexical bindings start off uninitialized for TDZ.
   1045  uint32_t lastSlot = env->getLastProperty().slot();
   1046  for (uint32_t slot = JSSLOT_FREE(&class_); slot <= lastSlot; slot++) {
   1047    env->initSlot(slot, MagicValue(JS_UNINITIALIZED_LEXICAL));
   1048  }
   1049 
   1050  env->initScope(scope);
   1051  return env;
   1052 }
   1053 
   1054 /* static */
   1055 BlockLexicalEnvironmentObject* BlockLexicalEnvironmentObject::createForFrame(
   1056    JSContext* cx, Handle<LexicalScope*> scope, AbstractFramePtr frame) {
   1057  RootedObject enclosing(cx, frame.environmentChain());
   1058  return create(cx, scope, enclosing, gc::Heap::Default);
   1059 }
   1060 
   1061 /* static */
   1062 BlockLexicalEnvironmentObject*
   1063 BlockLexicalEnvironmentObject::createHollowForDebug(
   1064    JSContext* cx, Handle<LexicalScope*> scope) {
   1065  MOZ_ASSERT(!scope->hasEnvironment());
   1066 
   1067  Rooted<SharedShape*> shape(
   1068      cx, LexicalScope::getEmptyExtensibleEnvironmentShape(cx));
   1069  if (!shape) {
   1070    return nullptr;
   1071  }
   1072 
   1073  // This environment's enclosing link is never used: the
   1074  // DebugEnvironmentProxy that refers to this scope carries its own
   1075  // enclosing link, which is what Debugger uses to construct the tree of
   1076  // Debugger.Environment objects.
   1077  RootedObject enclosingEnv(cx, &cx->global()->lexicalEnvironment());
   1078  Rooted<LexicalEnvironmentObject*> env(
   1079      cx, LexicalEnvironmentObject::create(cx, shape, enclosingEnv,
   1080                                           gc::Heap::Tenured));
   1081  if (!env) {
   1082    return nullptr;
   1083  }
   1084 
   1085  RootedValue optimizedOut(cx, MagicValue(JS_OPTIMIZED_OUT));
   1086  RootedId id(cx);
   1087  for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++) {
   1088    id = NameToId(bi.name()->asPropertyName());
   1089    if (!SetProperty(cx, env, id, optimizedOut)) {
   1090      return nullptr;
   1091    }
   1092  }
   1093 
   1094  if (!JSObject::setFlag(cx, env, ObjectFlag::NotExtensible)) {
   1095    return nullptr;
   1096  }
   1097 
   1098  env->as<ScopedLexicalEnvironmentObject>().initScope(scope);
   1099  return &env->as<BlockLexicalEnvironmentObject>();
   1100 }
   1101 
   1102 /* static */
   1103 BlockLexicalEnvironmentObject*
   1104 BlockLexicalEnvironmentObject::createTemplateObject(
   1105    JSContext* cx, Handle<LexicalScope*> scope) {
   1106  return create(cx, scope, nullptr, gc::Heap::Tenured);
   1107 }
   1108 
   1109 /* static */
   1110 BlockLexicalEnvironmentObject*
   1111 BlockLexicalEnvironmentObject::createWithoutEnclosing(
   1112    JSContext* cx, Handle<LexicalScope*> scope) {
   1113  return create(cx, scope, nullptr, gc::Heap::Default);
   1114 }
   1115 
   1116 /* static */
   1117 BlockLexicalEnvironmentObject* BlockLexicalEnvironmentObject::clone(
   1118    JSContext* cx, Handle<BlockLexicalEnvironmentObject*> env) {
   1119  Rooted<LexicalScope*> scope(cx, &env->scope());
   1120  RootedObject enclosing(cx, &env->enclosingEnvironment());
   1121  BlockLexicalEnvironmentObject* copy =
   1122      create(cx, scope, enclosing, gc::Heap::Default);
   1123  if (!copy) {
   1124    return nullptr;
   1125  }
   1126 
   1127  MOZ_ASSERT(env->shape() == copy->shape());
   1128  for (uint32_t i = JSSLOT_FREE(&class_); i < copy->slotSpan(); i++) {
   1129    copy->setSlot(i, env->getSlot(i));
   1130  }
   1131 
   1132  return copy;
   1133 }
   1134 
   1135 /* static */
   1136 BlockLexicalEnvironmentObject* BlockLexicalEnvironmentObject::recreate(
   1137    JSContext* cx, Handle<BlockLexicalEnvironmentObject*> env) {
   1138  Rooted<LexicalScope*> scope(cx, &env->scope());
   1139  RootedObject enclosing(cx, &env->enclosingEnvironment());
   1140  return create(cx, scope, enclosing, gc::Heap::Default);
   1141 }
   1142 
   1143 /* static */
   1144 NamedLambdaObject* NamedLambdaObject::create(JSContext* cx,
   1145                                             HandleFunction callee,
   1146                                             HandleObject enclosing,
   1147                                             gc::Heap heap,
   1148                                             gc::AllocSite* site) {
   1149  MOZ_ASSERT(callee->isNamedLambda());
   1150  Rooted<Scope*> scope(cx, callee->nonLazyScript()->maybeNamedLambdaScope());
   1151  MOZ_ASSERT(scope && scope->environmentShape());
   1152 
   1153 #ifdef DEBUG
   1154  {
   1155    // Named lambda objects have one (non-writable) property.
   1156    SharedShapePropertyIter<NoGC> iter(scope->environmentShape());
   1157    MOZ_ASSERT(iter->slot() == lambdaSlot());
   1158    MOZ_ASSERT(!iter->writable());
   1159    iter++;
   1160    MOZ_ASSERT(iter.done());
   1161 
   1162    // There should be exactly one binding in the named lambda scope.
   1163    BindingIter bi(scope);
   1164    bi++;
   1165    MOZ_ASSERT(bi.done());
   1166  }
   1167 #endif
   1168 
   1169  BlockLexicalEnvironmentObject* obj = BlockLexicalEnvironmentObject::create(
   1170      cx, scope.as<LexicalScope>(), enclosing, heap, site);
   1171  if (!obj) {
   1172    return nullptr;
   1173  }
   1174 
   1175  obj->initFixedSlot(lambdaSlot(), ObjectValue(*callee));
   1176  return static_cast<NamedLambdaObject*>(obj);
   1177 }
   1178 
   1179 /* static */
   1180 NamedLambdaObject* NamedLambdaObject::createTemplateObject(
   1181    JSContext* cx, HandleFunction callee) {
   1182  return create(cx, callee, nullptr, gc::Heap::Tenured);
   1183 }
   1184 
   1185 /* static */
   1186 NamedLambdaObject* NamedLambdaObject::createWithoutEnclosing(
   1187    JSContext* cx, HandleFunction callee, gc::Heap heap) {
   1188  return create(cx, callee, nullptr, heap);
   1189 }
   1190 
   1191 /* static */
   1192 NamedLambdaObject* NamedLambdaObject::createForFrame(JSContext* cx,
   1193                                                     AbstractFramePtr frame,
   1194                                                     gc::AllocSite* site) {
   1195  RootedFunction fun(cx, frame.callee());
   1196  RootedObject enclosing(cx, frame.environmentChain());
   1197  gc::Heap heap = site ? site->initialHeap() : gc::Heap::Default;
   1198  return create(cx, fun, enclosing, heap, site);
   1199 }
   1200 
   1201 /* static */
   1202 size_t NamedLambdaObject::lambdaSlot() {
   1203  // Named lambda environments have exactly one name.
   1204  return JSSLOT_FREE(&LexicalEnvironmentObject::class_);
   1205 }
   1206 
   1207 /* static */
   1208 ClassBodyLexicalEnvironmentObject* ClassBodyLexicalEnvironmentObject::create(
   1209    JSContext* cx, Handle<ClassBodyScope*> scope, HandleObject enclosing,
   1210    gc::Heap heap) {
   1211  cx->check(enclosing);
   1212  MOZ_ASSERT(scope->hasEnvironment());
   1213 
   1214  Rooted<SharedShape*> shape(cx, scope->environmentShape());
   1215  auto* env = static_cast<ClassBodyLexicalEnvironmentObject*>(
   1216      LexicalEnvironmentObject::create(cx, shape, enclosing, heap));
   1217  if (!env) {
   1218    return nullptr;
   1219  }
   1220 
   1221  env->initScope(scope);
   1222  return env;
   1223 }
   1224 
   1225 /* static */
   1226 ClassBodyLexicalEnvironmentObject*
   1227 ClassBodyLexicalEnvironmentObject::createForFrame(JSContext* cx,
   1228                                                  Handle<ClassBodyScope*> scope,
   1229                                                  AbstractFramePtr frame) {
   1230  RootedObject enclosing(cx, frame.environmentChain());
   1231  return create(cx, scope, enclosing, gc::Heap::Default);
   1232 }
   1233 
   1234 /* static */
   1235 ClassBodyLexicalEnvironmentObject*
   1236 ClassBodyLexicalEnvironmentObject::createTemplateObject(
   1237    JSContext* cx, Handle<ClassBodyScope*> scope) {
   1238  return create(cx, scope, nullptr, gc::Heap::Tenured);
   1239 }
   1240 
   1241 /* static */
   1242 ClassBodyLexicalEnvironmentObject*
   1243 ClassBodyLexicalEnvironmentObject::createWithoutEnclosing(
   1244    JSContext* cx, Handle<ClassBodyScope*> scope) {
   1245  return create(cx, scope, nullptr, gc::Heap::Default);
   1246 }
   1247 
   1248 JSObject* ExtensibleLexicalEnvironmentObject::thisObject() const {
   1249  JSObject* obj = &getReservedSlot(THIS_VALUE_OR_SCOPE_SLOT).toObject();
   1250 
   1251  // Windows must never be exposed to script. initThisObject should have set
   1252  // this to the WindowProxy.
   1253  MOZ_ASSERT(!IsWindow(obj));
   1254 
   1255  // WarpBuilder relies on the return value not being nursery-allocated for the
   1256  // global lexical environment.
   1257  MOZ_ASSERT_IF(isGlobal(), obj->isTenured());
   1258 
   1259  return obj;
   1260 }
   1261 
   1262 /* static */
   1263 ExtensibleLexicalEnvironmentObject*
   1264 ExtensibleLexicalEnvironmentObject::forVarEnvironment(JSObject* obj) {
   1265  ExtensibleLexicalEnvironmentObject* lexical = nullptr;
   1266  if (obj->is<GlobalObject>()) {
   1267    lexical = &obj->as<GlobalObject>().lexicalEnvironment();
   1268  } else {
   1269    lexical = ObjectRealm::get(obj).getNonSyntacticLexicalEnvironment(obj);
   1270  }
   1271  MOZ_ASSERT(lexical);
   1272  return lexical;
   1273 }
   1274 
   1275 /* static */
   1276 GlobalLexicalEnvironmentObject* GlobalLexicalEnvironmentObject::create(
   1277    JSContext* cx, Handle<GlobalObject*> global) {
   1278  MOZ_ASSERT(global);
   1279 
   1280  Rooted<SharedShape*> shape(
   1281      cx, LexicalScope::getEmptyExtensibleEnvironmentShape(cx));
   1282  if (!shape) {
   1283    return nullptr;
   1284  }
   1285 
   1286  Rooted<GlobalLexicalEnvironmentObject*> env(cx);
   1287  env = static_cast<GlobalLexicalEnvironmentObject*>(
   1288      LexicalEnvironmentObject::create(cx, shape, global, gc::Heap::Tenured));
   1289  if (!env) {
   1290    return nullptr;
   1291  }
   1292 
   1293  if (ShouldUseObjectFuses() && JS::Prefs::objectfuse_for_global()) {
   1294    if (!NativeObject::setHasObjectFuse(cx, env)) {
   1295      return nullptr;
   1296    }
   1297  }
   1298 
   1299  env->initThisObject(global);
   1300  return env;
   1301 }
   1302 
   1303 void GlobalLexicalEnvironmentObject::setWindowProxyThisObject(JSObject* obj) {
   1304  MOZ_ASSERT(IsWindowProxy(obj));
   1305  setReservedSlot(THIS_VALUE_OR_SCOPE_SLOT, ObjectValue(*obj));
   1306 }
   1307 
   1308 /* static */
   1309 NonSyntacticLexicalEnvironmentObject*
   1310 NonSyntacticLexicalEnvironmentObject::create(JSContext* cx,
   1311                                             HandleObject enclosing,
   1312                                             HandleObject thisv) {
   1313  MOZ_ASSERT(enclosing);
   1314  MOZ_ASSERT(!IsSyntacticEnvironment(enclosing));
   1315 
   1316  Rooted<SharedShape*> shape(
   1317      cx, LexicalScope::getEmptyExtensibleEnvironmentShape(cx));
   1318  if (!shape) {
   1319    return nullptr;
   1320  }
   1321 
   1322  auto* env = static_cast<NonSyntacticLexicalEnvironmentObject*>(
   1323      LexicalEnvironmentObject::create(cx, shape, enclosing,
   1324                                       gc::Heap::Tenured));
   1325  if (!env) {
   1326    return nullptr;
   1327  }
   1328 
   1329  env->initThisObject(thisv);
   1330 
   1331  return env;
   1332 }
   1333 
   1334 /* static */
   1335 RuntimeLexicalErrorObject* RuntimeLexicalErrorObject::create(
   1336    JSContext* cx, HandleObject enclosing, unsigned errorNumber) {
   1337  Rooted<SharedShape*> shape(
   1338      cx,
   1339      EmptyEnvironmentShape(cx, &class_, JSSLOT_FREE(&class_), ObjectFlags()));
   1340  if (!shape) {
   1341    return nullptr;
   1342  }
   1343 
   1344  auto* obj = CreateEnvironmentObject<RuntimeLexicalErrorObject>(cx, shape);
   1345  if (!obj) {
   1346    return nullptr;
   1347  }
   1348  obj->initEnclosingEnvironment(enclosing);
   1349  obj->initReservedSlot(ERROR_SLOT, Int32Value(int32_t(errorNumber)));
   1350 
   1351  return obj;
   1352 }
   1353 
   1354 static void ReportRuntimeLexicalErrorId(JSContext* cx, unsigned errorNumber,
   1355                                        HandleId id) {
   1356  if (id.isAtom()) {
   1357    Rooted<PropertyName*> name(cx, id.toAtom()->asPropertyName());
   1358    ReportRuntimeLexicalError(cx, errorNumber, name);
   1359    return;
   1360  }
   1361  MOZ_CRASH(
   1362      "RuntimeLexicalErrorObject should only be used with property names");
   1363 }
   1364 
   1365 static bool lexicalError_LookupProperty(JSContext* cx, HandleObject obj,
   1366                                        HandleId id, MutableHandleObject objp,
   1367                                        PropertyResult* propp) {
   1368  ReportRuntimeLexicalErrorId(
   1369      cx, obj->as<RuntimeLexicalErrorObject>().errorNumber(), id);
   1370  return false;
   1371 }
   1372 
   1373 static bool lexicalError_HasProperty(JSContext* cx, HandleObject obj,
   1374                                     HandleId id, bool* foundp) {
   1375  ReportRuntimeLexicalErrorId(
   1376      cx, obj->as<RuntimeLexicalErrorObject>().errorNumber(), id);
   1377  return false;
   1378 }
   1379 
   1380 static bool lexicalError_GetProperty(JSContext* cx, HandleObject obj,
   1381                                     HandleValue receiver, HandleId id,
   1382                                     MutableHandleValue vp) {
   1383  ReportRuntimeLexicalErrorId(
   1384      cx, obj->as<RuntimeLexicalErrorObject>().errorNumber(), id);
   1385  return false;
   1386 }
   1387 
   1388 static bool lexicalError_SetProperty(JSContext* cx, HandleObject obj,
   1389                                     HandleId id, HandleValue v,
   1390                                     HandleValue receiver,
   1391                                     ObjectOpResult& result) {
   1392  ReportRuntimeLexicalErrorId(
   1393      cx, obj->as<RuntimeLexicalErrorObject>().errorNumber(), id);
   1394  return false;
   1395 }
   1396 
   1397 static bool lexicalError_GetOwnPropertyDescriptor(
   1398    JSContext* cx, HandleObject obj, HandleId id,
   1399    MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) {
   1400  ReportRuntimeLexicalErrorId(
   1401      cx, obj->as<RuntimeLexicalErrorObject>().errorNumber(), id);
   1402  return false;
   1403 }
   1404 
   1405 static bool lexicalError_DeleteProperty(JSContext* cx, HandleObject obj,
   1406                                        HandleId id, ObjectOpResult& result) {
   1407  ReportRuntimeLexicalErrorId(
   1408      cx, obj->as<RuntimeLexicalErrorObject>().errorNumber(), id);
   1409  return false;
   1410 }
   1411 
   1412 static const ObjectOps RuntimeLexicalErrorObjectObjectOps = {
   1413    lexicalError_LookupProperty,            // lookupProperty
   1414    nullptr,                                // defineProperty
   1415    lexicalError_HasProperty,               // hasProperty
   1416    lexicalError_GetProperty,               // getProperty
   1417    lexicalError_SetProperty,               // setProperty
   1418    lexicalError_GetOwnPropertyDescriptor,  // getOwnPropertyDescriptor
   1419    lexicalError_DeleteProperty,            // deleteProperty
   1420    nullptr,                                // getElements
   1421    nullptr,                                // funToString
   1422 };
   1423 
   1424 const JSClass RuntimeLexicalErrorObject::class_ = {
   1425    "RuntimeLexicalError",
   1426    JSCLASS_HAS_RESERVED_SLOTS(RuntimeLexicalErrorObject::RESERVED_SLOTS),
   1427    JS_NULL_CLASS_OPS,
   1428    JS_NULL_CLASS_SPEC,
   1429    JS_NULL_CLASS_EXT,
   1430    &RuntimeLexicalErrorObjectObjectOps,
   1431 };
   1432 
   1433 /*****************************************************************************/
   1434 
   1435 EnvironmentIter::EnvironmentIter(JSContext* cx, const EnvironmentIter& ei)
   1436    : si_(cx, ei.si_.get()), env_(cx, ei.env_), frame_(ei.frame_) {}
   1437 
   1438 EnvironmentIter::EnvironmentIter(JSContext* cx, JSObject* env, Scope* scope)
   1439    : si_(cx, ScopeIter(scope)), env_(cx, env), frame_(NullFramePtr()) {
   1440  settle();
   1441 }
   1442 
   1443 EnvironmentIter::EnvironmentIter(JSContext* cx, AbstractFramePtr frame,
   1444                                 const jsbytecode* pc)
   1445    : si_(cx, frame.script()->innermostScope(pc)),
   1446      env_(cx, frame.environmentChain()),
   1447      frame_(frame) {
   1448  cx->check(frame);
   1449  settle();
   1450 }
   1451 
   1452 EnvironmentIter::EnvironmentIter(JSContext* cx, JSObject* env, Scope* scope,
   1453                                 AbstractFramePtr frame)
   1454    : si_(cx, ScopeIter(scope)), env_(cx, env), frame_(frame) {
   1455  cx->check(frame);
   1456  settle();
   1457 }
   1458 
   1459 void EnvironmentIter::incrementScopeIter() {
   1460  if (si_.scope()->is<GlobalScope>()) {
   1461    // GlobalScopes may be syntactic or non-syntactic. Non-syntactic
   1462    // GlobalScopes correspond to zero or more non-syntactic
   1463    // EnvironmentsObjects followed by the global lexical scope, then the
   1464    // GlobalObject or another non-EnvironmentObject object.
   1465    if (!env_->is<EnvironmentObject>()) {
   1466      si_++;
   1467    }
   1468  } else {
   1469    si_++;
   1470  }
   1471 }
   1472 
   1473 void EnvironmentIter::settle() {
   1474  // Check for trying to iterate a function or eval frame before the prologue
   1475  // has created the CallObject, in which case we have to skip.
   1476  if (frame_ && frame_.hasScript() &&
   1477      frame_.script()->initialEnvironmentShape() &&
   1478      !frame_.hasInitialEnvironment()) {
   1479    // Skip until we're at the enclosing scope of the script.
   1480    while (si_.scope() != frame_.script()->enclosingScope()) {
   1481      if (env_->is<BlockLexicalEnvironmentObject>() &&
   1482          &env_->as<BlockLexicalEnvironmentObject>().scope() == si_.scope()) {
   1483        MOZ_ASSERT(si_.kind() == ScopeKind::NamedLambda ||
   1484                   si_.kind() == ScopeKind::StrictNamedLambda);
   1485        env_ =
   1486            &env_->as<BlockLexicalEnvironmentObject>().enclosingEnvironment();
   1487      }
   1488      incrementScopeIter();
   1489    }
   1490  }
   1491 
   1492  // Check if we have left the extent of the initial frame after we've
   1493  // settled on a static scope.
   1494  if (frame_ &&
   1495      (!si_ ||
   1496       (frame_.hasScript() &&
   1497        si_.scope() == frame_.script()->enclosingScope()) ||
   1498       (frame_.isWasmDebugFrame() && !si_.scope()->is<WasmFunctionScope>()))) {
   1499    frame_ = NullFramePtr();
   1500  }
   1501 
   1502 #ifdef DEBUG
   1503  if (si_) {
   1504    if (hasSyntacticEnvironment()) {
   1505      Scope* scope = si_.scope();
   1506      if (scope->is<LexicalScope>()) {
   1507        MOZ_ASSERT(scope == &env_->as<BlockLexicalEnvironmentObject>().scope());
   1508      } else if (scope->is<FunctionScope>()) {
   1509        MOZ_ASSERT(scope->as<FunctionScope>().script() ==
   1510                   env_->as<CallObject>()
   1511                       .callee()
   1512                       .maybeCanonicalFunction()
   1513                       ->baseScript());
   1514      } else if (scope->is<VarScope>()) {
   1515        MOZ_ASSERT(scope == &env_->as<VarEnvironmentObject>().scope());
   1516      } else if (scope->is<WithScope>()) {
   1517        MOZ_ASSERT(scope == &env_->as<WithEnvironmentObject>().scope());
   1518      } else if (scope->is<EvalScope>()) {
   1519        MOZ_ASSERT(scope == &env_->as<VarEnvironmentObject>().scope());
   1520      } else if (scope->is<GlobalScope>()) {
   1521        MOZ_ASSERT(env_->is<GlobalObject>() ||
   1522                   env_->is<GlobalLexicalEnvironmentObject>());
   1523      }
   1524    } else if (hasNonSyntacticEnvironmentObject()) {
   1525      if (env_->is<LexicalEnvironmentObject>()) {
   1526        // The global lexical environment still encloses non-syntactic
   1527        // environment objects.
   1528        MOZ_ASSERT(env_->is<NonSyntacticLexicalEnvironmentObject>() ||
   1529                   env_->is<GlobalLexicalEnvironmentObject>());
   1530      } else if (env_->is<WithEnvironmentObject>()) {
   1531        MOZ_ASSERT(!env_->as<WithEnvironmentObject>().isSyntactic());
   1532      } else {
   1533        MOZ_ASSERT(env_->is<NonSyntacticVariablesObject>());
   1534      }
   1535    }
   1536  }
   1537 #endif
   1538 }
   1539 
   1540 JSObject& EnvironmentIter::enclosingEnvironment() const {
   1541  // As an engine invariant (maintained internally and asserted by Execute),
   1542  // EnvironmentObjects and non-EnvironmentObjects cannot be interleaved on
   1543  // the scope chain; every scope chain must start with zero or more
   1544  // EnvironmentObjects and terminate with one or more
   1545  // non-EnvironmentObjects (viz., GlobalObject).
   1546  MOZ_ASSERT(done());
   1547  MOZ_ASSERT(!env_->is<EnvironmentObject>());
   1548  return *env_;
   1549 }
   1550 
   1551 bool EnvironmentIter::hasNonSyntacticEnvironmentObject() const {
   1552  // The case we're worrying about here is a NonSyntactic static scope which
   1553  // has 0+ corresponding non-syntactic WithEnvironmentObject scopes, a
   1554  // NonSyntacticVariablesObject, or a NonSyntacticLexicalEnvironmentObject.
   1555  if (si_.kind() == ScopeKind::NonSyntactic) {
   1556    MOZ_ASSERT_IF(env_->is<WithEnvironmentObject>(),
   1557                  !env_->as<WithEnvironmentObject>().isSyntactic());
   1558    return env_->is<EnvironmentObject>();
   1559  }
   1560  return false;
   1561 }
   1562 
   1563 bool MissingEnvironmentKey::initFromEnvironmentIter(JSContext* cx,
   1564                                                    const EnvironmentIter& ei) {
   1565  frame_ = ei.maybeInitialFrame();
   1566  scope_ = ei.maybeScope();
   1567  if (frame_) {
   1568    nearestEnvId_ = 0;
   1569    return true;
   1570  }
   1571 
   1572  EnvironmentObject* env = nullptr;
   1573  EnvironmentIter copy(cx, ei);
   1574  while (copy) {
   1575    if (copy.hasAnyEnvironmentObject()) {
   1576      env = &copy.environment();
   1577      break;
   1578    }
   1579    ++copy;
   1580  }
   1581 
   1582  // In general, we should find an environment object for the global etc even
   1583  // if we don't find anything else.
   1584  //
   1585  // In certain situation where OOM and too much recursion happens and the
   1586  // debugger is trying to recover from it, we might not find anything, and in
   1587  // that case, there's nothing we can do. (see bug 1976630).
   1588  if (!env) {
   1589    ReportOutOfMemory(cx);
   1590    return false;
   1591  }
   1592 
   1593  if (!gc::GetOrCreateUniqueId(env, &nearestEnvId_)) {
   1594    ReportOutOfMemory(cx);
   1595    return false;
   1596  }
   1597 
   1598  return true;
   1599 }
   1600 
   1601 /* static */
   1602 HashNumber MissingEnvironmentKey::hash(MissingEnvironmentKey ek) {
   1603  return mozilla::HashGeneric(ek.frame_.raw(), ek.nearestEnvId_, ek.scope_);
   1604 }
   1605 
   1606 /* static */
   1607 bool MissingEnvironmentKey::match(MissingEnvironmentKey ek1,
   1608                                  MissingEnvironmentKey ek2) {
   1609  return ek1.frame_ == ek2.frame_ && ek1.nearestEnvId_ == ek2.nearestEnvId_ &&
   1610         ek1.scope_ == ek2.scope_;
   1611 }
   1612 
   1613 bool LiveEnvironmentVal::traceWeak(JSTracer* trc) {
   1614  return TraceWeakEdge(trc, &scope_, "LiveEnvironmentVal::scope_");
   1615 }
   1616 
   1617 // Live EnvironmentIter values may be added to DebugEnvironments::liveEnvs, as
   1618 // LiveEnvironmentVal instances.  They need to have write barriers when they are
   1619 // added to the hash table, but no barriers when rehashing inside GC.  It's a
   1620 // nasty hack, but the important thing is that LiveEnvironmentVal and
   1621 // MissingEnvironmentKey need to alias each other.
   1622 void LiveEnvironmentVal::staticAsserts() {
   1623  static_assert(
   1624      sizeof(LiveEnvironmentVal) == sizeof(MissingEnvironmentKey),
   1625      "LiveEnvironmentVal must be same size of MissingEnvironmentKey");
   1626  static_assert(
   1627      offsetof(LiveEnvironmentVal, scope_) ==
   1628          offsetof(MissingEnvironmentKey, scope_),
   1629      "LiveEnvironmentVal.scope_ must alias MissingEnvironmentKey.scope_");
   1630 }
   1631 
   1632 /*****************************************************************************/
   1633 
   1634 namespace {
   1635 
   1636 /*
   1637 * DebugEnvironmentProxy is the handler for DebugEnvironmentProxy proxy
   1638 * objects. Having a custom handler (rather than trying to reuse js::Wrapper)
   1639 * gives us several important abilities:
   1640 *  - We want to pass the EnvironmentObject as the receiver to forwarded scope
   1641 *    property ops on aliased variables so that Call/Block/With ops do not all
   1642 *    require a 'normalization' step.
   1643 *  - The debug scope proxy can directly manipulate the stack frame to allow
   1644 *    the debugger to read/write args/locals that were otherwise unaliased.
   1645 *  - The debug scope proxy can store unaliased variables after the stack frame
   1646 *    is popped so that they may still be read/written by the debugger.
   1647 *  - The engine has made certain assumptions about the possible reads/writes
   1648 *    in a scope. DebugEnvironmentProxy allows us to prevent the debugger from
   1649 *    breaking those assumptions.
   1650 *  - The engine makes optimizations that are observable to the debugger. The
   1651 *    proxy can either hide these optimizations or make the situation more
   1652 *    clear to the debugger. An example is 'arguments'.
   1653 */
   1654 class DebugEnvironmentProxyHandler : public NurseryAllocableProxyHandler {
   1655  enum Action { SET, GET };
   1656 
   1657  enum AccessResult { ACCESS_UNALIASED, ACCESS_GENERIC, ACCESS_LOST };
   1658 
   1659  /*
   1660   * This function handles access to unaliased locals/formals. Since they
   1661   * are unaliased, the values of these variables are not stored in the
   1662   * slots of the normal CallObject and BlockLexicalEnvironmentObject
   1663   * environments and thus must be recovered from somewhere else:
   1664   *  + if the invocation for which the env was created is still executing,
   1665   *    there is a JS frame live on the stack holding the values;
   1666   *  + if the invocation for which the env was created finished executing:
   1667   *     - and there was a DebugEnvironmentProxy associated with env, then
   1668   *       the DebugEnvironments::onPop(Call|Lexical) handler copied out the
   1669   *       unaliased variables. In both cases, a dense array is created in
   1670   *       onPop(Call|Lexical) to hold the unaliased values and attached to
   1671   *       the DebugEnvironmentProxy;
   1672   *     - and there was not a DebugEnvironmentProxy yet associated with the
   1673   *       scope, then the unaliased values are lost and not recoverable.
   1674   *
   1675   * Callers should check accessResult for non-failure results:
   1676   *  - ACCESS_UNALIASED if the access was unaliased and completed
   1677   *  - ACCESS_GENERIC   if the access was aliased or the property not found
   1678   *  - ACCESS_LOST      if the value has been lost to the debugger and the
   1679   *                     action is GET; if the action is SET, we assign to the
   1680   *                     name of the variable on the environment object
   1681   */
   1682  bool handleUnaliasedAccess(JSContext* cx,
   1683                             Handle<DebugEnvironmentProxy*> debugEnv,
   1684                             Handle<EnvironmentObject*> env, HandleId id,
   1685                             Action action, MutableHandleValue vp,
   1686                             AccessResult* accessResult) const {
   1687    MOZ_ASSERT(&debugEnv->environment() == env);
   1688    MOZ_ASSERT_IF(action == SET, !debugEnv->isOptimizedOut());
   1689    *accessResult = ACCESS_GENERIC;
   1690    LiveEnvironmentVal* maybeLiveEnv =
   1691        DebugEnvironments::hasLiveEnvironment(*env);
   1692 
   1693    // Handle unaliased formals, vars, lets, and consts at function or module
   1694    // scope.
   1695    if (env->is<CallObject>() || env->is<ModuleEnvironmentObject>()) {
   1696      RootedScript script(cx);
   1697      if (env->is<CallObject>()) {
   1698        CallObject& callobj = env->as<CallObject>();
   1699        RootedFunction fun(cx, &callobj.callee());
   1700        script = JSFunction::getOrCreateScript(cx, fun);
   1701      } else {
   1702        script = env->as<ModuleEnvironmentObject>().module().maybeScript();
   1703        if (!script) {
   1704          return true;
   1705        }
   1706      }
   1707 
   1708      BindingIter bi(script);
   1709      while (bi && NameToId(bi.name()->asPropertyName()) != id) {
   1710        bi++;
   1711      }
   1712      if (!bi) {
   1713        return true;
   1714      }
   1715 
   1716      if (action == SET && bi.kind() == BindingKind::Const) {
   1717        ReportRuntimeLexicalError(cx, JSMSG_BAD_CONST_ASSIGN, id);
   1718        return false;
   1719      }
   1720 
   1721      if (bi.location().kind() == BindingLocation::Kind::Import) {
   1722        return true;
   1723      }
   1724 
   1725      if (!bi.hasArgumentSlot()) {
   1726        if (bi.closedOver()) {
   1727          return true;
   1728        }
   1729 
   1730        uint32_t i = bi.location().slot();
   1731        if (maybeLiveEnv) {
   1732          AbstractFramePtr frame = maybeLiveEnv->frame();
   1733          if (action == GET) {
   1734            vp.set(frame.unaliasedLocal(i));
   1735          } else {
   1736            frame.unaliasedLocal(i) = vp;
   1737          }
   1738        } else if (AbstractGeneratorObject* genObj =
   1739                       GetGeneratorObjectForEnvironment(cx, env);
   1740                   genObj && genObj->isSuspended() &&
   1741                   genObj->hasStackStorage()) {
   1742          if (action == GET) {
   1743            vp.set(genObj->getUnaliasedLocal(i));
   1744          } else {
   1745            genObj->setUnaliasedLocal(i, vp);
   1746          }
   1747        } else if (NativeObject* snapshot = debugEnv->maybeSnapshot()) {
   1748          if (action == GET) {
   1749            vp.set(snapshot->getDenseElement(script->numArgs() + i));
   1750          } else {
   1751            snapshot->setDenseElement(script->numArgs() + i, vp);
   1752          }
   1753        } else {
   1754          /* The unaliased value has been lost to the debugger. */
   1755          if (action == GET) {
   1756            *accessResult = ACCESS_LOST;
   1757            return true;
   1758          }
   1759        }
   1760      } else {
   1761        unsigned i = bi.argumentSlot();
   1762        if (bi.closedOver()) {
   1763          return true;
   1764        }
   1765 
   1766        if (maybeLiveEnv) {
   1767          AbstractFramePtr frame = maybeLiveEnv->frame();
   1768          if (script->argsObjAliasesFormals() && frame.hasArgsObj()) {
   1769            if (action == GET) {
   1770              vp.set(frame.argsObj().arg(i));
   1771            } else {
   1772              frame.argsObj().setArg(i, vp);
   1773            }
   1774          } else {
   1775            if (action == GET) {
   1776              vp.set(frame.unaliasedFormal(i, DONT_CHECK_ALIASING));
   1777            } else {
   1778              frame.unaliasedFormal(i, DONT_CHECK_ALIASING) = vp;
   1779            }
   1780          }
   1781        } else if (NativeObject* snapshot = debugEnv->maybeSnapshot()) {
   1782          if (action == GET) {
   1783            vp.set(snapshot->getDenseElement(i));
   1784          } else {
   1785            snapshot->setDenseElement(i, vp);
   1786          }
   1787        } else {
   1788          /* The unaliased value has been lost to the debugger. */
   1789          if (action == GET) {
   1790            *accessResult = ACCESS_LOST;
   1791            return true;
   1792          }
   1793        }
   1794      }
   1795 
   1796      // It is possible that an optimized out value flows to this
   1797      // location due to Debugger.Frame.prototype.eval operating on a
   1798      // live bailed-out Baseline frame. In that case, treat the access
   1799      // as lost.
   1800      if (vp.isMagic() && vp.whyMagic() == JS_OPTIMIZED_OUT) {
   1801        *accessResult = ACCESS_LOST;
   1802      } else {
   1803        *accessResult = ACCESS_UNALIASED;
   1804      }
   1805 
   1806      return true;
   1807    }
   1808 
   1809    /*
   1810     * Handle unaliased vars in functions with parameter expressions and
   1811     * lexical bindings at block scope.
   1812     */
   1813    if (env->is<LexicalEnvironmentObject>() ||
   1814        env->is<VarEnvironmentObject>()) {
   1815      // Currently consider all non-syntactic top-level lexical bindings to be
   1816      // aliased.
   1817      if (env->is<LexicalEnvironmentObject>() &&
   1818          !env->is<GlobalLexicalEnvironmentObject>() &&
   1819          env->as<LexicalEnvironmentObject>().isExtensible()) {
   1820        MOZ_ASSERT(!IsSyntacticEnvironment(env));
   1821        return true;
   1822      }
   1823 
   1824      // Currently all vars inside non-strict eval var environments are aliased.
   1825      if (env->is<VarEnvironmentObject>() &&
   1826          env->as<VarEnvironmentObject>().isForNonStrictEval()) {
   1827        return true;
   1828      }
   1829 
   1830      Rooted<Scope*> scope(cx, getEnvironmentScope(*env));
   1831      uint32_t firstFrameSlot = scope->firstFrameSlot();
   1832 
   1833      BindingIter bi(scope);
   1834      while (bi && NameToId(bi.name()->asPropertyName()) != id) {
   1835        bi++;
   1836      }
   1837      if (!bi) {
   1838        return true;
   1839      }
   1840 
   1841      if (action == SET && bi.kind() == BindingKind::Const) {
   1842        ReportRuntimeLexicalError(cx, JSMSG_BAD_CONST_ASSIGN, id);
   1843        return false;
   1844      }
   1845 
   1846      BindingLocation loc = bi.location();
   1847      if (loc.kind() == BindingLocation::Kind::Environment) {
   1848        return true;
   1849      }
   1850 
   1851      // Named lambdas that are not closed over are lost.
   1852      if (loc.kind() == BindingLocation::Kind::NamedLambdaCallee) {
   1853        if (action == GET) {
   1854          *accessResult = ACCESS_LOST;
   1855        }
   1856        return true;
   1857      }
   1858 
   1859      MOZ_ASSERT(loc.kind() == BindingLocation::Kind::Frame);
   1860 
   1861      if (maybeLiveEnv) {
   1862        AbstractFramePtr frame = maybeLiveEnv->frame();
   1863        uint32_t local = loc.slot();
   1864        MOZ_ASSERT(local < frame.script()->nfixed());
   1865        Value& localVal = frame.unaliasedLocal(local);
   1866        if (action == GET) {
   1867          vp.set(localVal);
   1868        } else {
   1869          // Note: localVal could also be JS_OPTIMIZED_OUT.
   1870          if (localVal.isMagic() &&
   1871              localVal.whyMagic() == JS_UNINITIALIZED_LEXICAL) {
   1872            ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, id);
   1873            return false;
   1874          }
   1875 
   1876          localVal = vp;
   1877        }
   1878      } else if (AbstractGeneratorObject* genObj =
   1879                     GetGeneratorObjectForEnvironment(cx, debugEnv);
   1880                 genObj && genObj->isSuspended() && genObj->hasStackStorage()) {
   1881        if (action == GET) {
   1882          vp.set(genObj->getUnaliasedLocal(loc.slot()));
   1883        } else {
   1884          genObj->setUnaliasedLocal(loc.slot(), vp);
   1885        }
   1886      } else if (NativeObject* snapshot = debugEnv->maybeSnapshot()) {
   1887        // Indices in the frame snapshot are offset by the first frame
   1888        // slot. See DebugEnvironments::takeFrameSnapshot.
   1889        MOZ_ASSERT(loc.slot() >= firstFrameSlot);
   1890        uint32_t snapshotIndex = loc.slot() - firstFrameSlot;
   1891        if (action == GET) {
   1892          vp.set(snapshot->getDenseElement(snapshotIndex));
   1893        } else {
   1894          snapshot->setDenseElement(snapshotIndex, vp);
   1895        }
   1896      } else {
   1897        if (action == GET) {
   1898          // A {Lexical,Var}EnvironmentObject whose static scope
   1899          // does not have an environment shape at all is a "hollow"
   1900          // block object reflected for missing block scopes. Their
   1901          // slot values are lost.
   1902          if (!scope->hasEnvironment()) {
   1903            *accessResult = ACCESS_LOST;
   1904            return true;
   1905          }
   1906 
   1907          if (!GetProperty(cx, env, env, id, vp)) {
   1908            return false;
   1909          }
   1910        } else {
   1911          if (!SetProperty(cx, env, id, vp)) {
   1912            return false;
   1913          }
   1914        }
   1915      }
   1916 
   1917      // See comment above in analogous CallObject case.
   1918      if (vp.isMagic() && vp.whyMagic() == JS_OPTIMIZED_OUT) {
   1919        *accessResult = ACCESS_LOST;
   1920      } else {
   1921        *accessResult = ACCESS_UNALIASED;
   1922      }
   1923 
   1924      return true;
   1925    }
   1926 
   1927    if (env->is<WasmFunctionCallObject>()) {
   1928      if (maybeLiveEnv) {
   1929        Rooted<Scope*> scope(cx, getEnvironmentScope(*env));
   1930        uint32_t index = 0;
   1931        for (BindingIter bi(scope); bi; bi++) {
   1932          if (id.isAtom(bi.name())) {
   1933            break;
   1934          }
   1935          MOZ_ASSERT(!bi.isLast());
   1936          index++;
   1937        }
   1938 
   1939        AbstractFramePtr frame = maybeLiveEnv->frame();
   1940        MOZ_ASSERT(frame.isWasmDebugFrame());
   1941        wasm::DebugFrame* wasmFrame = frame.asWasmDebugFrame();
   1942        if (action == GET) {
   1943          if (!wasmFrame->getLocal(index, vp)) {
   1944            ReportOutOfMemory(cx);
   1945            return false;
   1946          }
   1947          *accessResult = ACCESS_UNALIASED;
   1948        } else {  // if (action == SET)
   1949                  // TODO
   1950        }
   1951      } else {
   1952        *accessResult = ACCESS_LOST;
   1953      }
   1954      return true;
   1955    }
   1956 
   1957    if (env->is<WasmInstanceEnvironmentObject>()) {
   1958      Rooted<Scope*> scope(cx, getEnvironmentScope(*env));
   1959      MOZ_ASSERT(scope->is<WasmInstanceScope>());
   1960      uint32_t index = 0;
   1961      for (BindingIter bi(scope); bi; bi++) {
   1962        if (id.isAtom(bi.name())) {
   1963          break;
   1964        }
   1965        MOZ_ASSERT(!bi.isLast());
   1966        index++;
   1967      }
   1968      Rooted<WasmInstanceScope*> instanceScope(cx,
   1969                                               &scope->as<WasmInstanceScope>());
   1970      wasm::Instance& instance = instanceScope->instance()->instance();
   1971 
   1972      if (action == GET) {
   1973        if (instanceScope->memoriesStart() <= index &&
   1974            index < instanceScope->globalsStart()) {
   1975          vp.set(ObjectValue(
   1976              *instance.memory(index - instanceScope->memoriesStart())));
   1977        }
   1978        if (instanceScope->globalsStart() <= index) {
   1979          MOZ_ASSERT(index < instanceScope->namesCount());
   1980          if (!instance.debug().getGlobal(
   1981                  instance, index - instanceScope->globalsStart(), vp)) {
   1982            ReportOutOfMemory(cx);
   1983            return false;
   1984          }
   1985        }
   1986        *accessResult = ACCESS_UNALIASED;
   1987      } else {  // if (action == SET)
   1988                // TODO
   1989      }
   1990      return true;
   1991    }
   1992 
   1993    /* The rest of the internal scopes do not have unaliased vars. */
   1994    MOZ_ASSERT(!IsSyntacticEnvironment(env) ||
   1995               env->is<WithEnvironmentObject>());
   1996    return true;
   1997  }
   1998 
   1999  static bool isArguments(JSContext* cx, jsid id) {
   2000    return id == NameToId(cx->names().arguments);
   2001  }
   2002  static bool isThis(JSContext* cx, jsid id) {
   2003    return id == NameToId(cx->names().dot_this_);
   2004  }
   2005 
   2006  static bool isFunctionEnvironment(const JSObject& env) {
   2007    return env.is<CallObject>();
   2008  }
   2009 
   2010  static bool isNonExtensibleLexicalEnvironment(const JSObject& env) {
   2011    return env.is<ScopedLexicalEnvironmentObject>();
   2012  }
   2013 
   2014  static Scope* getEnvironmentScope(const JSObject& env) {
   2015    if (isFunctionEnvironment(env)) {
   2016      return env.as<CallObject>().callee().nonLazyScript()->bodyScope();
   2017    }
   2018    if (env.is<ModuleEnvironmentObject>()) {
   2019      JSScript* script =
   2020          env.as<ModuleEnvironmentObject>().module().maybeScript();
   2021      return script ? script->bodyScope() : nullptr;
   2022    }
   2023    if (isNonExtensibleLexicalEnvironment(env)) {
   2024      return &env.as<ScopedLexicalEnvironmentObject>().scope();
   2025    }
   2026    if (env.is<VarEnvironmentObject>()) {
   2027      return &env.as<VarEnvironmentObject>().scope();
   2028    }
   2029    if (env.is<WasmInstanceEnvironmentObject>()) {
   2030      return &env.as<WasmInstanceEnvironmentObject>().scope();
   2031    }
   2032    if (env.is<WasmFunctionCallObject>()) {
   2033      return &env.as<WasmFunctionCallObject>().scope();
   2034    }
   2035    if (env.is<GlobalLexicalEnvironmentObject>()) {
   2036      return &env.as<GlobalLexicalEnvironmentObject>()
   2037                  .global()
   2038                  .emptyGlobalScope();
   2039    }
   2040    return nullptr;
   2041  }
   2042 
   2043  friend Scope* js::GetEnvironmentScope(const JSObject& env);
   2044 
   2045  /*
   2046   * In theory, every non-arrow function scope contains an 'arguments'
   2047   * bindings.  However, the engine only adds a binding if 'arguments' is
   2048   * used in the function body. Thus, from the debugger's perspective,
   2049   * 'arguments' may be missing from the list of bindings.
   2050   */
   2051  static bool isMissingArgumentsBinding(EnvironmentObject& env) {
   2052    return isFunctionEnvironment(env) &&
   2053           !env.as<CallObject>().callee().baseScript()->needsArgsObj();
   2054  }
   2055 
   2056  /*
   2057   * Similar to 'arguments' above, we don't add a 'this' binding to
   2058   * non-arrow functions if it's not used.
   2059   */
   2060  static bool isMissingThisBinding(EnvironmentObject& env) {
   2061    return isFunctionEnvironmentWithThis(env) &&
   2062           !env.as<CallObject>()
   2063                .callee()
   2064                .baseScript()
   2065                ->functionHasThisBinding();
   2066  }
   2067 
   2068  /*
   2069   * This function checks if an arguments object needs to be created when
   2070   * the debugger requests 'arguments' for a function scope where the
   2071   * arguments object was not otherwise needed.
   2072   */
   2073  static bool isMissingArguments(JSContext* cx, jsid id,
   2074                                 EnvironmentObject& env) {
   2075    return isArguments(cx, id) && isMissingArgumentsBinding(env);
   2076  }
   2077  static bool isMissingThis(JSContext* cx, jsid id, EnvironmentObject& env) {
   2078    return isThis(cx, id) && isMissingThisBinding(env);
   2079  }
   2080 
   2081  /*
   2082   * If the value of |this| is requested before the this-binding has been
   2083   * initialized by JSOp::FunctionThis, the this-binding will be |undefined|.
   2084   * In that case, we have to call createMissingThis to initialize the
   2085   * this-binding.
   2086   *
   2087   * Note that an |undefined| this-binding is perfectly valid in strict-mode
   2088   * code, but that's fine: createMissingThis will do the right thing in that
   2089   * case.
   2090   */
   2091  static bool isMaybeUninitializedThisValue(JSContext* cx, jsid id,
   2092                                            const Value& v) {
   2093    return isThis(cx, id) && v.isUndefined();
   2094  }
   2095 
   2096  /*
   2097   * Create a missing arguments object. If the function returns true but
   2098   * argsObj is null, it means the env is dead.
   2099   */
   2100  static bool createMissingArguments(JSContext* cx, EnvironmentObject& env,
   2101                                     MutableHandle<ArgumentsObject*> argsObj) {
   2102    argsObj.set(nullptr);
   2103 
   2104    LiveEnvironmentVal* maybeEnv = DebugEnvironments::hasLiveEnvironment(env);
   2105    if (!maybeEnv) {
   2106      return true;
   2107    }
   2108 
   2109    argsObj.set(ArgumentsObject::createUnexpected(cx, maybeEnv->frame()));
   2110    return !!argsObj;
   2111  }
   2112 
   2113  /*
   2114   * Create a missing this Value. If the function returns true but
   2115   * *success is false, it means the scope is dead.
   2116   */
   2117  static bool createMissingThis(JSContext* cx, EnvironmentObject& env,
   2118                                MutableHandleValue thisv, bool* success) {
   2119    *success = false;
   2120 
   2121    LiveEnvironmentVal* maybeEnv = DebugEnvironments::hasLiveEnvironment(env);
   2122    if (!maybeEnv) {
   2123      return true;
   2124    }
   2125 
   2126    AbstractFramePtr frame = maybeEnv->frame();
   2127    if (!GetFunctionThis(cx, frame, thisv)) {
   2128      return false;
   2129    }
   2130 
   2131    // Update the this-argument to avoid boxing primitive |this| more
   2132    // than once.
   2133    frame.thisArgument() = thisv;
   2134    *success = true;
   2135    return true;
   2136  }
   2137 
   2138  static void reportOptimizedOut(JSContext* cx, HandleId id) {
   2139    if (isThis(cx, id)) {
   2140      JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   2141                               JSMSG_DEBUG_OPTIMIZED_OUT, "this");
   2142      return;
   2143    }
   2144 
   2145    if (UniqueChars printable =
   2146            IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsIdentifier)) {
   2147      JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   2148                               JSMSG_DEBUG_OPTIMIZED_OUT, printable.get());
   2149    }
   2150  }
   2151 
   2152 public:
   2153  static const char family;
   2154  static const DebugEnvironmentProxyHandler singleton;
   2155 
   2156  constexpr DebugEnvironmentProxyHandler()
   2157      : NurseryAllocableProxyHandler(&family) {}
   2158 
   2159  static bool isFunctionEnvironmentWithThis(const JSObject& env) {
   2160    // All functions except arrows should have their own this binding.
   2161    return isFunctionEnvironment(env) &&
   2162           !env.as<CallObject>().callee().hasLexicalThis();
   2163  }
   2164 
   2165  bool getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy,
   2166                              bool* isOrdinary,
   2167                              MutableHandleObject protop) const override {
   2168    MOZ_CRASH(
   2169        "shouldn't be possible to access the prototype chain of a "
   2170        "DebugEnvironmentProxyHandler");
   2171  }
   2172 
   2173  bool preventExtensions(JSContext* cx, HandleObject proxy,
   2174                         ObjectOpResult& result) const override {
   2175    // always [[Extensible]], can't be made non-[[Extensible]], like most
   2176    // proxies
   2177    return result.fail(JSMSG_CANT_CHANGE_EXTENSIBILITY);
   2178  }
   2179 
   2180  bool isExtensible(JSContext* cx, HandleObject proxy,
   2181                    bool* extensible) const override {
   2182    // See above.
   2183    *extensible = true;
   2184    return true;
   2185  }
   2186 
   2187  bool getMissingArgumentsPropertyDescriptor(
   2188      JSContext* cx, Handle<DebugEnvironmentProxy*> debugEnv,
   2189      EnvironmentObject& env,
   2190      MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) const {
   2191    Rooted<ArgumentsObject*> argsObj(cx);
   2192    if (!createMissingArguments(cx, env, &argsObj)) {
   2193      return false;
   2194    }
   2195 
   2196    if (!argsObj) {
   2197      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   2198                                JSMSG_DEBUG_NOT_ON_STACK, "Debugger scope");
   2199      return false;
   2200    }
   2201 
   2202    desc.set(mozilla::Some(PropertyDescriptor::Data(
   2203        ObjectValue(*argsObj), {JS::PropertyAttribute::Enumerable})));
   2204    return true;
   2205  }
   2206  bool getMissingThisPropertyDescriptor(
   2207      JSContext* cx, Handle<DebugEnvironmentProxy*> debugEnv,
   2208      EnvironmentObject& env,
   2209      MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) const {
   2210    RootedValue thisv(cx);
   2211    bool success;
   2212    if (!createMissingThis(cx, env, &thisv, &success)) {
   2213      return false;
   2214    }
   2215 
   2216    if (!success) {
   2217      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   2218                                JSMSG_DEBUG_NOT_ON_STACK, "Debugger scope");
   2219      return false;
   2220    }
   2221 
   2222    desc.set(mozilla::Some(
   2223        PropertyDescriptor::Data(thisv, {JS::PropertyAttribute::Enumerable})));
   2224    return true;
   2225  }
   2226 
   2227  bool getOwnPropertyDescriptor(
   2228      JSContext* cx, HandleObject proxy, HandleId id,
   2229      MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) const override {
   2230    Rooted<DebugEnvironmentProxy*> debugEnv(
   2231        cx, &proxy->as<DebugEnvironmentProxy>());
   2232    Rooted<EnvironmentObject*> env(cx, &debugEnv->environment());
   2233 
   2234    if (isMissingArguments(cx, id, *env)) {
   2235      return getMissingArgumentsPropertyDescriptor(cx, debugEnv, *env, desc);
   2236    }
   2237 
   2238    if (isMissingThis(cx, id, *env)) {
   2239      return getMissingThisPropertyDescriptor(cx, debugEnv, *env, desc);
   2240    }
   2241 
   2242    RootedValue v(cx);
   2243    AccessResult access;
   2244    if (!handleUnaliasedAccess(cx, debugEnv, env, id, GET, &v, &access)) {
   2245      return false;
   2246    }
   2247 
   2248    switch (access) {
   2249      case ACCESS_UNALIASED: {
   2250        desc.set(mozilla::Some(
   2251            PropertyDescriptor::Data(v, {JS::PropertyAttribute::Enumerable})));
   2252        return true;
   2253      }
   2254      case ACCESS_GENERIC:
   2255        return GetOwnPropertyDescriptor(cx, env, id, desc);
   2256      case ACCESS_LOST:
   2257        reportOptimizedOut(cx, id);
   2258        return false;
   2259      default:
   2260        MOZ_CRASH("bad AccessResult");
   2261    }
   2262  }
   2263 
   2264  bool getMissingArguments(JSContext* cx, EnvironmentObject& env,
   2265                           MutableHandleValue vp) const {
   2266    Rooted<ArgumentsObject*> argsObj(cx);
   2267    if (!createMissingArguments(cx, env, &argsObj)) {
   2268      return false;
   2269    }
   2270 
   2271    if (!argsObj) {
   2272      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   2273                                JSMSG_DEBUG_NOT_ON_STACK, "Debugger env");
   2274      return false;
   2275    }
   2276 
   2277    vp.setObject(*argsObj);
   2278    return true;
   2279  }
   2280 
   2281  bool getMissingThis(JSContext* cx, EnvironmentObject& env,
   2282                      MutableHandleValue vp) const {
   2283    RootedValue thisv(cx);
   2284    bool success;
   2285    if (!createMissingThis(cx, env, &thisv, &success)) {
   2286      return false;
   2287    }
   2288 
   2289    if (!success) {
   2290      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   2291                                JSMSG_DEBUG_NOT_ON_STACK, "Debugger env");
   2292      return false;
   2293    }
   2294 
   2295    vp.set(thisv);
   2296    return true;
   2297  }
   2298 
   2299  bool get(JSContext* cx, HandleObject proxy, HandleValue receiver, HandleId id,
   2300           MutableHandleValue vp) const override {
   2301    Rooted<DebugEnvironmentProxy*> debugEnv(
   2302        cx, &proxy->as<DebugEnvironmentProxy>());
   2303    Rooted<EnvironmentObject*> env(
   2304        cx, &proxy->as<DebugEnvironmentProxy>().environment());
   2305 
   2306    if (isMissingArguments(cx, id, *env)) {
   2307      return getMissingArguments(cx, *env, vp);
   2308    }
   2309 
   2310    if (isMissingThis(cx, id, *env)) {
   2311      return getMissingThis(cx, *env, vp);
   2312    }
   2313 
   2314    AccessResult access;
   2315    if (!handleUnaliasedAccess(cx, debugEnv, env, id, GET, vp, &access)) {
   2316      return false;
   2317    }
   2318 
   2319    switch (access) {
   2320      case ACCESS_UNALIASED:
   2321        if (isMaybeUninitializedThisValue(cx, id, vp)) {
   2322          return getMissingThis(cx, *env, vp);
   2323        }
   2324        return true;
   2325      case ACCESS_GENERIC:
   2326        if (!GetProperty(cx, env, env, id, vp)) {
   2327          return false;
   2328        }
   2329        if (isMaybeUninitializedThisValue(cx, id, vp)) {
   2330          return getMissingThis(cx, *env, vp);
   2331        }
   2332        return true;
   2333      case ACCESS_LOST:
   2334        reportOptimizedOut(cx, id);
   2335        return false;
   2336      default:
   2337        MOZ_CRASH("bad AccessResult");
   2338    }
   2339  }
   2340 
   2341  bool getMissingArgumentsMaybeSentinelValue(JSContext* cx,
   2342                                             EnvironmentObject& env,
   2343                                             MutableHandleValue vp) const {
   2344    Rooted<ArgumentsObject*> argsObj(cx);
   2345    if (!createMissingArguments(cx, env, &argsObj)) {
   2346      return false;
   2347    }
   2348    vp.set(argsObj ? ObjectValue(*argsObj) : MagicValue(JS_MISSING_ARGUMENTS));
   2349    return true;
   2350  }
   2351 
   2352  bool getMissingThisMaybeSentinelValue(JSContext* cx, EnvironmentObject& env,
   2353                                        MutableHandleValue vp) const {
   2354    RootedValue thisv(cx);
   2355    bool success;
   2356    if (!createMissingThis(cx, env, &thisv, &success)) {
   2357      return false;
   2358    }
   2359    vp.set(success ? thisv : MagicValue(JS_OPTIMIZED_OUT));
   2360    return true;
   2361  }
   2362 
   2363  /*
   2364   * Like 'get', but returns sentinel values instead of throwing on
   2365   * exceptional cases.
   2366   */
   2367  bool getMaybeSentinelValue(JSContext* cx,
   2368                             Handle<DebugEnvironmentProxy*> debugEnv,
   2369                             HandleId id, MutableHandleValue vp) const {
   2370    Rooted<EnvironmentObject*> env(cx, &debugEnv->environment());
   2371 
   2372    if (isMissingArguments(cx, id, *env)) {
   2373      return getMissingArgumentsMaybeSentinelValue(cx, *env, vp);
   2374    }
   2375    if (isMissingThis(cx, id, *env)) {
   2376      return getMissingThisMaybeSentinelValue(cx, *env, vp);
   2377    }
   2378 
   2379    AccessResult access;
   2380    if (!handleUnaliasedAccess(cx, debugEnv, env, id, GET, vp, &access)) {
   2381      return false;
   2382    }
   2383 
   2384    switch (access) {
   2385      case ACCESS_UNALIASED:
   2386        if (isMaybeUninitializedThisValue(cx, id, vp)) {
   2387          return getMissingThisMaybeSentinelValue(cx, *env, vp);
   2388        }
   2389        return true;
   2390      case ACCESS_GENERIC:
   2391        if (!GetProperty(cx, env, env, id, vp)) {
   2392          return false;
   2393        }
   2394        if (isMaybeUninitializedThisValue(cx, id, vp)) {
   2395          return getMissingThisMaybeSentinelValue(cx, *env, vp);
   2396        }
   2397        return true;
   2398      case ACCESS_LOST:
   2399        vp.setMagic(JS_OPTIMIZED_OUT);
   2400        return true;
   2401      default:
   2402        MOZ_CRASH("bad AccessResult");
   2403    }
   2404  }
   2405 
   2406  bool set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v,
   2407           HandleValue receiver, ObjectOpResult& result) const override {
   2408    Rooted<DebugEnvironmentProxy*> debugEnv(
   2409        cx, &proxy->as<DebugEnvironmentProxy>());
   2410    Rooted<EnvironmentObject*> env(
   2411        cx, &proxy->as<DebugEnvironmentProxy>().environment());
   2412 
   2413    if (debugEnv->isOptimizedOut()) {
   2414      return Throw(cx, id, JSMSG_DEBUG_CANT_SET_OPT_ENV);
   2415    }
   2416 
   2417    AccessResult access;
   2418    RootedValue valCopy(cx, v);
   2419    if (!handleUnaliasedAccess(cx, debugEnv, env, id, SET, &valCopy, &access)) {
   2420      return false;
   2421    }
   2422 
   2423    switch (access) {
   2424      case ACCESS_UNALIASED:
   2425        return result.succeed();
   2426      case ACCESS_GENERIC: {
   2427        RootedValue envVal(cx, ObjectValue(*env));
   2428        RootedValue initialVal(cx);
   2429        if (!GetProperty(cx, env, env, id, &initialVal)) {
   2430          return false;
   2431        }
   2432        // Note: initialVal could be JS_OPTIMIZED_OUT, which is why we don't use
   2433        // .whyMagic(JS_UNINITALIZED_LEXICAL).
   2434        if (initialVal.isMagic() &&
   2435            initialVal.whyMagic() == JS_UNINITIALIZED_LEXICAL) {
   2436          ReportRuntimeLexicalErrorId(cx, JSMSG_UNINITIALIZED_LEXICAL, id);
   2437          return false;
   2438        }
   2439 
   2440        return SetProperty(cx, env, id, v, envVal, result);
   2441      }
   2442      default:
   2443        MOZ_CRASH("bad AccessResult");
   2444    }
   2445  }
   2446 
   2447  bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
   2448                      Handle<PropertyDescriptor> desc,
   2449                      ObjectOpResult& result) const override {
   2450    Rooted<EnvironmentObject*> env(
   2451        cx, &proxy->as<DebugEnvironmentProxy>().environment());
   2452 
   2453    bool found;
   2454    if (!has(cx, proxy, id, &found)) {
   2455      return false;
   2456    }
   2457    if (found) {
   2458      return Throw(cx, id, JSMSG_CANT_REDEFINE_PROP);
   2459    }
   2460 
   2461    return JS_DefinePropertyById(cx, env, id, desc, result);
   2462  }
   2463 
   2464  bool ownPropertyKeys(JSContext* cx, HandleObject proxy,
   2465                       MutableHandleIdVector props) const override {
   2466    Rooted<EnvironmentObject*> env(
   2467        cx, &proxy->as<DebugEnvironmentProxy>().environment());
   2468 
   2469    if (isMissingArgumentsBinding(*env)) {
   2470      if (!props.append(NameToId(cx->names().arguments))) {
   2471        return false;
   2472      }
   2473    }
   2474    if (isMissingThisBinding(*env)) {
   2475      if (!props.append(NameToId(cx->names().dot_this_))) {
   2476        return false;
   2477      }
   2478    }
   2479 
   2480    // WithEnvironmentObject isn't a very good proxy.  It doesn't have a
   2481    // JSNewEnumerateOp implementation, because if it just delegated to the
   2482    // target object, the object would indicate that native enumeration is
   2483    // the thing to do, but native enumeration over the WithEnvironmentObject
   2484    // wrapper yields no properties.  So instead here we hack around the
   2485    // issue: punch a hole through to the with object target, then manually
   2486    // examine @@unscopables.
   2487    RootedObject target(cx);
   2488    bool isWith = env->is<WithEnvironmentObject>();
   2489    if (isWith) {
   2490      target = &env->as<WithEnvironmentObject>().object();
   2491    } else {
   2492      target = env;
   2493    }
   2494    if (!GetPropertyKeys(cx, target, JSITER_OWNONLY, props)) {
   2495      return false;
   2496    }
   2497 
   2498    if (isWith) {
   2499      size_t j = 0;
   2500      bool supportUnscopables =
   2501          env->as<WithEnvironmentObject>().supportUnscopables();
   2502      for (size_t i = 0; i < props.length(); i++) {
   2503        bool inScope = true;
   2504        if (supportUnscopables &&
   2505            !CheckUnscopables(cx, env, props[i], &inScope)) {
   2506          return false;
   2507        }
   2508        if (inScope) {
   2509          props[j++].set(props[i]);
   2510        }
   2511      }
   2512      if (!props.resize(j)) {
   2513        return false;
   2514      }
   2515    }
   2516 
   2517    /*
   2518     * Environments with Scopes are optimized to not contain unaliased
   2519     * variables so they must be manually appended here.
   2520     */
   2521    if (Scope* scope = getEnvironmentScope(*env)) {
   2522      for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++) {
   2523        if (!bi.closedOver() &&
   2524            !props.append(NameToId(bi.name()->asPropertyName()))) {
   2525          return false;
   2526        }
   2527      }
   2528    }
   2529 
   2530    return true;
   2531  }
   2532 
   2533  bool has(JSContext* cx, HandleObject proxy, HandleId id_,
   2534           bool* bp) const override {
   2535    RootedId id(cx, id_);
   2536    EnvironmentObject& envObj =
   2537        proxy->as<DebugEnvironmentProxy>().environment();
   2538 
   2539    if (isArguments(cx, id) && isFunctionEnvironment(envObj)) {
   2540      *bp = true;
   2541      return true;
   2542    }
   2543 
   2544    // Be careful not to look up '.this' as a normal binding below, it will
   2545    // assert in with_HasProperty.
   2546    if (isThis(cx, id)) {
   2547      *bp = isFunctionEnvironmentWithThis(envObj);
   2548      return true;
   2549    }
   2550 
   2551    bool found;
   2552    RootedObject env(cx, &envObj);
   2553    if (!JS_HasPropertyById(cx, env, id, &found)) {
   2554      return false;
   2555    }
   2556 
   2557    if (!found) {
   2558      if (Scope* scope = getEnvironmentScope(*env)) {
   2559        for (BindingIter bi(scope); bi; bi++) {
   2560          if (!bi.closedOver() && NameToId(bi.name()->asPropertyName()) == id) {
   2561            found = true;
   2562            break;
   2563          }
   2564        }
   2565      }
   2566    }
   2567 
   2568    *bp = found;
   2569    return true;
   2570  }
   2571 
   2572  bool delete_(JSContext* cx, HandleObject proxy, HandleId id,
   2573               ObjectOpResult& result) const override {
   2574    return result.fail(JSMSG_CANT_DELETE);
   2575  }
   2576 };
   2577 
   2578 } /* anonymous namespace */
   2579 
   2580 Scope* js::GetEnvironmentScope(const JSObject& env) {
   2581  return DebugEnvironmentProxyHandler::getEnvironmentScope(env);
   2582 }
   2583 
   2584 template <>
   2585 bool JSObject::is<js::DebugEnvironmentProxy>() const {
   2586  return IsDerivedProxyObject(this, &DebugEnvironmentProxyHandler::singleton);
   2587 }
   2588 
   2589 const char DebugEnvironmentProxyHandler::family = 0;
   2590 const DebugEnvironmentProxyHandler DebugEnvironmentProxyHandler::singleton;
   2591 
   2592 /* static */
   2593 DebugEnvironmentProxy* DebugEnvironmentProxy::create(JSContext* cx,
   2594                                                     EnvironmentObject& env,
   2595                                                     HandleObject enclosing) {
   2596  MOZ_ASSERT(env.realm() == cx->realm());
   2597  MOZ_ASSERT(!enclosing->is<EnvironmentObject>());
   2598 
   2599  RootedValue priv(cx, ObjectValue(env));
   2600  JSObject* obj = NewProxyObject(cx, &DebugEnvironmentProxyHandler::singleton,
   2601                                 priv, nullptr /* proto */);
   2602  if (!obj) {
   2603    return nullptr;
   2604  }
   2605 
   2606  DebugEnvironmentProxy* debugEnv = &obj->as<DebugEnvironmentProxy>();
   2607  debugEnv->setReservedSlot(ENCLOSING_SLOT, ObjectValue(*enclosing));
   2608  debugEnv->setReservedSlot(SNAPSHOT_SLOT, NullValue());
   2609 
   2610  return debugEnv;
   2611 }
   2612 
   2613 EnvironmentObject& DebugEnvironmentProxy::environment() const {
   2614  return target()->as<EnvironmentObject>();
   2615 }
   2616 
   2617 JSObject& DebugEnvironmentProxy::enclosingEnvironment() const {
   2618  return reservedSlot(ENCLOSING_SLOT).toObject();
   2619 }
   2620 
   2621 ArrayObject* DebugEnvironmentProxy::maybeSnapshot() const {
   2622  JSObject* obj = reservedSlot(SNAPSHOT_SLOT).toObjectOrNull();
   2623  return obj ? &obj->as<ArrayObject>() : nullptr;
   2624 }
   2625 
   2626 void DebugEnvironmentProxy::initSnapshot(ArrayObject& o) {
   2627 #ifdef DEBUG
   2628  if (maybeSnapshot()) {
   2629    auto* callObj = CallObject::find(&environment());
   2630    if (callObj) {
   2631      MOZ_ASSERT(callObj->callee().isGeneratorOrAsync());
   2632    } else {
   2633      auto* moduleEnv = ModuleEnvironmentObject::find(&environment());
   2634      MOZ_ASSERT(moduleEnv);
   2635      MOZ_ASSERT(moduleEnv->module().hasTopLevelAwait());
   2636    }
   2637  }
   2638 #endif
   2639 
   2640  setReservedSlot(SNAPSHOT_SLOT, ObjectValue(o));
   2641 }
   2642 
   2643 bool DebugEnvironmentProxy::isForDeclarative() const {
   2644  EnvironmentObject& e = environment();
   2645  return e.is<CallObject>() || e.is<VarEnvironmentObject>() ||
   2646         e.is<ModuleEnvironmentObject>() ||
   2647         e.is<WasmInstanceEnvironmentObject>() ||
   2648         e.is<WasmFunctionCallObject>() || e.is<LexicalEnvironmentObject>();
   2649 }
   2650 
   2651 /* static */
   2652 bool DebugEnvironmentProxy::getMaybeSentinelValue(
   2653    JSContext* cx, Handle<DebugEnvironmentProxy*> env, HandleId id,
   2654    MutableHandleValue vp) {
   2655  return DebugEnvironmentProxyHandler::singleton.getMaybeSentinelValue(cx, env,
   2656                                                                       id, vp);
   2657 }
   2658 
   2659 bool DebugEnvironmentProxy::isFunctionEnvironmentWithThis() {
   2660  return DebugEnvironmentProxyHandler::isFunctionEnvironmentWithThis(
   2661      environment());
   2662 }
   2663 
   2664 bool DebugEnvironmentProxy::isOptimizedOut() const {
   2665  EnvironmentObject& e = environment();
   2666 
   2667  if (DebugEnvironments::hasLiveEnvironment(e)) {
   2668    return false;
   2669  }
   2670 
   2671  if (e.is<LexicalEnvironmentObject>()) {
   2672    return e.is<BlockLexicalEnvironmentObject>() &&
   2673           !e.as<BlockLexicalEnvironmentObject>().scope().hasEnvironment();
   2674  }
   2675 
   2676  if (e.is<CallObject>()) {
   2677    return !e.as<CallObject>().callee().needsCallObject() && !maybeSnapshot();
   2678  }
   2679 
   2680  return false;
   2681 }
   2682 
   2683 /*****************************************************************************/
   2684 
   2685 [[nodiscard]] static bool GetFrameEnvironmentAndScope(
   2686    JSContext* cx, AbstractFramePtr frame, const jsbytecode* pc,
   2687    MutableHandleObject env, MutableHandle<Scope*> scope);
   2688 
   2689 DebugEnvironments::DebugEnvironments(JSContext* cx, Zone* zone)
   2690    : zone_(zone),
   2691      proxiedEnvs(cx),
   2692      missingEnvs(cx->zone()),
   2693      liveEnvs(cx->zone()) {}
   2694 
   2695 DebugEnvironments::~DebugEnvironments() { MOZ_ASSERT(missingEnvs.empty()); }
   2696 
   2697 void DebugEnvironments::trace(JSTracer* trc) { proxiedEnvs.trace(trc); }
   2698 
   2699 void DebugEnvironments::traceWeak(JSTracer* trc) {
   2700  /*
   2701   * missingEnvs points to debug envs weakly so that debug envs can be
   2702   * released more eagerly.
   2703   */
   2704  for (MissingEnvironmentMap::Enum e(missingEnvs); !e.empty(); e.popFront()) {
   2705    auto result =
   2706        TraceWeakEdge(trc, &e.front().value(), "MissingEnvironmentMap value");
   2707    if (result.isDead()) {
   2708      /*
   2709       * Note that onPopCall, onPopVar, and onPopLexical rely on missingEnvs to
   2710       * find environment objects that we synthesized for the debugger's sake,
   2711       * and clean up the synthetic environment objects' entries in liveEnvs.
   2712       * So if we remove an entry from missingEnvs here, we must also remove the
   2713       * corresponding liveEnvs entry.
   2714       *
   2715       * Since the DebugEnvironmentProxy is the only thing using its environment
   2716       * object, and the DSO is about to be finalized, you might assume that the
   2717       * synthetic SO is also about to be finalized too, and thus the loop below
   2718       * will take care of things. But complex GC behavior means that marks are
   2719       * only conservative approximations of liveness; we should assume that
   2720       * anything could be marked.
   2721       *
   2722       * Thus, we must explicitly remove the entries from both liveEnvs and
   2723       * missingEnvs here.
   2724       */
   2725      liveEnvs.remove(&result.initialTarget()->environment());
   2726      e.removeFront();
   2727    } else {
   2728      MissingEnvironmentKey key = e.front().key();
   2729      Scope* scope = key.scope();
   2730      MOZ_ALWAYS_TRUE(TraceManuallyBarrieredWeakEdge(
   2731          trc, &scope, "MissingEnvironmentKey scope"));
   2732      if (scope != key.scope()) {
   2733        key.updateScope(scope);
   2734        e.rekeyFront(key);
   2735      }
   2736    }
   2737  }
   2738 
   2739  /*
   2740   * Scopes can be finalized when a debugger-synthesized EnvironmentObject is
   2741   * no longer reachable via its DebugEnvironmentProxy.
   2742   */
   2743  liveEnvs.traceWeak(trc);
   2744 }
   2745 
   2746 void DebugEnvironments::finish() { proxiedEnvs.clear(); }
   2747 
   2748 #ifdef JSGC_HASH_TABLE_CHECKS
   2749 void DebugEnvironments::checkHashTablesAfterMovingGC() {
   2750  /*
   2751   * This is called at the end of StoreBuffer::mark() to check that our
   2752   * postbarriers have worked and that no hashtable keys (or values) are left
   2753   * pointing into the nursery.
   2754   *
   2755   * |proxiedEnvs| is checked automatically because it is a WeakMap.
   2756   */
   2757  CheckTableAfterMovingGC(missingEnvs, [this](const auto& entry) {
   2758    CheckGCThingAfterMovingGC(entry.key().scope(), zone());
   2759    // Use unbarrieredGet() to prevent triggering read barrier while collecting.
   2760    CheckGCThingAfterMovingGC(entry.value().unbarrieredGet(), zone());
   2761    return entry.key();
   2762  });
   2763  CheckTableAfterMovingGC(liveEnvs, [this](const auto& entry) {
   2764    CheckGCThingAfterMovingGC(entry.key(), zone());
   2765    CheckGCThingAfterMovingGC(entry.value().scope_.get(), zone());
   2766    return entry.key().unbarrieredGet();
   2767  });
   2768 }
   2769 #endif
   2770 
   2771 /*
   2772 * Unfortunately, GetDebugEnvironmentForFrame needs to work even outside debug
   2773 * mode (in particular, JS_GetFrameScopeChain does not require debug mode).
   2774 * Since DebugEnvironments::onPop* are only called in debuggee frames, this
   2775 * means we cannot use any of the maps in DebugEnvironments. This will produce
   2776 * debug scope chains that do not obey the debugger invariants but that is just
   2777 * fine.
   2778 */
   2779 static bool CanUseDebugEnvironmentMaps(JSContext* cx) {
   2780  return cx->realm()->isDebuggee();
   2781 }
   2782 
   2783 DebugEnvironments* DebugEnvironments::ensureRealmData(JSContext* cx) {
   2784  Realm* realm = cx->realm();
   2785  if (auto* debugEnvs = realm->debugEnvs()) {
   2786    return debugEnvs;
   2787  }
   2788 
   2789  auto debugEnvs = cx->make_unique<DebugEnvironments>(cx, cx->zone());
   2790  if (!debugEnvs) {
   2791    return nullptr;
   2792  }
   2793 
   2794  realm->debugEnvsRef() = std::move(debugEnvs);
   2795  return realm->debugEnvs();
   2796 }
   2797 
   2798 /* static */
   2799 DebugEnvironmentProxy* DebugEnvironments::hasDebugEnvironment(
   2800    JSContext* cx, EnvironmentObject& env) {
   2801  DebugEnvironments* envs = env.realm()->debugEnvs();
   2802  if (!envs) {
   2803    return nullptr;
   2804  }
   2805 
   2806  if (JSObject* obj = envs->proxiedEnvs.get(&env)) {
   2807    MOZ_ASSERT(CanUseDebugEnvironmentMaps(cx));
   2808    return &obj->as<DebugEnvironmentProxy>();
   2809  }
   2810 
   2811  return nullptr;
   2812 }
   2813 
   2814 /* static */
   2815 bool DebugEnvironments::addDebugEnvironment(
   2816    JSContext* cx, Handle<EnvironmentObject*> env,
   2817    Handle<DebugEnvironmentProxy*> debugEnv) {
   2818  MOZ_ASSERT(cx->realm() == env->realm());
   2819  MOZ_ASSERT(cx->realm() == debugEnv->nonCCWRealm());
   2820 
   2821  if (!CanUseDebugEnvironmentMaps(cx)) {
   2822    return true;
   2823  }
   2824 
   2825  DebugEnvironments* envs = ensureRealmData(cx);
   2826  if (!envs) {
   2827    return false;
   2828  }
   2829 
   2830  if (!envs->proxiedEnvs.put(env, debugEnv)) {
   2831    ReportOutOfMemory(cx);
   2832    return false;
   2833  }
   2834 
   2835  return true;
   2836 }
   2837 
   2838 /* static */
   2839 bool DebugEnvironments::getExistingDebugEnvironment(
   2840    JSContext* cx, const EnvironmentIter& ei, DebugEnvironmentProxy** out) {
   2841  MOZ_ASSERT(!ei.hasSyntacticEnvironment());
   2842 
   2843  DebugEnvironments* envs = cx->realm()->debugEnvs();
   2844  if (!envs) {
   2845    *out = nullptr;
   2846    return true;
   2847  }
   2848 
   2849  MissingEnvironmentKey key;
   2850  if (!key.initFromEnvironmentIter(cx, ei)) {
   2851    return false;
   2852  }
   2853 
   2854  if (MissingEnvironmentMap::Ptr p = envs->missingEnvs.lookup(key)) {
   2855    MOZ_ASSERT(CanUseDebugEnvironmentMaps(cx));
   2856    *out = p->value();
   2857    return true;
   2858  }
   2859 
   2860  *out = nullptr;
   2861  return true;
   2862 }
   2863 
   2864 /* static */
   2865 bool DebugEnvironments::addDebugEnvironment(
   2866    JSContext* cx, const EnvironmentIter& ei,
   2867    Handle<DebugEnvironmentProxy*> debugEnv) {
   2868  MOZ_ASSERT(!ei.hasSyntacticEnvironment());
   2869  MOZ_ASSERT(cx->realm() == debugEnv->nonCCWRealm());
   2870 
   2871  if (!CanUseDebugEnvironmentMaps(cx)) {
   2872    return true;
   2873  }
   2874 
   2875  DebugEnvironments* envs = ensureRealmData(cx);
   2876  if (!envs) {
   2877    return false;
   2878  }
   2879 
   2880  MissingEnvironmentKey key;
   2881  if (!key.initFromEnvironmentIter(cx, ei)) {
   2882    return false;
   2883  }
   2884  MOZ_ASSERT(!envs->missingEnvs.has(key));
   2885  if (!envs->missingEnvs.put(key,
   2886                             WeakHeapPtr<DebugEnvironmentProxy*>(debugEnv))) {
   2887    ReportOutOfMemory(cx);
   2888    return false;
   2889  }
   2890 
   2891  // Only add to liveEnvs if we synthesized the debug env on a live
   2892  // frame.
   2893  if (ei.withinInitialFrame()) {
   2894    MOZ_ASSERT(!envs->liveEnvs.has(&debugEnv->environment()));
   2895    if (!envs->liveEnvs.put(&debugEnv->environment(), LiveEnvironmentVal(ei))) {
   2896      ReportOutOfMemory(cx);
   2897      return false;
   2898    }
   2899  }
   2900 
   2901  return true;
   2902 }
   2903 
   2904 /* static */
   2905 void DebugEnvironments::takeFrameSnapshot(
   2906    JSContext* cx, Handle<DebugEnvironmentProxy*> debugEnv,
   2907    AbstractFramePtr frame) {
   2908  /*
   2909   * When the JS stack frame is popped, the values of unaliased variables
   2910   * are lost. If there is any debug env referring to this environment, save a
   2911   * copy of the unaliased variables' values in an array for later debugger
   2912   * access via DebugEnvironmentProxy::handleUnaliasedAccess.
   2913   *
   2914   * Note: since it is simplest for this function to be infallible, failure
   2915   * in this code will be silently ignored. This does not break any
   2916   * invariants since DebugEnvironmentProxy::maybeSnapshot can already be
   2917   * nullptr.
   2918   */
   2919 
   2920  // Because this can be called during exception unwinding, save the exception
   2921  // state and restore it when we're done.
   2922  JS::AutoSaveExceptionState ases(cx);
   2923 
   2924  JSScript* script = frame.script();
   2925 
   2926  // Act like no snapshot was taken if we run OOM while taking the snapshot.
   2927  Rooted<GCVector<Value>> vec(cx, GCVector<Value>(cx));
   2928  if (debugEnv->environment().is<CallObject>()) {
   2929    FunctionScope* scope = &script->bodyScope()->as<FunctionScope>();
   2930    uint32_t frameSlotCount = scope->nextFrameSlot();
   2931    MOZ_ASSERT(frameSlotCount <= script->nfixed());
   2932 
   2933    // For simplicity, copy all frame slots from 0 to the frameSlotCount,
   2934    // even if we don't need all of them (like in the case of a defaults
   2935    // parameter scope having frame slots).
   2936    uint32_t numFormals = frame.numFormalArgs();
   2937    if (!vec.resize(numFormals + frameSlotCount)) {
   2938      cx->recoverFromOutOfMemory();
   2939      return;
   2940    }
   2941    mozilla::PodCopy(vec.begin(), frame.argv(), numFormals);
   2942    for (uint32_t slot = 0; slot < frameSlotCount; slot++) {
   2943      vec[slot + frame.numFormalArgs()].set(frame.unaliasedLocal(slot));
   2944    }
   2945 
   2946    /*
   2947     * Copy in formals that are not aliased via the scope chain
   2948     * but are aliased via the arguments object.
   2949     */
   2950    if (script->needsArgsObj() && frame.hasArgsObj()) {
   2951      for (unsigned i = 0; i < frame.numFormalArgs(); ++i) {
   2952        if (script->formalLivesInArgumentsObject(i)) {
   2953          vec[i].set(frame.argsObj().arg(i));
   2954        }
   2955      }
   2956    }
   2957  } else {
   2958    uint32_t frameSlotStart;
   2959    uint32_t frameSlotEnd;
   2960 
   2961    if (debugEnv->environment().is<BlockLexicalEnvironmentObject>()) {
   2962      LexicalScope* scope =
   2963          &debugEnv->environment().as<BlockLexicalEnvironmentObject>().scope();
   2964      frameSlotStart = scope->firstFrameSlot();
   2965      frameSlotEnd = scope->nextFrameSlot();
   2966    } else if (debugEnv->environment()
   2967                   .is<ClassBodyLexicalEnvironmentObject>()) {
   2968      ClassBodyScope* scope = &debugEnv->environment()
   2969                                   .as<ClassBodyLexicalEnvironmentObject>()
   2970                                   .scope();
   2971      frameSlotStart = scope->firstFrameSlot();
   2972      frameSlotEnd = scope->nextFrameSlot();
   2973    } else if (debugEnv->environment().is<VarEnvironmentObject>()) {
   2974      VarEnvironmentObject* env =
   2975          &debugEnv->environment().as<VarEnvironmentObject>();
   2976      if (frame.isFunctionFrame()) {
   2977        VarScope* scope = &env->scope().as<VarScope>();
   2978        frameSlotStart = scope->firstFrameSlot();
   2979        frameSlotEnd = scope->nextFrameSlot();
   2980      } else {
   2981        EvalScope* scope = &env->scope().as<EvalScope>();
   2982        MOZ_ASSERT(scope == script->bodyScope());
   2983        frameSlotStart = 0;
   2984        frameSlotEnd = scope->nextFrameSlot();
   2985      }
   2986    } else {
   2987      MOZ_ASSERT(&debugEnv->environment().as<ModuleEnvironmentObject>() ==
   2988                 script->module()->environment());
   2989      ModuleScope* scope = &script->bodyScope()->as<ModuleScope>();
   2990      frameSlotStart = 0;
   2991      frameSlotEnd = scope->nextFrameSlot();
   2992    }
   2993 
   2994    uint32_t frameSlotCount = frameSlotEnd - frameSlotStart;
   2995    MOZ_ASSERT(frameSlotCount <= script->nfixed());
   2996 
   2997    if (!vec.resize(frameSlotCount)) {
   2998      cx->recoverFromOutOfMemory();
   2999      return;
   3000    }
   3001    for (uint32_t slot = frameSlotStart; slot < frameSlotCount; slot++) {
   3002      vec[slot - frameSlotStart].set(frame.unaliasedLocal(slot));
   3003    }
   3004  }
   3005 
   3006  if (vec.length() == 0) {
   3007    return;
   3008  }
   3009 
   3010  /*
   3011   * Use a dense array as storage (since proxies do not have trace
   3012   * hooks). This array must not escape into the wild.
   3013   */
   3014  Rooted<ArrayObject*> snapshot(
   3015      cx, NewDenseCopiedArray(cx, vec.length(), vec.begin()));
   3016  if (!snapshot) {
   3017    MOZ_ASSERT(cx->isThrowingOutOfMemory() || cx->isThrowingOverRecursed());
   3018    cx->clearPendingException();
   3019    return;
   3020  }
   3021 
   3022  debugEnv->initSnapshot(*snapshot);
   3023 }
   3024 
   3025 /* static */
   3026 void DebugEnvironments::onPopCall(JSContext* cx, AbstractFramePtr frame) {
   3027  cx->check(frame);
   3028 
   3029  DebugEnvironments* envs = cx->realm()->debugEnvs();
   3030  if (!envs) {
   3031    return;
   3032  }
   3033 
   3034  Rooted<DebugEnvironmentProxy*> debugEnv(cx, nullptr);
   3035 
   3036  FunctionScope* funScope = &frame.script()->bodyScope()->as<FunctionScope>();
   3037  if (funScope->hasEnvironment()) {
   3038    MOZ_ASSERT(frame.callee()->needsCallObject());
   3039 
   3040    /*
   3041     * The frame may be observed before the prologue has created the
   3042     * CallObject. See EnvironmentIter::settle.
   3043     */
   3044    if (!frame.environmentChain()->is<CallObject>()) {
   3045      return;
   3046    }
   3047 
   3048    CallObject& callobj = frame.environmentChain()->as<CallObject>();
   3049    envs->liveEnvs.remove(&callobj);
   3050    if (JSObject* obj = envs->proxiedEnvs.get(&callobj)) {
   3051      debugEnv = &obj->as<DebugEnvironmentProxy>();
   3052    }
   3053  } else {
   3054    MissingEnvironmentKey key(frame, funScope);
   3055    if (MissingEnvironmentMap::Ptr p = envs->missingEnvs.lookup(key)) {
   3056      debugEnv = p->value();
   3057      envs->liveEnvs.remove(&debugEnv->environment().as<CallObject>());
   3058      envs->missingEnvs.remove(p);
   3059    }
   3060  }
   3061 
   3062  if (debugEnv) {
   3063    DebugEnvironments::takeFrameSnapshot(cx, debugEnv, frame);
   3064  }
   3065 }
   3066 
   3067 void DebugEnvironments::onPopLexical(JSContext* cx, AbstractFramePtr frame,
   3068                                     const jsbytecode* pc) {
   3069  cx->check(frame);
   3070 
   3071  DebugEnvironments* envs = cx->realm()->debugEnvs();
   3072  if (!envs) {
   3073    return;
   3074  }
   3075 
   3076  EnvironmentIter ei(cx, frame, pc);
   3077  onPopLexical(cx, ei);
   3078 }
   3079 
   3080 template <typename Environment, typename Scope>
   3081 void DebugEnvironments::onPopGeneric(JSContext* cx, const EnvironmentIter& ei) {
   3082  DebugEnvironments* envs = cx->realm()->debugEnvs();
   3083  if (!envs) {
   3084    return;
   3085  }
   3086 
   3087  MOZ_ASSERT(ei.withinInitialFrame());
   3088  MOZ_ASSERT(ei.scope().is<Scope>());
   3089 
   3090  MissingEnvironmentKey key;
   3091  {
   3092    js::AutoEnterOOMUnsafeRegion oomUnsafe;
   3093    if (!key.initFromEnvironmentIter(cx, ei)) {
   3094      oomUnsafe.crash("OOM during onPopGeneric");
   3095      return;
   3096    }
   3097  }
   3098  Rooted<Environment*> env(cx);
   3099  if (MissingEnvironmentMap::Ptr p = envs->missingEnvs.lookup(key)) {
   3100    env = &p->value()->environment().as<Environment>();
   3101    envs->missingEnvs.remove(p);
   3102  } else if (ei.hasSyntacticEnvironment()) {
   3103    env = &ei.environment().as<Environment>();
   3104  }
   3105 
   3106  if (env) {
   3107    envs->liveEnvs.remove(env);
   3108 
   3109    if (JSObject* obj = envs->proxiedEnvs.get(env)) {
   3110      Rooted<DebugEnvironmentProxy*> debugEnv(
   3111          cx, &obj->as<DebugEnvironmentProxy>());
   3112      DebugEnvironments::takeFrameSnapshot(cx, debugEnv, ei.initialFrame());
   3113    }
   3114  }
   3115 }
   3116 
   3117 void DebugEnvironments::onPopLexical(JSContext* cx, const EnvironmentIter& ei) {
   3118  if (ei.scope().is<ClassBodyScope>()) {
   3119    onPopGeneric<ScopedLexicalEnvironmentObject, ClassBodyScope>(cx, ei);
   3120  } else {
   3121    onPopGeneric<ScopedLexicalEnvironmentObject, LexicalScope>(cx, ei);
   3122  }
   3123 }
   3124 
   3125 void DebugEnvironments::onPopVar(JSContext* cx, const EnvironmentIter& ei) {
   3126  if (ei.scope().is<EvalScope>()) {
   3127    onPopGeneric<VarEnvironmentObject, EvalScope>(cx, ei);
   3128  } else {
   3129    onPopGeneric<VarEnvironmentObject, VarScope>(cx, ei);
   3130  }
   3131 }
   3132 
   3133 void DebugEnvironments::onPopWith(AbstractFramePtr frame) {
   3134  Realm* realm = frame.realm();
   3135  if (DebugEnvironments* envs = realm->debugEnvs()) {
   3136    envs->liveEnvs.remove(
   3137        &frame.environmentChain()->as<WithEnvironmentObject>());
   3138  }
   3139 }
   3140 
   3141 void DebugEnvironments::onPopModule(JSContext* cx, const EnvironmentIter& ei) {
   3142  onPopGeneric<ModuleEnvironmentObject, ModuleScope>(cx, ei);
   3143 }
   3144 
   3145 void DebugEnvironments::onRealmUnsetIsDebuggee(Realm* realm) {
   3146  if (DebugEnvironments* envs = realm->debugEnvs()) {
   3147    envs->proxiedEnvs.clear();
   3148    envs->missingEnvs.clear();
   3149    envs->liveEnvs.clear();
   3150  }
   3151 }
   3152 
   3153 bool DebugEnvironments::updateLiveEnvironments(JSContext* cx) {
   3154  AutoCheckRecursionLimit recursion(cx);
   3155  if (!recursion.check(cx)) {
   3156    return false;
   3157  }
   3158 
   3159  /*
   3160   * Note that we must always update the top frame's environment objects'
   3161   * entries in liveEnvs because we can't be sure code hasn't run in that
   3162   * frame to change the environment chain since we were last called. The
   3163   * fp->prevUpToDate() flag indicates whether the environments of frames
   3164   * older than fp are already included in liveEnvs. It might seem simpler
   3165   * to have fp instead carry a flag indicating whether fp itself is
   3166   * accurately described, but then we would need to clear that flag
   3167   * whenever fp ran code. By storing the 'up to date' bit for fp->prev() in
   3168   * fp, simply popping fp effectively clears the flag for us, at exactly
   3169   * the time when execution resumes fp->prev().
   3170   */
   3171  for (AllFramesIter i(cx); !i.done(); ++i) {
   3172    if (!i.hasUsableAbstractFramePtr()) {
   3173      continue;
   3174    }
   3175 
   3176    AbstractFramePtr frame = i.abstractFramePtr();
   3177    if (frame.realm() != cx->realm()) {
   3178      continue;
   3179    }
   3180 
   3181    if (!frame.isDebuggee()) {
   3182      continue;
   3183    }
   3184 
   3185    RootedObject env(cx);
   3186    Rooted<Scope*> scope(cx);
   3187    if (!GetFrameEnvironmentAndScope(cx, frame, i.pc(), &env, &scope)) {
   3188      return false;
   3189    }
   3190 
   3191    for (EnvironmentIter ei(cx, env, scope, frame); ei.withinInitialFrame();
   3192         ei++) {
   3193      if (ei.hasSyntacticEnvironment() && !ei.scope().is<GlobalScope>()) {
   3194        MOZ_ASSERT(ei.environment().realm() == cx->realm());
   3195        DebugEnvironments* envs = ensureRealmData(cx);
   3196        if (!envs) {
   3197          return false;
   3198        }
   3199        if (!envs->liveEnvs.put(&ei.environment(), LiveEnvironmentVal(ei))) {
   3200          ReportOutOfMemory(cx);
   3201          return false;
   3202        }
   3203      }
   3204    }
   3205 
   3206    if (frame.prevUpToDate()) {
   3207      return true;
   3208    }
   3209    MOZ_ASSERT(frame.realm()->isDebuggee());
   3210    frame.setPrevUpToDate();
   3211  }
   3212 
   3213  return true;
   3214 }
   3215 
   3216 LiveEnvironmentVal* DebugEnvironments::hasLiveEnvironment(
   3217    EnvironmentObject& env) {
   3218  DebugEnvironments* envs = env.realm()->debugEnvs();
   3219  if (!envs) {
   3220    return nullptr;
   3221  }
   3222 
   3223  if (LiveEnvironmentMap::Ptr p = envs->liveEnvs.lookup(&env)) {
   3224    return &p->value();
   3225  }
   3226 
   3227  return nullptr;
   3228 }
   3229 
   3230 /* static */
   3231 void DebugEnvironments::unsetPrevUpToDateUntil(JSContext* cx,
   3232                                               AbstractFramePtr until) {
   3233  // This are two exceptions where fp->prevUpToDate() is cleared without
   3234  // popping the frame. When a frame is rematerialized or has its
   3235  // debuggeeness toggled off->on, all frames younger than the frame must
   3236  // have their prevUpToDate set to false. This is because unrematerialized
   3237  // Ion frames and non-debuggee frames are skipped by updateLiveEnvironments.
   3238  // If in the future a frame suddenly gains a usable AbstractFramePtr via
   3239  // rematerialization or becomes a debuggee, the prevUpToDate invariant
   3240  // will no longer hold for older frames on its stack.
   3241  for (AllFramesIter i(cx); !i.done(); ++i) {
   3242    if (!i.hasUsableAbstractFramePtr()) {
   3243      continue;
   3244    }
   3245 
   3246    AbstractFramePtr frame = i.abstractFramePtr();
   3247    if (frame == until) {
   3248      return;
   3249    }
   3250 
   3251    if (frame.realm() != cx->realm()) {
   3252      continue;
   3253    }
   3254 
   3255    frame.unsetPrevUpToDate();
   3256  }
   3257 }
   3258 
   3259 /* static */
   3260 void DebugEnvironments::forwardLiveFrame(JSContext* cx, AbstractFramePtr from,
   3261                                         AbstractFramePtr to) {
   3262  DebugEnvironments* envs = cx->realm()->debugEnvs();
   3263  if (!envs) {
   3264    return;
   3265  }
   3266 
   3267  for (MissingEnvironmentMap::Enum e(envs->missingEnvs); !e.empty();
   3268       e.popFront()) {
   3269    MissingEnvironmentKey key = e.front().key();
   3270    if (key.frame() == from) {
   3271      key.updateFrame(to);
   3272      e.rekeyFront(key);
   3273    }
   3274  }
   3275 
   3276  for (LiveEnvironmentMap::Enum e(envs->liveEnvs); !e.empty(); e.popFront()) {
   3277    LiveEnvironmentVal& val = e.front().value();
   3278    if (val.frame() == from) {
   3279      val.updateFrame(to);
   3280    }
   3281  }
   3282 }
   3283 
   3284 /* static */
   3285 void DebugEnvironments::traceLiveFrame(JSTracer* trc, AbstractFramePtr frame) {
   3286  for (MissingEnvironmentMap::Enum e(missingEnvs); !e.empty(); e.popFront()) {
   3287    if (e.front().key().frame() == frame) {
   3288      TraceEdge(trc, &e.front().value(), "debug-env-live-frame-missing-env");
   3289    }
   3290  }
   3291 }
   3292 
   3293 /*****************************************************************************/
   3294 
   3295 static JSObject* GetDebugEnvironment(JSContext* cx, const EnvironmentIter& ei);
   3296 
   3297 static DebugEnvironmentProxy* GetDebugEnvironmentForEnvironmentObject(
   3298    JSContext* cx, const EnvironmentIter& ei) {
   3299  Rooted<EnvironmentObject*> env(cx, &ei.environment());
   3300  if (DebugEnvironmentProxy* debugEnv =
   3301          DebugEnvironments::hasDebugEnvironment(cx, *env)) {
   3302    return debugEnv;
   3303  }
   3304 
   3305  EnvironmentIter copy(cx, ei);
   3306  RootedObject enclosingDebug(cx, GetDebugEnvironment(cx, ++copy));
   3307  if (!enclosingDebug) {
   3308    return nullptr;
   3309  }
   3310 
   3311  Rooted<DebugEnvironmentProxy*> debugEnv(
   3312      cx, DebugEnvironmentProxy::create(cx, *env, enclosingDebug));
   3313  if (!debugEnv) {
   3314    return nullptr;
   3315  }
   3316 
   3317  if (!DebugEnvironments::addDebugEnvironment(cx, env, debugEnv)) {
   3318    return nullptr;
   3319  }
   3320 
   3321  return debugEnv;
   3322 }
   3323 
   3324 static DebugEnvironmentProxy* GetDebugEnvironmentForMissing(
   3325    JSContext* cx, const EnvironmentIter& ei) {
   3326  MOZ_ASSERT(!ei.hasSyntacticEnvironment() &&
   3327             (ei.scope().is<FunctionScope>() || ei.scope().is<LexicalScope>() ||
   3328              ei.scope().is<WasmInstanceScope>() ||
   3329              ei.scope().is<WasmFunctionScope>() || ei.scope().is<VarScope>() ||
   3330              ei.scope().kind() == ScopeKind::StrictEval));
   3331 
   3332  DebugEnvironmentProxy* maybeDebugEnv;
   3333  if (!DebugEnvironments::getExistingDebugEnvironment(cx, ei, &maybeDebugEnv)) {
   3334    return nullptr;
   3335  }
   3336  if (maybeDebugEnv) {
   3337    return maybeDebugEnv;
   3338  }
   3339 
   3340  EnvironmentIter copy(cx, ei);
   3341  RootedObject enclosingDebug(cx, GetDebugEnvironment(cx, ++copy));
   3342  if (!enclosingDebug) {
   3343    return nullptr;
   3344  }
   3345 
   3346  /*
   3347   * Create the missing environment object. For lexical environment objects,
   3348   * this takes care of storing variable values after the stack frame has
   3349   * been popped. For call objects, we only use the pretend call object to
   3350   * access callee, bindings and to receive dynamically added
   3351   * properties. Together, this provides the nice invariant that every
   3352   * DebugEnvironmentProxy has a EnvironmentObject.
   3353   *
   3354   * Note: to preserve envChain depth invariants, these lazily-reified
   3355   * envs must not be put on the frame's environment chain; instead, they are
   3356   * maintained via DebugEnvironments hooks.
   3357   */
   3358  Rooted<DebugEnvironmentProxy*> debugEnv(cx);
   3359  if (ei.scope().is<FunctionScope>()) {
   3360    RootedFunction callee(cx,
   3361                          ei.scope().as<FunctionScope>().canonicalFunction());
   3362 
   3363    JS::ExposeObjectToActiveJS(callee);
   3364    Rooted<CallObject*> callobj(cx,
   3365                                CallObject::createHollowForDebug(cx, callee));
   3366    if (!callobj) {
   3367      return nullptr;
   3368    }
   3369 
   3370    debugEnv = DebugEnvironmentProxy::create(cx, *callobj, enclosingDebug);
   3371  } else if (ei.scope().is<LexicalScope>()) {
   3372    Rooted<LexicalScope*> lexicalScope(cx, &ei.scope().as<LexicalScope>());
   3373    Rooted<BlockLexicalEnvironmentObject*> env(
   3374        cx,
   3375        BlockLexicalEnvironmentObject::createHollowForDebug(cx, lexicalScope));
   3376    if (!env) {
   3377      return nullptr;
   3378    }
   3379 
   3380    debugEnv = DebugEnvironmentProxy::create(cx, *env, enclosingDebug);
   3381  } else if (ei.scope().is<WasmInstanceScope>()) {
   3382    Rooted<WasmInstanceScope*> wasmInstanceScope(
   3383        cx, &ei.scope().as<WasmInstanceScope>());
   3384    Rooted<WasmInstanceEnvironmentObject*> env(
   3385        cx, WasmInstanceEnvironmentObject::createHollowForDebug(
   3386                cx, wasmInstanceScope));
   3387    if (!env) {
   3388      return nullptr;
   3389    }
   3390 
   3391    debugEnv = DebugEnvironmentProxy::create(cx, *env, enclosingDebug);
   3392  } else if (ei.scope().is<WasmFunctionScope>()) {
   3393    Rooted<WasmFunctionScope*> wasmFunctionScope(
   3394        cx, &ei.scope().as<WasmFunctionScope>());
   3395    RootedObject enclosing(
   3396        cx, &enclosingDebug->as<DebugEnvironmentProxy>().environment());
   3397    Rooted<WasmFunctionCallObject*> callobj(
   3398        cx, WasmFunctionCallObject::createHollowForDebug(cx, enclosing,
   3399                                                         wasmFunctionScope));
   3400    if (!callobj) {
   3401      return nullptr;
   3402    }
   3403 
   3404    debugEnv = DebugEnvironmentProxy::create(cx, *callobj, enclosingDebug);
   3405  } else {
   3406    Rooted<Scope*> scope(cx, &ei.scope());
   3407    MOZ_ASSERT(scope->is<VarScope>() || scope->kind() == ScopeKind::StrictEval);
   3408 
   3409    Rooted<VarEnvironmentObject*> env(
   3410        cx, VarEnvironmentObject::createHollowForDebug(cx, scope));
   3411    if (!env) {
   3412      return nullptr;
   3413    }
   3414 
   3415    debugEnv = DebugEnvironmentProxy::create(cx, *env, enclosingDebug);
   3416  }
   3417 
   3418  if (!debugEnv) {
   3419    return nullptr;
   3420  }
   3421 
   3422  if (!DebugEnvironments::addDebugEnvironment(cx, ei, debugEnv)) {
   3423    return nullptr;
   3424  }
   3425 
   3426  return debugEnv;
   3427 }
   3428 
   3429 static JSObject* GetDebugEnvironmentForNonEnvironmentObject(
   3430    const EnvironmentIter& ei) {
   3431  JSObject& enclosing = ei.enclosingEnvironment();
   3432 #ifdef DEBUG
   3433  JSObject* o = &enclosing;
   3434  while ((o = o->enclosingEnvironment())) {
   3435    MOZ_ASSERT(!o->is<EnvironmentObject>());
   3436  }
   3437 #endif
   3438  return &enclosing;
   3439 }
   3440 
   3441 static JSObject* GetDebugEnvironment(JSContext* cx, const EnvironmentIter& ei) {
   3442  AutoCheckRecursionLimit recursion(cx);
   3443  if (!recursion.check(cx)) {
   3444    return nullptr;
   3445  }
   3446 
   3447  if (ei.done()) {
   3448    return GetDebugEnvironmentForNonEnvironmentObject(ei);
   3449  }
   3450 
   3451  if (ei.hasAnyEnvironmentObject()) {
   3452    return GetDebugEnvironmentForEnvironmentObject(cx, ei);
   3453  }
   3454 
   3455  if (ei.scope().is<FunctionScope>() || ei.scope().is<LexicalScope>() ||
   3456      ei.scope().is<WasmInstanceScope>() ||
   3457      ei.scope().is<WasmFunctionScope>() || ei.scope().is<VarScope>() ||
   3458      ei.scope().kind() == ScopeKind::StrictEval) {
   3459    return GetDebugEnvironmentForMissing(cx, ei);
   3460  }
   3461 
   3462  EnvironmentIter copy(cx, ei);
   3463  return GetDebugEnvironment(cx, ++copy);
   3464 }
   3465 
   3466 JSObject* js::GetDebugEnvironmentForFunction(JSContext* cx,
   3467                                             HandleFunction fun) {
   3468  cx->check(fun);
   3469  MOZ_ASSERT(CanUseDebugEnvironmentMaps(cx));
   3470  if (!DebugEnvironments::updateLiveEnvironments(cx)) {
   3471    return nullptr;
   3472  }
   3473  JSScript* script = JSFunction::getOrCreateScript(cx, fun);
   3474  if (!script) {
   3475    return nullptr;
   3476  }
   3477  EnvironmentIter ei(cx, fun->environment(), script->enclosingScope());
   3478  return GetDebugEnvironment(cx, ei);
   3479 }
   3480 
   3481 static auto GetSuspendedGeneratorEnvironmentAndScope(
   3482    AbstractGeneratorObject& genObj, JSScript* script) {
   3483  jsbytecode* pc =
   3484      script->offsetToPC(script->resumeOffsets()[genObj.resumeIndex()]);
   3485  return std::pair{&genObj.environmentChain(), script->innermostScope(pc)};
   3486 }
   3487 
   3488 JSObject* js::GetDebugEnvironmentForSuspendedGenerator(
   3489    JSContext* cx, JSScript* script, AbstractGeneratorObject& genObj) {
   3490  auto [env, scope] = GetSuspendedGeneratorEnvironmentAndScope(genObj, script);
   3491 
   3492  EnvironmentIter ei(cx, env, scope);
   3493  return GetDebugEnvironment(cx, ei);
   3494 }
   3495 
   3496 JSObject* js::GetDebugEnvironmentForFrame(JSContext* cx, AbstractFramePtr frame,
   3497                                          jsbytecode* pc) {
   3498  cx->check(frame);
   3499  if (CanUseDebugEnvironmentMaps(cx) &&
   3500      !DebugEnvironments::updateLiveEnvironments(cx)) {
   3501    return nullptr;
   3502  }
   3503 
   3504  RootedObject env(cx);
   3505  Rooted<Scope*> scope(cx);
   3506  if (!GetFrameEnvironmentAndScope(cx, frame, pc, &env, &scope)) {
   3507    return nullptr;
   3508  }
   3509 
   3510  EnvironmentIter ei(cx, env, scope, frame);
   3511  return GetDebugEnvironment(cx, ei);
   3512 }
   3513 
   3514 JSObject* js::GetDebugEnvironmentForGlobalLexicalEnvironment(JSContext* cx) {
   3515  EnvironmentIter ei(cx, &cx->global()->lexicalEnvironment(),
   3516                     &cx->global()->emptyGlobalScope());
   3517  return GetDebugEnvironment(cx, ei);
   3518 }
   3519 
   3520 WithEnvironmentObject* js::CreateObjectsForEnvironmentChain(
   3521    JSContext* cx, const JS::EnvironmentChain& chain,
   3522    HandleObject terminatingEnv) {
   3523  MOZ_ASSERT(!chain.empty());
   3524 
   3525 #ifdef DEBUG
   3526  for (size_t i = 0; i < chain.length(); ++i) {
   3527    cx->check(chain.chain()[i]);
   3528    MOZ_ASSERT(!chain.chain()[i]->isUnqualifiedVarObj());
   3529  }
   3530 #endif
   3531 
   3532  // Construct With object wrappers for the things on this environment chain
   3533  // and use the result as the thing to scope the function to.
   3534  Rooted<WithEnvironmentObject*> withEnv(cx);
   3535  RootedObject enclosingEnv(cx, terminatingEnv);
   3536  for (size_t i = chain.length(); i > 0;) {
   3537    withEnv = WithEnvironmentObject::createNonSyntactic(
   3538        cx, chain.chain()[--i], enclosingEnv, chain.supportUnscopables());
   3539    if (!withEnv) {
   3540      return nullptr;
   3541    }
   3542    enclosingEnv = withEnv;
   3543  }
   3544 
   3545  return withEnv;
   3546 }
   3547 
   3548 JSObject& WithEnvironmentObject::object() const {
   3549  return getReservedSlot(OBJECT_SLOT).toObject();
   3550 }
   3551 
   3552 JSObject* WithEnvironmentObject::withThis() const {
   3553  JSObject* obj = &getReservedSlot(THIS_SLOT).toObject();
   3554 
   3555  // Windows must never be exposed to script. WithEnvironmentObject::create
   3556  // should have set this to the WindowProxy.
   3557  MOZ_ASSERT(!IsWindow(obj));
   3558 
   3559  return obj;
   3560 }
   3561 
   3562 bool WithEnvironmentObject::isSyntactic() const {
   3563  Value v = getReservedSlot(SCOPE_OR_SUPPORT_UNSCOPABLES_SLOT);
   3564  MOZ_ASSERT(v.isPrivateGCThing() || v.isBoolean());
   3565  return v.isPrivateGCThing();
   3566 }
   3567 
   3568 bool WithEnvironmentObject::supportUnscopables() const {
   3569  if (isSyntactic()) {
   3570    return true;
   3571  }
   3572  Value v = getReservedSlot(SCOPE_OR_SUPPORT_UNSCOPABLES_SLOT);
   3573  MOZ_ASSERT(v.isBoolean());
   3574  return v.isTrue();
   3575 }
   3576 
   3577 WithScope& WithEnvironmentObject::scope() const {
   3578  MOZ_ASSERT(isSyntactic());
   3579  Value v = getReservedSlot(SCOPE_OR_SUPPORT_UNSCOPABLES_SLOT);
   3580  return *static_cast<WithScope*>(v.toGCThing());
   3581 }
   3582 
   3583 ModuleEnvironmentObject* js::GetModuleEnvironmentForScript(JSScript* script) {
   3584  return GetModuleObjectForScript(script)->environment();
   3585 }
   3586 
   3587 ModuleObject* js::GetModuleObjectForScript(JSScript* script) {
   3588  for (ScopeIter si(script); si; si++) {
   3589    if (si.kind() == ScopeKind::Module) {
   3590      return si.scope()->as<ModuleScope>().module();
   3591    }
   3592  }
   3593 
   3594  MOZ_CRASH("No module scope found for script");
   3595 }
   3596 
   3597 static bool GetThisValueForDebuggerEnvironmentIterMaybeOptimizedOut(
   3598    JSContext* cx, const EnvironmentIter& originalIter, HandleObject envChain,
   3599    const jsbytecode* pc, MutableHandleValue res) {
   3600  for (EnvironmentIter ei(cx, originalIter); ei; ei++) {
   3601    if (ei.scope().kind() == ScopeKind::Module) {
   3602      res.setUndefined();
   3603      return true;
   3604    }
   3605 
   3606    if (!ei.scope().is<FunctionScope>() ||
   3607        ei.scope().as<FunctionScope>().canonicalFunction()->hasLexicalThis()) {
   3608      continue;
   3609    }
   3610 
   3611    RootedScript script(cx, ei.scope().as<FunctionScope>().script());
   3612 
   3613    if (ei.withinInitialFrame()) {
   3614      MOZ_ASSERT(pc, "must have PC if there is an initial frame");
   3615 
   3616      // Figure out if we executed JSOp::FunctionThis and set it.
   3617      bool executedInitThisOp = false;
   3618      if (script->functionHasThisBinding()) {
   3619        for (const BytecodeLocation& loc : js::AllBytecodesIterable(script)) {
   3620          if (loc.getOp() == JSOp::FunctionThis) {
   3621            // The next op after JSOp::FunctionThis always sets it.
   3622            executedInitThisOp = pc > GetNextPc(loc.toRawBytecode());
   3623            break;
   3624          }
   3625        }
   3626      }
   3627 
   3628      if (!executedInitThisOp) {
   3629        AbstractFramePtr initialFrame = ei.initialFrame();
   3630        // Either we're yet to initialize the this-binding
   3631        // (JSOp::FunctionThis), or the script does not have a this-binding
   3632        // (because it doesn't use |this|).
   3633 
   3634        // If our this-argument is an object, or we're in strict mode,
   3635        // the this-binding is always the same as our this-argument.
   3636        if (initialFrame.thisArgument().isObject() || script->strict()) {
   3637          res.set(initialFrame.thisArgument());
   3638          return true;
   3639        }
   3640 
   3641        // We didn't initialize the this-binding yet. Determine the
   3642        // correct |this| value for this frame (box primitives if not
   3643        // in strict mode), and assign it to the this-argument slot so
   3644        // JSOp::FunctionThis will use it and not box a second time.
   3645        if (!GetFunctionThis(cx, initialFrame, res)) {
   3646          return false;
   3647        }
   3648        initialFrame.thisArgument() = res;
   3649        return true;
   3650      }
   3651    }
   3652 
   3653    if (!script->functionHasThisBinding()) {
   3654      res.setMagic(JS_OPTIMIZED_OUT);
   3655      return true;
   3656    }
   3657 
   3658    for (Rooted<BindingIter> bi(cx, BindingIter(script)); bi; bi++) {
   3659      if (bi.name() != cx->names().dot_this_) {
   3660        continue;
   3661      }
   3662 
   3663      BindingLocation loc = bi.location();
   3664      if (loc.kind() == BindingLocation::Kind::Environment) {
   3665        RootedObject callObj(cx, &ei.environment().as<CallObject>());
   3666        return GetProperty(cx, callObj, callObj, bi.name()->asPropertyName(),
   3667                           res);
   3668      }
   3669 
   3670      if (loc.kind() == BindingLocation::Kind::Frame) {
   3671        if (ei.withinInitialFrame()) {
   3672          res.set(ei.initialFrame().unaliasedLocal(loc.slot()));
   3673          return true;
   3674        }
   3675 
   3676        if (ei.hasAnyEnvironmentObject()) {
   3677          RootedObject env(cx, &ei.environment());
   3678          AbstractGeneratorObject* genObj =
   3679              GetGeneratorObjectForEnvironment(cx, env);
   3680          if (genObj && genObj->isSuspended() && genObj->hasStackStorage()) {
   3681            res.set(genObj->getUnaliasedLocal(loc.slot()));
   3682            return true;
   3683          }
   3684        }
   3685      }
   3686 
   3687      res.setMagic(JS_OPTIMIZED_OUT);
   3688      return true;
   3689    }
   3690 
   3691    MOZ_CRASH("'this' binding must be found");
   3692  }
   3693 
   3694  GetNonSyntacticGlobalThis(cx, envChain, res);
   3695  return true;
   3696 }
   3697 
   3698 bool js::GetThisValueForDebuggerFrameMaybeOptimizedOut(JSContext* cx,
   3699                                                       AbstractFramePtr frame,
   3700                                                       const jsbytecode* pc,
   3701                                                       MutableHandleValue res) {
   3702  RootedObject envChain(cx);
   3703  Rooted<Scope*> scope(cx);
   3704  if (!GetFrameEnvironmentAndScope(cx, frame, pc, &envChain, &scope)) {
   3705    return false;
   3706  }
   3707 
   3708  EnvironmentIter ei(cx, envChain, scope, frame);
   3709  return GetThisValueForDebuggerEnvironmentIterMaybeOptimizedOut(
   3710      cx, ei, envChain, pc, res);
   3711 }
   3712 
   3713 bool js::GetThisValueForDebuggerSuspendedGeneratorMaybeOptimizedOut(
   3714    JSContext* cx, AbstractGeneratorObject& genObj, JSScript* script,
   3715    MutableHandleValue res) {
   3716  auto [env, scope] = GetSuspendedGeneratorEnvironmentAndScope(genObj, script);
   3717  Rooted<JSObject*> envChain(cx, env);
   3718 
   3719  EnvironmentIter ei(cx, envChain, scope);
   3720  return GetThisValueForDebuggerEnvironmentIterMaybeOptimizedOut(
   3721      cx, ei, envChain, nullptr, res);
   3722 }
   3723 
   3724 static void ReportRuntimeRedeclaration(JSContext* cx,
   3725                                       Handle<PropertyName*> name,
   3726                                       const char* redeclKind) {
   3727  if (UniqueChars printable = AtomToPrintableString(cx, name)) {
   3728    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3729                              JSMSG_REDECLARED_VAR, redeclKind,
   3730                              printable.get());
   3731  }
   3732 }
   3733 
   3734 [[nodiscard]] static bool CheckLexicalNameConflict(
   3735    JSContext* cx, Handle<ExtensibleLexicalEnvironmentObject*> lexicalEnv,
   3736    HandleObject varObj, Handle<PropertyName*> name) {
   3737  const char* redeclKind = nullptr;
   3738  RootedId id(cx, NameToId(name));
   3739  mozilla::Maybe<PropertyInfo> prop, shadowedExistingProp;
   3740 
   3741  if ((prop = lexicalEnv->lookup(cx, name))) {
   3742    // ES 15.1.11 step 5.b
   3743    redeclKind = prop->writable() ? "let" : "const";
   3744  } else if (varObj->is<NativeObject>() &&
   3745             (prop = varObj->as<NativeObject>().lookup(cx, name))) {
   3746    // Faster path for ES 15.1.11 step 5.c-d when the shape can be found
   3747    // without going through a resolve hook.
   3748    if (!prop->configurable()) {
   3749      redeclKind = "non-configurable global property";
   3750    } else {
   3751      shadowedExistingProp = prop;
   3752    }
   3753  } else {
   3754    // ES 15.1.11 step 5.c-d
   3755    Rooted<mozilla::Maybe<PropertyDescriptor>> desc(cx);
   3756    if (!GetOwnPropertyDescriptor(cx, varObj, id, &desc)) {
   3757      return false;
   3758    }
   3759    if (desc.isSome()) {
   3760      if (!desc->configurable()) {
   3761        redeclKind = "non-configurable global property";
   3762      } else {
   3763        // Note: we don't have to set |shadowedExistingProp| here because if
   3764        // |varObj| is a global object, the NativeObject::lookup call above
   3765        // ensures this wasn't an existing property (that might require JIT/IC
   3766        // invalidation) but a new property defined by a resolve hook.
   3767        MOZ_ASSERT_IF(varObj->is<GlobalObject>(),
   3768                      varObj->getClass()->getResolve());
   3769        MOZ_ASSERT_IF(varObj->is<GlobalObject>(),
   3770                      varObj->as<GlobalObject>().containsPure(name));
   3771      }
   3772    }
   3773  }
   3774 
   3775  if (redeclKind) {
   3776    ReportRuntimeRedeclaration(cx, name, redeclKind);
   3777    return false;
   3778  }
   3779  if (shadowedExistingProp && varObj->is<GlobalObject>()) {
   3780    // Shadowing a configurable global property with a new lexical is one
   3781    // of the rare ways to invalidate a GetGName stub.
   3782    auto* global = &varObj->as<GlobalObject>();
   3783    global->bumpGenerationCount();
   3784 
   3785    // Also invalidate GetGName stubs and Ion code relying on an object fuse
   3786    // guard to bake in the property's value.
   3787    if (global->hasObjectFuse()) {
   3788      if (auto* objFuse = cx->zone()->objectFuses.get(global)) {
   3789        objFuse->handleShadowedGlobalProperty(cx, *shadowedExistingProp);
   3790      }
   3791    }
   3792  }
   3793 
   3794  return true;
   3795 }
   3796 
   3797 [[nodiscard]] static bool CheckVarNameConflict(
   3798    JSContext* cx, Handle<LexicalEnvironmentObject*> lexicalEnv,
   3799    Handle<PropertyName*> name) {
   3800  mozilla::Maybe<PropertyInfo> prop = lexicalEnv->lookup(cx, name);
   3801  if (prop.isSome()) {
   3802    ReportRuntimeRedeclaration(cx, name, prop->writable() ? "let" : "const");
   3803    return false;
   3804  }
   3805  return true;
   3806 }
   3807 
   3808 static void ReportCannotDeclareGlobalBinding(JSContext* cx,
   3809                                             Handle<PropertyName*> name,
   3810                                             const char* reason) {
   3811  if (UniqueChars printable = AtomToPrintableString(cx, name)) {
   3812    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3813                              JSMSG_CANT_DECLARE_GLOBAL_BINDING,
   3814                              printable.get(), reason);
   3815  }
   3816 }
   3817 
   3818 [[nodiscard]] static bool CheckCanDeclareGlobalBinding(
   3819    JSContext* cx, Handle<GlobalObject*> global, Handle<PropertyName*> name,
   3820    bool isFunction) {
   3821  RootedId id(cx, NameToId(name));
   3822  Rooted<mozilla::Maybe<PropertyDescriptor>> desc(cx);
   3823  if (!GetOwnPropertyDescriptor(cx, global, id, &desc)) {
   3824    return false;
   3825  }
   3826 
   3827  // ES 8.1.1.4.15 CanDeclareGlobalVar
   3828  // ES 8.1.1.4.16 CanDeclareGlobalFunction
   3829 
   3830  // Step 4.
   3831  if (desc.isNothing()) {
   3832    // 8.1.14.15 step 6.
   3833    // 8.1.14.16 step 5.
   3834    if (global->isExtensible()) {
   3835      return true;
   3836    }
   3837 
   3838    ReportCannotDeclareGlobalBinding(cx, name, "global is non-extensible");
   3839    return false;
   3840  }
   3841 
   3842  // Global functions have additional restrictions.
   3843  if (isFunction) {
   3844    // 8.1.14.16 step 6.
   3845    if (desc->configurable()) {
   3846      return true;
   3847    }
   3848 
   3849    // 8.1.14.16 step 7.
   3850    if (desc->isDataDescriptor() && desc->writable() && desc->enumerable()) {
   3851      return true;
   3852    }
   3853 
   3854    ReportCannotDeclareGlobalBinding(cx, name,
   3855                                     "property must be configurable or "
   3856                                     "both writable and enumerable");
   3857    return false;
   3858  }
   3859 
   3860  return true;
   3861 }
   3862 
   3863 // Add the var/let/const bindings to the variables environment of a global or
   3864 // sloppy-eval script. The redeclaration checks should already have been
   3865 // performed.
   3866 static bool InitGlobalOrEvalDeclarations(
   3867    JSContext* cx, HandleScript script,
   3868    Handle<ExtensibleLexicalEnvironmentObject*> lexicalEnv,
   3869    HandleObject varObj) {
   3870  Rooted<BindingIter> bi(cx, BindingIter(script));
   3871  for (; bi; bi++) {
   3872    if (bi.isTopLevelFunction()) {
   3873      continue;
   3874    }
   3875 
   3876    Rooted<PropertyName*> name(cx, bi.name()->asPropertyName());
   3877    unsigned attrs = script->isForEval() ? JSPROP_ENUMERATE
   3878                                         : JSPROP_ENUMERATE | JSPROP_PERMANENT;
   3879 
   3880    switch (bi.kind()) {
   3881      case BindingKind::Var: {
   3882        PropertyResult prop;
   3883        RootedObject obj2(cx);
   3884        if (!LookupProperty(cx, varObj, name, &obj2, &prop)) {
   3885          return false;
   3886        }
   3887 
   3888        if (prop.isNotFound() ||
   3889            (obj2 != varObj && varObj->is<GlobalObject>())) {
   3890          if (!DefineDataProperty(cx, varObj, name, UndefinedHandleValue,
   3891                                  attrs)) {
   3892            return false;
   3893          }
   3894        }
   3895 
   3896        break;
   3897      }
   3898 
   3899      case BindingKind::Const:
   3900        attrs |= JSPROP_READONLY;
   3901        [[fallthrough]];
   3902 
   3903      case BindingKind::Let: {
   3904        RootedId id(cx, NameToId(name));
   3905        RootedValue uninitialized(cx, MagicValue(JS_UNINITIALIZED_LEXICAL));
   3906        if (!NativeDefineDataProperty(cx, lexicalEnv, id, uninitialized,
   3907                                      attrs)) {
   3908          return false;
   3909        }
   3910 
   3911        break;
   3912      }
   3913 
   3914      default:
   3915        MOZ_CRASH("Expected binding kind");
   3916        return false;
   3917    }
   3918  }
   3919 
   3920  return true;
   3921 }
   3922 
   3923 // Define the hoisted top-level functions on the variables environment of a
   3924 // global or sloppy-eval script. Redeclaration checks must already have been
   3925 // performed.
   3926 static bool InitHoistedFunctionDeclarations(JSContext* cx, HandleScript script,
   3927                                            HandleObject envChain,
   3928                                            HandleObject varObj,
   3929                                            GCThingIndex lastFun) {
   3930  // The inner-functions up to `lastFun` are the hoisted function declarations
   3931  // of the script. We must clone and bind them now.
   3932  for (size_t i = 0; i <= lastFun; ++i) {
   3933    JS::GCCellPtr thing = script->gcthings()[i];
   3934 
   3935    // Skip the initial scopes. In practice, there is at most one variables and
   3936    // one lexical scope.
   3937    if (thing.is<js::Scope>()) {
   3938      MOZ_ASSERT(i < 2);
   3939      continue;
   3940    }
   3941 
   3942    RootedFunction fun(cx, &thing.as<JSObject>().as<JSFunction>());
   3943    Rooted<PropertyName*> name(cx, fun->fullExplicitName()->asPropertyName());
   3944 
   3945    // Clone the function before exposing to script as a binding.
   3946    JSObject* clone = Lambda(cx, fun, envChain);
   3947    if (!clone) {
   3948      return false;
   3949    }
   3950    RootedValue rval(cx, ObjectValue(*clone));
   3951 
   3952    PropertyResult prop;
   3953    RootedObject pobj(cx);
   3954    if (!LookupProperty(cx, varObj, name, &pobj, &prop)) {
   3955      return false;
   3956    }
   3957 
   3958    // ECMA requires functions defined when entering Eval code to be
   3959    // impermanent.
   3960    unsigned attrs = script->isForEval() ? JSPROP_ENUMERATE
   3961                                         : JSPROP_ENUMERATE | JSPROP_PERMANENT;
   3962 
   3963    if (prop.isNotFound() || pobj != varObj) {
   3964      if (!DefineDataProperty(cx, varObj, name, rval, attrs)) {
   3965        return false;
   3966      }
   3967 
   3968      // Done processing this function.
   3969      continue;
   3970    }
   3971 
   3972    /*
   3973     * A DebugEnvironmentProxy is okay here, and sometimes necessary. If
   3974     * Debugger.Frame.prototype.eval defines a function with the same name as an
   3975     * extant variable in the frame, the DebugEnvironmentProxy takes care of
   3976     * storing the function in the stack frame (for non-aliased variables) or on
   3977     * the scope object (for aliased).
   3978     */
   3979    MOZ_ASSERT(varObj->is<NativeObject>() ||
   3980               varObj->is<DebugEnvironmentProxy>());
   3981    if (varObj->is<GlobalObject>()) {
   3982      PropertyInfo propInfo = prop.propertyInfo();
   3983      if (propInfo.configurable()) {
   3984        if (!DefineDataProperty(cx, varObj, name, rval, attrs)) {
   3985          return false;
   3986        }
   3987      } else {
   3988        MOZ_ASSERT(propInfo.isDataProperty());
   3989        MOZ_ASSERT(propInfo.writable());
   3990        MOZ_ASSERT(propInfo.enumerable());
   3991      }
   3992    }
   3993 
   3994    /*
   3995     * Non-global properties, and global properties which we aren't simply
   3996     * redefining, must be set.  First, this preserves their attributes.
   3997     * Second, this will produce warnings and/or errors as necessary if the
   3998     * specified Call object property is not writable (const).
   3999     */
   4000 
   4001    RootedId id(cx, NameToId(name));
   4002    if (!PutProperty(cx, varObj, id, rval, script->strict())) {
   4003      return false;
   4004    }
   4005  }
   4006 
   4007  return true;
   4008 }
   4009 
   4010 [[nodiscard]] static bool CheckGlobalDeclarationConflicts(
   4011    JSContext* cx, HandleScript script,
   4012    Handle<ExtensibleLexicalEnvironmentObject*> lexicalEnv,
   4013    HandleObject varObj) {
   4014  // Due to the extensibility of the global lexical environment, we must
   4015  // check for redeclaring a binding.
   4016  //
   4017  // In the case of non-syntactic environment chains, we are checking
   4018  // redeclarations against the non-syntactic lexical environment and the
   4019  // variables object that the lexical environment corresponds to.
   4020  Rooted<PropertyName*> name(cx);
   4021  Rooted<BindingIter> bi(cx, BindingIter(script));
   4022 
   4023  // ES 15.1.11 GlobalDeclarationInstantiation
   4024 
   4025  // Step 6.
   4026  //
   4027  // Check 'var' declarations do not conflict with existing bindings in the
   4028  // global lexical environment.
   4029  for (; bi; bi++) {
   4030    if (bi.kind() != BindingKind::Var) {
   4031      break;
   4032    }
   4033    name = bi.name()->asPropertyName();
   4034    if (!CheckVarNameConflict(cx, lexicalEnv, name)) {
   4035      return false;
   4036    }
   4037 
   4038    // Step 10 and 12.
   4039    //
   4040    // Check that global functions and vars may be declared.
   4041    if (varObj->is<GlobalObject>()) {
   4042      Handle<GlobalObject*> global = varObj.as<GlobalObject>();
   4043      if (!CheckCanDeclareGlobalBinding(cx, global, name,
   4044                                        bi.isTopLevelFunction())) {
   4045        return false;
   4046      }
   4047    }
   4048  }
   4049 
   4050  // Step 5.
   4051  //
   4052  // Check that lexical bindings do not conflict.
   4053  for (; bi; bi++) {
   4054    name = bi.name()->asPropertyName();
   4055    if (!CheckLexicalNameConflict(cx, lexicalEnv, varObj, name)) {
   4056      return false;
   4057    }
   4058  }
   4059 
   4060  return true;
   4061 }
   4062 
   4063 [[nodiscard]] static bool CheckVarNameConflictsInEnv(JSContext* cx,
   4064                                                     HandleScript script,
   4065                                                     HandleObject obj) {
   4066  Rooted<LexicalEnvironmentObject*> env(cx);
   4067 
   4068  if (obj->is<LexicalEnvironmentObject>()) {
   4069    env = &obj->as<LexicalEnvironmentObject>();
   4070  } else if (obj->is<DebugEnvironmentProxy>() &&
   4071             obj->as<DebugEnvironmentProxy>()
   4072                 .environment()
   4073                 .is<LexicalEnvironmentObject>()) {
   4074    env = &obj->as<DebugEnvironmentProxy>()
   4075               .environment()
   4076               .as<LexicalEnvironmentObject>();
   4077  } else {
   4078    // Environment cannot contain lexical bindings.
   4079    return true;
   4080  }
   4081 
   4082  if (env->is<BlockLexicalEnvironmentObject>() &&
   4083      env->as<BlockLexicalEnvironmentObject>().scope().kind() ==
   4084          ScopeKind::SimpleCatch) {
   4085    // Annex B.3.5 allows redeclaring simple (non-destructured) catch parameters
   4086    // with var declarations.
   4087    return true;
   4088  }
   4089 
   4090  Rooted<PropertyName*> name(cx);
   4091  for (BindingIter bi(script); bi; bi++) {
   4092    name = bi.name()->asPropertyName();
   4093    if (!CheckVarNameConflict(cx, env, name)) {
   4094      return false;
   4095    }
   4096  }
   4097 
   4098  return true;
   4099 }
   4100 
   4101 static bool CheckArgumentsRedeclaration(JSContext* cx, HandleScript script) {
   4102  for (BindingIter bi(script); bi; bi++) {
   4103    if (bi.name() == cx->names().arguments) {
   4104      ReportRuntimeRedeclaration(cx, cx->names().arguments, "let");
   4105      return false;
   4106    }
   4107  }
   4108 
   4109  return true;
   4110 }
   4111 
   4112 static bool CheckEvalDeclarationConflicts(JSContext* cx, HandleScript script,
   4113                                          HandleObject envChain,
   4114                                          HandleObject varObj) {
   4115  // Strict eval has its own call objects and we shouldn't end up here.
   4116  //
   4117  // Non-strict eval may introduce 'var' bindings that conflict with lexical
   4118  // bindings in an enclosing lexical scope.
   4119  MOZ_ASSERT(!script->bodyScope()->hasEnvironment());
   4120  MOZ_ASSERT(!script->strict());
   4121 
   4122  MOZ_ASSERT(script->bodyScope()->as<EvalScope>().hasBindings());
   4123 
   4124  RootedObject env(cx, envChain);
   4125 
   4126  // ES 18.2.1.3.
   4127 
   4128  // Step 5.
   4129  //
   4130  // Check that a direct eval will not hoist 'var' bindings over lexical
   4131  // bindings with the same name.
   4132  while (env != varObj) {
   4133    if (!CheckVarNameConflictsInEnv(cx, script, env)) {
   4134      return false;
   4135    }
   4136    env = env->enclosingEnvironment();
   4137  }
   4138 
   4139  // Check for redeclared "arguments" in function parameter expressions.
   4140  //
   4141  // Direct eval in function parameter expressions isn't allowed to redeclare
   4142  // the implicit "arguments" bindings:
   4143  //   function f(a = eval("var arguments;")) {}
   4144  //
   4145  // |varObj| isn't a CallObject when the direct eval occurs in the function
   4146  // body and the extra function body var scope is present. The extra var scope
   4147  // is present iff the function has parameter expressions. So when we test
   4148  // that |varObj| is a CallObject and function parameter expressions are
   4149  // present, we can pinpoint the direct eval location to be in a function
   4150  // parameter expression. Additionally we must ensure the function isn't an
   4151  // arrow function, because arrow functions don't have an implicit "arguments"
   4152  // binding.
   4153  if (script->isDirectEvalInFunction() && varObj->is<CallObject>()) {
   4154    JSFunction* fun = &varObj->as<CallObject>().callee();
   4155    JSScript* funScript = fun->nonLazyScript();
   4156    if (funScript->functionHasParameterExprs() && !fun->isArrow()) {
   4157      if (!CheckArgumentsRedeclaration(cx, script)) {
   4158        return false;
   4159      }
   4160    }
   4161  }
   4162 
   4163  // Step 8.
   4164  //
   4165  // Check that global functions may be declared.
   4166  if (varObj->is<GlobalObject>()) {
   4167    Handle<GlobalObject*> global = varObj.as<GlobalObject>();
   4168    Rooted<PropertyName*> name(cx);
   4169    for (Rooted<BindingIter> bi(cx, BindingIter(script)); bi; bi++) {
   4170      name = bi.name()->asPropertyName();
   4171      if (!CheckCanDeclareGlobalBinding(cx, global, name,
   4172                                        bi.isTopLevelFunction())) {
   4173        return false;
   4174      }
   4175    }
   4176  }
   4177 
   4178  return true;
   4179 }
   4180 
   4181 bool js::GlobalOrEvalDeclInstantiation(JSContext* cx, HandleObject envChain,
   4182                                       HandleScript script,
   4183                                       GCThingIndex lastFun) {
   4184  MOZ_ASSERT(script->isGlobalCode() || script->isForEval());
   4185  MOZ_ASSERT(!script->selfHosted());
   4186 
   4187  RootedObject varObj(cx, &GetVariablesObject(envChain));
   4188  Rooted<ExtensibleLexicalEnvironmentObject*> lexicalEnv(cx);
   4189 
   4190  if (script->isForEval()) {
   4191    if (!CheckEvalDeclarationConflicts(cx, script, envChain, varObj)) {
   4192      return false;
   4193    }
   4194  } else {
   4195    lexicalEnv = &NearestEnclosingExtensibleLexicalEnvironment(envChain);
   4196    if (!CheckGlobalDeclarationConflicts(cx, script, lexicalEnv, varObj)) {
   4197      return false;
   4198    }
   4199  }
   4200 
   4201  if (!InitGlobalOrEvalDeclarations(cx, script, lexicalEnv, varObj)) {
   4202    return false;
   4203  }
   4204 
   4205  return InitHoistedFunctionDeclarations(cx, script, envChain, varObj, lastFun);
   4206 }
   4207 
   4208 bool js::InitFunctionEnvironmentObjects(JSContext* cx, AbstractFramePtr frame) {
   4209  MOZ_ASSERT(frame.isFunctionFrame());
   4210  MOZ_ASSERT(frame.callee()->needsFunctionEnvironmentObjects());
   4211 
   4212  RootedFunction callee(cx, frame.callee());
   4213 
   4214  gc::AllocSite* site = nullptr;
   4215  if (frame.isBaselineFrame()) {
   4216    site = frame.asBaselineFrame()->icScript()->maybeEnvAllocSite();
   4217  }
   4218 
   4219  // Named lambdas may have an environment that holds itself for recursion.
   4220  if (callee->needsNamedLambdaEnvironment()) {
   4221    NamedLambdaObject* declEnv =
   4222        NamedLambdaObject::createForFrame(cx, frame, site);
   4223    if (!declEnv) {
   4224      return false;
   4225    }
   4226    frame.pushOnEnvironmentChain(*declEnv);
   4227  }
   4228 
   4229  // If the function has parameter default expressions, there may be an
   4230  // extra environment to hold the parameters.
   4231  if (callee->needsCallObject()) {
   4232    CallObject* callObj = CallObject::createForFrame(cx, frame, site);
   4233    if (!callObj) {
   4234      return false;
   4235    }
   4236    frame.pushOnEnvironmentChain(*callObj);
   4237  }
   4238 
   4239  return true;
   4240 }
   4241 
   4242 bool js::PushVarEnvironmentObject(JSContext* cx, Handle<Scope*> scope,
   4243                                  AbstractFramePtr frame) {
   4244  auto* env = VarEnvironmentObject::createForFrame(cx, scope, frame);
   4245  if (!env) {
   4246    return false;
   4247  }
   4248  frame.pushOnEnvironmentChain(*env);
   4249  return true;
   4250 }
   4251 
   4252 static bool GetFrameEnvironmentAndScope(JSContext* cx, AbstractFramePtr frame,
   4253                                        const jsbytecode* pc,
   4254                                        MutableHandleObject env,
   4255                                        MutableHandle<Scope*> scope) {
   4256  env.set(frame.environmentChain());
   4257 
   4258  if (frame.isWasmDebugFrame()) {
   4259    Rooted<WasmInstanceObject*> instance(cx, frame.wasmInstance()->object());
   4260    uint32_t funcIndex = frame.asWasmDebugFrame()->funcIndex();
   4261    scope.set(WasmInstanceObject::getFunctionScope(cx, instance, funcIndex));
   4262    if (!scope) {
   4263      return false;
   4264    }
   4265  } else {
   4266    scope.set(frame.script()->innermostScope(pc));
   4267  }
   4268  return true;
   4269 }
   4270 
   4271 #ifdef DEBUG
   4272 
   4273 using PropertyNameSet = HashSet<PropertyName*>;
   4274 
   4275 static bool RemoveReferencedNames(JSContext* cx, HandleScript script,
   4276                                  PropertyNameSet& remainingNames) {
   4277  // Remove from remainingNames --- the closure variables in some outer
   4278  // script --- any free variables in this script. This analysis isn't perfect:
   4279  //
   4280  // - It will not account for free variables in an inner script which are
   4281  //   actually accessing some name in an intermediate script between the
   4282  //   inner and outer scripts. This can cause remainingNames to be an
   4283  //   underapproximation.
   4284  //
   4285  // - It will not account for new names introduced via eval. This can cause
   4286  //   remainingNames to be an overapproximation. This would be easy to fix
   4287  //   but is nice to have as the eval will probably not access these
   4288  //   these names and putting eval in an inner script is bad news if you
   4289  //   care about entraining variables unnecessarily.
   4290 
   4291  AllBytecodesIterable iter(script);
   4292  for (BytecodeLocation loc : iter) {
   4293    PropertyName* name;
   4294 
   4295    switch (loc.getOp()) {
   4296      case JSOp::GetName:
   4297      case JSOp::SetName:
   4298      case JSOp::StrictSetName:
   4299        name = script->getName(loc.toRawBytecode());
   4300        break;
   4301 
   4302      case JSOp::GetAliasedVar:
   4303      case JSOp::SetAliasedVar:
   4304        name = EnvironmentCoordinateNameSlow(script, loc.toRawBytecode());
   4305        break;
   4306 
   4307      default:
   4308        name = nullptr;
   4309        break;
   4310    }
   4311 
   4312    if (name) {
   4313      remainingNames.remove(name);
   4314    }
   4315  }
   4316 
   4317  RootedFunction fun(cx);
   4318  RootedScript innerScript(cx);
   4319  for (JS::GCCellPtr gcThing : script->gcthings()) {
   4320    if (!gcThing.is<JSObject>()) {
   4321      continue;
   4322    }
   4323    JSObject* obj = &gcThing.as<JSObject>();
   4324 
   4325    if (!obj->is<JSFunction>()) {
   4326      continue;
   4327    }
   4328    fun = &obj->as<JSFunction>();
   4329 
   4330    if (!fun->isInterpreted()) {
   4331      continue;
   4332    }
   4333 
   4334    innerScript = JSFunction::getOrCreateScript(cx, fun);
   4335    if (!innerScript) {
   4336      return false;
   4337    }
   4338 
   4339    if (!RemoveReferencedNames(cx, innerScript, remainingNames)) {
   4340      return false;
   4341    }
   4342  }
   4343 
   4344  return true;
   4345 }
   4346 
   4347 static bool AnalyzeEntrainedVariablesInScript(JSContext* cx,
   4348                                              HandleScript script,
   4349                                              HandleScript innerScript) {
   4350  PropertyNameSet remainingNames(cx);
   4351 
   4352  for (BindingIter bi(script); bi; bi++) {
   4353    if (bi.closedOver()) {
   4354      PropertyName* name = bi.name()->asPropertyName();
   4355      PropertyNameSet::AddPtr p = remainingNames.lookupForAdd(name);
   4356      if (!p && !remainingNames.add(p, name)) {
   4357        return false;
   4358      }
   4359    }
   4360  }
   4361 
   4362  if (!RemoveReferencedNames(cx, innerScript, remainingNames)) {
   4363    return false;
   4364  }
   4365 
   4366  if (!remainingNames.empty()) {
   4367    Sprinter buf(cx);
   4368    if (!buf.init()) {
   4369      return false;
   4370    }
   4371 
   4372    buf.printf("Script ");
   4373 
   4374    if (JSAtom* name = script->function()->fullDisplayAtom()) {
   4375      buf.putString(cx, name);
   4376      buf.printf(" ");
   4377    }
   4378 
   4379    buf.printf("(%s:%u) has variables entrained by ", script->filename(),
   4380               script->lineno());
   4381 
   4382    if (JSAtom* name = innerScript->function()->fullDisplayAtom()) {
   4383      buf.putString(cx, name);
   4384      buf.printf(" ");
   4385    }
   4386 
   4387    buf.printf("(%s:%u) ::", innerScript->filename(), innerScript->lineno());
   4388 
   4389    for (PropertyNameSet::Range r = remainingNames.all(); !r.empty();
   4390         r.popFront()) {
   4391      buf.printf(" ");
   4392      buf.putString(cx, r.front());
   4393    }
   4394 
   4395    JS::UniqueChars str = buf.release();
   4396    if (!str) {
   4397      return false;
   4398    }
   4399    printf("%s\n", str.get());
   4400  }
   4401 
   4402  RootedFunction fun(cx);
   4403  RootedScript innerInnerScript(cx);
   4404  for (JS::GCCellPtr gcThing : script->gcthings()) {
   4405    if (!gcThing.is<JSObject>()) {
   4406      continue;
   4407    }
   4408    JSObject* obj = &gcThing.as<JSObject>();
   4409 
   4410    if (!obj->is<JSFunction>()) {
   4411      continue;
   4412    }
   4413    fun = &obj->as<JSFunction>();
   4414 
   4415    if (!fun->isInterpreted()) {
   4416      continue;
   4417    }
   4418 
   4419    innerInnerScript = JSFunction::getOrCreateScript(cx, fun);
   4420    if (!innerInnerScript) {
   4421      return false;
   4422    }
   4423 
   4424    if (!AnalyzeEntrainedVariablesInScript(cx, script, innerInnerScript)) {
   4425      return false;
   4426    }
   4427  }
   4428 
   4429  return true;
   4430 }
   4431 
   4432 // Look for local variables in script or any other script inner to it, which are
   4433 // part of the script's call object and are unnecessarily entrained by their own
   4434 // inner scripts which do not refer to those variables. An example is:
   4435 //
   4436 // function foo() {
   4437 //   var a, b;
   4438 //   function bar() { return a; }
   4439 //   function baz() { return b; }
   4440 // }
   4441 //
   4442 // |bar| unnecessarily entrains |b|, and |baz| unnecessarily entrains |a|.
   4443 bool js::AnalyzeEntrainedVariables(JSContext* cx, HandleScript script) {
   4444  RootedFunction fun(cx);
   4445  RootedScript innerScript(cx);
   4446  for (JS::GCCellPtr gcThing : script->gcthings()) {
   4447    if (!gcThing.is<JSObject>()) {
   4448      continue;
   4449    }
   4450    JSObject* obj = &gcThing.as<JSObject>();
   4451 
   4452    if (!obj->is<JSFunction>()) {
   4453      continue;
   4454    }
   4455    fun = &obj->as<JSFunction>();
   4456 
   4457    if (!fun->isInterpreted()) {
   4458      continue;
   4459    }
   4460 
   4461    innerScript = JSFunction::getOrCreateScript(cx, fun);
   4462    if (!innerScript) {
   4463      return false;
   4464    }
   4465 
   4466    if (fun->needsCallObject()) {
   4467      if (!AnalyzeEntrainedVariablesInScript(cx, script, innerScript)) {
   4468        return false;
   4469      }
   4470    }
   4471 
   4472    if (!AnalyzeEntrainedVariables(cx, innerScript)) {
   4473      return false;
   4474    }
   4475  }
   4476 
   4477  return true;
   4478 }
   4479 #endif
   4480 
   4481 JSObject* js::MaybeOptimizeBindUnqualifiedGlobalName(GlobalObject* global,
   4482                                                     PropertyName* name) {
   4483  // We can bind name to the global lexical scope if the binding already
   4484  // exists, is initialized, and is writable (i.e., an initialized
   4485  // 'let') at compile time.
   4486  GlobalLexicalEnvironmentObject* env = &global->lexicalEnvironment();
   4487  mozilla::Maybe<PropertyInfo> prop = env->lookupPure(name);
   4488  if (prop.isSome()) {
   4489    if (prop->writable() &&
   4490        !env->getSlot(prop->slot()).isMagic(JS_UNINITIALIZED_LEXICAL)) {
   4491      return env;
   4492    }
   4493    return nullptr;
   4494  }
   4495 
   4496  prop = global->lookupPure(name);
   4497  if (prop.isSome()) {
   4498    // If the property does not currently exist on the global lexical
   4499    // scope, we can bind name to the global object if the property
   4500    // exists on the global and is non-configurable, as then it cannot
   4501    // be shadowed.
   4502    if (!prop->configurable()) {
   4503      return global;
   4504    }
   4505  }
   4506 
   4507  return nullptr;
   4508 }
   4509 
   4510 const char* EnvironmentObject::typeString() const {
   4511  if (is<CallObject>()) {
   4512    return "CallObject";
   4513  }
   4514  if (is<VarEnvironmentObject>()) {
   4515    return "VarEnvironmentObject";
   4516  }
   4517  if (is<ModuleEnvironmentObject>()) {
   4518    return "ModuleEnvironmentObject";
   4519  }
   4520  if (is<WasmInstanceEnvironmentObject>()) {
   4521    return "WasmInstanceEnvironmentObject";
   4522  }
   4523  if (is<WasmFunctionCallObject>()) {
   4524    return "WasmFunctionCallObject";
   4525  }
   4526  if (is<LexicalEnvironmentObject>()) {
   4527    if (is<ScopedLexicalEnvironmentObject>()) {
   4528      if (is<BlockLexicalEnvironmentObject>()) {
   4529        if (is<NamedLambdaObject>()) {
   4530          return "NamedLambdaObject";
   4531        }
   4532        return "BlockLexicalEnvironmentObject";
   4533      }
   4534      if (is<ClassBodyLexicalEnvironmentObject>()) {
   4535        return "ClassBodyLexicalEnvironmentObject";
   4536      }
   4537      return "ScopedLexicalEnvironmentObject";
   4538    }
   4539 
   4540    if (is<ExtensibleLexicalEnvironmentObject>()) {
   4541      if (is<GlobalLexicalEnvironmentObject>()) {
   4542        return "GlobalLexicalEnvironmentObject";
   4543      }
   4544      if (is<NonSyntacticLexicalEnvironmentObject>()) {
   4545        return "NonSyntacticLexicalEnvironmentObject";
   4546      }
   4547      return "ExtensibleLexicalEnvironmentObject";
   4548    }
   4549 
   4550    return "LexicalEnvironmentObject";
   4551  }
   4552  if (is<NonSyntacticVariablesObject>()) {
   4553    return "NonSyntacticVariablesObject";
   4554  }
   4555  if (is<WithEnvironmentObject>()) {
   4556    return "WithEnvironmentObject";
   4557  }
   4558  if (is<RuntimeLexicalErrorObject>()) {
   4559    return "RuntimeLexicalErrorObject";
   4560  }
   4561 
   4562  return "EnvironmentObject";
   4563 }
   4564 
   4565 #if defined(DEBUG) || defined(JS_JITSPEW)
   4566 static void DumpEnvironmentObject(JSObject* unrootedEnvObj) {
   4567  JSContext* cx = TlsContext.get();
   4568  if (!cx) {
   4569    fprintf(stderr, "*** can't get JSContext for current thread\n");
   4570    return;
   4571  }
   4572 
   4573  Rooted<JSObject*> envObj(cx, unrootedEnvObj);
   4574  while (envObj) {
   4575    Rooted<EnvironmentObject*> env(cx);
   4576    if (envObj->is<EnvironmentObject>()) {
   4577      env = &envObj->as<EnvironmentObject>();
   4578    } else if (envObj->is<DebugEnvironmentProxy>()) {
   4579      fprintf(stderr, "[DebugProxy] ");
   4580      env = &envObj->as<DebugEnvironmentProxy>().environment();
   4581    } else {
   4582      MOZ_ASSERT(envObj->is<GlobalObject>());
   4583      fprintf(stderr, "global\n");
   4584      break;
   4585    }
   4586 
   4587    fprintf(stderr, "%s (%p)", env->typeString(), env.get());
   4588 
   4589    Rooted<Scope*> scope(cx);
   4590    if (env->is<VarEnvironmentObject>()) {
   4591      scope = &env->as<VarEnvironmentObject>().scope();
   4592    }
   4593    if (env->is<WasmInstanceEnvironmentObject>()) {
   4594      scope = &env->as<WasmInstanceEnvironmentObject>().scope();
   4595    } else if (env->is<WasmFunctionCallObject>()) {
   4596      scope = &env->as<WasmFunctionCallObject>().scope();
   4597    } else if (env->is<ScopedLexicalEnvironmentObject>()) {
   4598      scope = &env->as<ScopedLexicalEnvironmentObject>().scope();
   4599    }
   4600 
   4601    bool hadProp = false;
   4602 
   4603    // Set of names in the scope.
   4604    // This is used for filtering out those names from properties bwlow.
   4605    Rooted<GCHashSet<JSAtom*>> names(cx, GCHashSet<JSAtom*>(cx, 0));
   4606 
   4607    if (scope) {
   4608      if (!hadProp) {
   4609        fprintf(stderr, " {\n");
   4610      }
   4611      hadProp = true;
   4612      for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++) {
   4613        fprintf(stderr, "  ");
   4614 
   4615        switch (bi.location().kind()) {
   4616          case BindingLocation::Kind::Global:
   4617            if (bi.isTopLevelFunction()) {
   4618              fprintf(stderr, "global function: ");
   4619            } else {
   4620              fprintf(stderr, "global: ");
   4621            }
   4622            break;
   4623          case BindingLocation::Kind::Argument:
   4624            fprintf(stderr, "arg slot %u: ", bi.location().argumentSlot());
   4625            break;
   4626          case BindingLocation::Kind::Frame:
   4627            fprintf(stderr, "frame slot %u: ", bi.location().slot());
   4628            break;
   4629          case BindingLocation::Kind::Environment:
   4630            fprintf(stderr, "env slot %u: %s ", bi.location().slot(),
   4631                    BindingKindString(bi.kind()));
   4632            break;
   4633          case BindingLocation::Kind::NamedLambdaCallee:
   4634            fprintf(stderr, "named lambda callee: ");
   4635            break;
   4636          case BindingLocation::Kind::Import:
   4637            fprintf(stderr, "import: ");
   4638            break;
   4639        }
   4640 
   4641        JSAtom* name = bi.name();
   4642        if (!names.put(name)) {
   4643          fprintf(stderr, "  *** out of memory\n");
   4644          return;
   4645        }
   4646 
   4647        UniqueChars bytes = AtomToPrintableString(cx, name);
   4648        if (!bytes) {
   4649          fprintf(stderr, "  *** out of memory\n");
   4650          return;
   4651        }
   4652        fprintf(stderr, "%s\n", bytes.get());
   4653      }
   4654    }
   4655 
   4656    // The environment object can have random properties that can be found in
   4657    // the name lookup.  Show them as well, excluding the properties which
   4658    // are already shown above for the scope.
   4659    if (PropMap* map = env->shape()->propMap()) {
   4660      Vector<PropMap*, 8, SystemAllocPolicy> maps;
   4661      while (true) {
   4662        if (!maps.append(map)) {
   4663          fprintf(stderr, "  *** out of memory\n");
   4664          return;
   4665        }
   4666        if (!map->hasPrevious()) {
   4667          break;
   4668        }
   4669        map = map->asLinked()->previous();
   4670      }
   4671 
   4672      for (size_t i = maps.length(); i > 0; i--) {
   4673        size_t index = i - 1;
   4674        PropMap* map = maps[index];
   4675        uint32_t len = (index == 0) ? env->shape()->asNative().propMapLength()
   4676                                    : PropMap::Capacity;
   4677        for (uint32_t j = 0; j < len; j++) {
   4678          if (!map->hasKey(j)) {
   4679            MOZ_ASSERT(map->isDictionary());
   4680            continue;
   4681          }
   4682 
   4683          PropertyKey propKey = map->getKey(j);
   4684          if (propKey.isAtom()) {
   4685            JSAtom* name = propKey.toAtom();
   4686            if (names.has(name)) {
   4687              continue;
   4688            }
   4689          }
   4690 
   4691          JS::UniqueChars propChars = map->getPropertyNameAt(j);
   4692          if (!propChars) {
   4693            fprintf(stderr, "  *** out of memory\n");
   4694            return;
   4695          }
   4696 
   4697          if (!hadProp) {
   4698            fprintf(stderr, " {\n");
   4699          }
   4700          hadProp = true;
   4701 
   4702          PropertyInfo prop = map->getPropertyInfo(j);
   4703          if (prop.hasSlot()) {
   4704            fprintf(stderr, "  prop %u: %s\n", prop.slot(), propChars.get());
   4705          } else {
   4706            fprintf(stderr, "  prop: %s\n", propChars.get());
   4707          }
   4708        }
   4709      }
   4710    }
   4711 
   4712    if (hadProp) {
   4713      fprintf(stderr, "}");
   4714    }
   4715 
   4716    fprintf(stderr, "\n");
   4717 
   4718    if (envObj->is<DebugEnvironmentProxy>()) {
   4719      envObj = &envObj->as<DebugEnvironmentProxy>().enclosingEnvironment();
   4720    } else {
   4721      envObj = &env->enclosingEnvironment();
   4722    }
   4723 
   4724    if (envObj) {
   4725      fprintf(stderr, "-> ");
   4726    }
   4727  }
   4728 }
   4729 
   4730 void EnvironmentObject::dump() { DumpEnvironmentObject(this); }
   4731 
   4732 void DebugEnvironmentProxy::dump() { DumpEnvironmentObject(this); }
   4733 #endif /* defined(DEBUG) || defined(JS_JITSPEW) */