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