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