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