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