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 #ifndef vm_ScopeObject_h
8 #define vm_ScopeObject_h
9 
10 #include "jscntxt.h"
11 #include "jsobj.h"
12 #include "jsweakmap.h"
13 
14 #include "builtin/ModuleObject.h"
15 #include "gc/Barrier.h"
16 #include "js/GCHashTable.h"
17 #include "vm/ArgumentsObject.h"
18 #include "vm/ProxyObject.h"
19 
20 namespace js {
21 
22 namespace frontend {
23 struct Definition;
24 class FunctionBox;
25 class ModuleBox;
26 }
27 
28 class StaticWithObject;
29 class StaticEvalObject;
30 class StaticNonSyntacticScopeObjects;
31 
32 class ModuleObject;
33 typedef Handle<ModuleObject*> HandleModuleObject;
34 
35 /*****************************************************************************/
36 
37 /*
38  * The static scope chain is the canonical truth for lexical scope contour of
39  * a program. The dynamic scope chain is derived from the static scope chain:
40  * it is the chain of scopes whose static scopes have a runtime
41  * representation, for example, due to aliased bindings.
42  *
43  * Static scopes roughly correspond to a scope in the program text. They are
44  * divided into scopes that have direct correspondence to program text (i.e.,
45  * syntactic) and ones used internally for scope walking (i.e., non-syntactic).
46  *
47  * The following are syntactic static scopes:
48  *
49  * StaticBlockObject
50  *   Scope for non-function body blocks. e.g., |{ let x; }|
51  *
52  * JSFunction
53  *   Scope for function bodies. e.g., |function f() { var x; let y; }|
54  *
55  * ModuleObject
56  *   Scope for moddules.
57  *
58  * StaticWithObject
59  *   Scope for |with|. e.g., |with ({}) { ... }|
60  *
61  * StaticEvalObject
62  *   Scope for |eval|. e.g., |eval(...)|
63  *
64  * The following are non-syntactic static scopes:
65  *
66  * StaticNonSyntacticScopeObjects
67  *   Signals presence of "polluting" scope objects. Used by Gecko.
68  *
69  * There is an additional scope for named lambdas without a static scope
70  * object. E.g., in:
71  *
72  *   (function f() { var x; function g() { } })
73  *
74  * All static scope objects are ScopeObjects with the exception of JSFunction
75  * and ModuleObject, which keeps their enclosing scope link on
76  * |JSScript::enclosingStaticScope()|.
77  */
78 template <AllowGC allowGC>
79 class StaticScopeIter
80 {
81     typename MaybeRooted<JSObject*, allowGC>::RootType obj;
82     bool onNamedLambda;
83 
IsStaticScope(JSObject * obj)84     static bool IsStaticScope(JSObject* obj) {
85         return obj->is<StaticBlockObject>() ||
86                obj->is<StaticWithObject>() ||
87                obj->is<StaticEvalObject>() ||
88                obj->is<StaticNonSyntacticScopeObjects>() ||
89                obj->is<JSFunction>() ||
90                obj->is<ModuleObject>();
91     }
92 
93   public:
StaticScopeIter(ExclusiveContext * cx,JSObject * obj)94     StaticScopeIter(ExclusiveContext* cx, JSObject* obj)
95       : obj(cx, obj), onNamedLambda(false)
96     {
97         static_assert(allowGC == CanGC,
98                       "the context-accepting constructor should only be used "
99                       "in CanGC code");
100         MOZ_ASSERT_IF(obj, IsStaticScope(obj));
101     }
102 
StaticScopeIter(ExclusiveContext * cx,const StaticScopeIter<CanGC> & ssi)103     StaticScopeIter(ExclusiveContext* cx, const StaticScopeIter<CanGC>& ssi)
104       : obj(cx, ssi.obj), onNamedLambda(ssi.onNamedLambda)
105     {
106         JS_STATIC_ASSERT(allowGC == CanGC);
107     }
108 
StaticScopeIter(JSObject * obj)109     explicit StaticScopeIter(JSObject* obj)
110       : obj((ExclusiveContext*) nullptr, obj), onNamedLambda(false)
111     {
112         static_assert(allowGC == NoGC,
113                       "the constructor not taking a context should only be "
114                       "used in NoGC code");
115         MOZ_ASSERT_IF(obj, IsStaticScope(obj));
116     }
117 
StaticScopeIter(const StaticScopeIter<NoGC> & ssi)118     explicit StaticScopeIter(const StaticScopeIter<NoGC>& ssi)
119       : obj((ExclusiveContext*) nullptr, ssi.obj), onNamedLambda(ssi.onNamedLambda)
120     {
121         static_assert(allowGC == NoGC,
122                       "the constructor not taking a context should only be "
123                       "used in NoGC code");
124     }
125 
done()126     bool done() const { return !obj; }
127     void operator++(int);
128 
staticScope()129     JSObject* staticScope() const { MOZ_ASSERT(!done()); return obj; }
130 
131     // Return whether this static scope will have a syntactic scope (i.e. a
132     // ScopeObject that isn't a non-syntactic With or
133     // NonSyntacticVariablesObject) on the dynamic scope chain.
134     bool hasSyntacticDynamicScopeObject() const;
135     Shape* scopeShape() const;
136 
137     enum Type { Module, Function, Block, With, NamedLambda, Eval, NonSyntactic };
138     Type type() const;
139 
140     StaticBlockObject& block() const;
141     StaticWithObject& staticWith() const;
142     StaticEvalObject& eval() const;
143     StaticNonSyntacticScopeObjects& nonSyntactic() const;
144     JSScript* funScript() const;
145     JSFunction& fun() const;
146     frontend::FunctionBox* maybeFunctionBox() const;
147     JSScript* moduleScript() const;
148     ModuleObject& module() const;
149 };
150 
151 /*****************************************************************************/
152 
153 /*
154  * A "scope coordinate" describes how to get from head of the scope chain to a
155  * given lexically-enclosing variable. A scope coordinate has two dimensions:
156  *  - hops: the number of scope objects on the scope chain to skip
157  *  - slot: the slot on the scope object holding the variable's value
158  */
159 class ScopeCoordinate
160 {
161     uint32_t hops_;
162     uint32_t slot_;
163 
164     /*
165      * Technically, hops_/slot_ are SCOPECOORD_(HOPS|SLOT)_BITS wide.  Since
166      * ScopeCoordinate is a temporary value, don't bother with a bitfield as
167      * this only adds overhead.
168      */
169     static_assert(SCOPECOORD_HOPS_BITS <= 32, "We have enough bits below");
170     static_assert(SCOPECOORD_SLOT_BITS <= 32, "We have enough bits below");
171 
172   public:
ScopeCoordinate(jsbytecode * pc)173     explicit inline ScopeCoordinate(jsbytecode* pc)
174       : hops_(GET_SCOPECOORD_HOPS(pc)), slot_(GET_SCOPECOORD_SLOT(pc + SCOPECOORD_HOPS_LEN))
175     {
176         MOZ_ASSERT(JOF_OPTYPE(JSOp(*pc)) == JOF_SCOPECOORD);
177     }
178 
ScopeCoordinate()179     inline ScopeCoordinate() {}
180 
setHops(uint32_t hops)181     void setHops(uint32_t hops) { MOZ_ASSERT(hops < SCOPECOORD_HOPS_LIMIT); hops_ = hops; }
setSlot(uint32_t slot)182     void setSlot(uint32_t slot) { MOZ_ASSERT(slot < SCOPECOORD_SLOT_LIMIT); slot_ = slot; }
183 
hops()184     uint32_t hops() const { MOZ_ASSERT(hops_ < SCOPECOORD_HOPS_LIMIT); return hops_; }
slot()185     uint32_t slot() const { MOZ_ASSERT(slot_ < SCOPECOORD_SLOT_LIMIT); return slot_; }
186 
187     bool operator==(const ScopeCoordinate& rhs) const {
188         return hops() == rhs.hops() && slot() == rhs.slot();
189     }
190 };
191 
192 /*
193  * Return a shape representing the static scope containing the variable
194  * accessed by the ALIASEDVAR op at 'pc'.
195  */
196 extern Shape*
197 ScopeCoordinateToStaticScopeShape(JSScript* script, jsbytecode* pc);
198 
199 /* Return the name being accessed by the given ALIASEDVAR op. */
200 extern PropertyName*
201 ScopeCoordinateName(ScopeCoordinateNameCache& cache, JSScript* script, jsbytecode* pc);
202 
203 /* Return the function script accessed by the given ALIASEDVAR op, or nullptr. */
204 extern JSScript*
205 ScopeCoordinateFunctionScript(JSScript* script, jsbytecode* pc);
206 
207 /*****************************************************************************/
208 
209 /*
210  * Scope objects
211  *
212  * Scope objects are technically real JSObjects but only belong on the scope
213  * chain (that is, fp->scopeChain() or fun->environment()). The hierarchy of
214  * scope objects is:
215  *
216  *   JSObject                      Generic object
217  *     |
218  *   ScopeObject---+---+           Engine-internal scope
219  *     |   |   |   |   |
220  *     |   |   |   |  StaticNonSyntacticScopeObjects  See "Non-syntactic scope objects"
221  *     |   |   |   |
222  *     |   |   |  StaticEvalObject  Placeholder so eval scopes may be iterated through
223  *     |   |   |
224  *     |   |  DeclEnvObject         Holds name of recursive/needsCallObject named lambda
225  *     |   |
226  *     |  LexicalScopeBase          Shared base for function and modules scopes
227  *     |   |   |
228  *     |   |  CallObject            Scope of entire function or strict eval
229  *     |   |
230  *     |  ModuleEnvironmentObject   Module top-level scope on run-time scope chain
231  *     |
232  *   NestedScopeObject              Statement scopes; don't cross script boundaries
233  *     |   |   |
234  *     |   |  StaticWithObject      Template for "with" object in static scope chain
235  *     |   |
236  *     |  DynamicWithObject         Run-time "with" object on scope chain
237  *     |
238  *   BlockObject                    Shared interface of cloned/static block objects
239  *     |   |
240  *     |  ClonedBlockObject         let, switch, catch, for
241  *     |
242  *   StaticBlockObject              See NB
243  *
244  * This hierarchy represents more than just the interface hierarchy: reserved
245  * slots in base classes are fixed for all derived classes. Thus, for example,
246  * ScopeObject::enclosingScope() can simply access a fixed slot without further
247  * dynamic type information.
248  *
249  * NB: Static block objects are a special case: these objects are created at
250  * compile time to hold the shape/binding information from which block objects
251  * are cloned at runtime. These objects should never escape into the wild and
252  * support a restricted set of ScopeObject operations.
253  *
254  * See also "Debug scope objects" below.
255  */
256 
257 class ScopeObject : public NativeObject
258 {
259   protected:
260     static const uint32_t SCOPE_CHAIN_SLOT = 0;
261 
262   public:
263     /*
264      * Since every scope chain terminates with a global object and GlobalObject
265      * does not derive ScopeObject (it has a completely different layout), the
266      * enclosing scope of a ScopeObject is necessarily non-null.
267      */
enclosingScope()268     inline JSObject& enclosingScope() const {
269         return getFixedSlot(SCOPE_CHAIN_SLOT).toObject();
270     }
271 
272     void setEnclosingScope(HandleObject obj);
273 
274     /*
275      * Get or set an aliased variable contained in this scope. Unaliased
276      * variables should instead access the stack frame. Aliased variable access
277      * is primarily made through JOF_SCOPECOORD ops which is why these members
278      * take a ScopeCoordinate instead of just the slot index.
279      */
280     inline const Value& aliasedVar(ScopeCoordinate sc);
281 
282     inline void setAliasedVar(JSContext* cx, ScopeCoordinate sc, PropertyName* name, const Value& v);
283 
284     /* For jit access. */
offsetOfEnclosingScope()285     static size_t offsetOfEnclosingScope() {
286         return getFixedSlotOffset(SCOPE_CHAIN_SLOT);
287     }
288 
enclosingScopeSlot()289     static size_t enclosingScopeSlot() {
290         return SCOPE_CHAIN_SLOT;
291     }
292 };
293 
294 class LexicalScopeBase : public ScopeObject
295 {
296   protected:
297     inline void initRemainingSlotsToUninitializedLexicals(uint32_t begin);
298     inline void initAliasedLexicalsToThrowOnTouch(JSScript* script);
299 
300   public:
301     /* Get/set the aliased variable referred to by 'fi'. */
aliasedVar(AliasedFormalIter fi)302     const Value& aliasedVar(AliasedFormalIter fi) {
303         return getSlot(fi.scopeSlot());
304     }
305     inline void setAliasedVar(JSContext* cx, AliasedFormalIter fi, PropertyName* name,
306                               const Value& v);
307 
308     /*
309      * When an aliased var (var accessed by nested closures) is also aliased by
310      * the arguments object, it must of course exist in one canonical location
311      * and that location is always the CallObject. For this to work, the
312      * ArgumentsObject stores special MagicValue in its array for forwarded-to-
313      * CallObject variables. This MagicValue's payload is the slot of the
314      * CallObject to access.
315      */
aliasedVarFromArguments(const Value & argsValue)316     const Value& aliasedVarFromArguments(const Value& argsValue) {
317         return getSlot(ArgumentsObject::SlotFromMagicScopeSlotValue(argsValue));
318     }
319     inline void setAliasedVarFromArguments(JSContext* cx, const Value& argsValue, jsid id,
320                                            const Value& v);
321 };
322 
323 class CallObject : public LexicalScopeBase
324 {
325   protected:
326     static const uint32_t CALLEE_SLOT = 1;
327 
328     static CallObject*
329     create(JSContext* cx, HandleScript script, HandleObject enclosing, HandleFunction callee);
330 
331   public:
332     static const Class class_;
333 
334     /* These functions are internal and are exposed only for JITs. */
335 
336     /*
337      * Construct a bare-bones call object given a shape and a non-singleton
338      * group.  The call object must be further initialized to be usable.
339      */
340     static CallObject*
341     create(JSContext* cx, HandleShape shape, HandleObjectGroup group, uint32_t lexicalBegin);
342 
343     /*
344      * Construct a bare-bones call object given a shape and make it into
345      * a singleton.  The call object must be initialized to be usable.
346      */
347     static CallObject*
348     createSingleton(JSContext* cx, HandleShape shape, uint32_t lexicalBegin);
349 
350     static CallObject*
351     createTemplateObject(JSContext* cx, HandleScript script, gc::InitialHeap heap);
352 
353     static const uint32_t RESERVED_SLOTS = 2;
354 
355     static CallObject* createForFunction(JSContext* cx, HandleObject enclosing, HandleFunction callee);
356 
357     static CallObject* createForFunction(JSContext* cx, AbstractFramePtr frame);
358     static CallObject* createForStrictEval(JSContext* cx, AbstractFramePtr frame);
359     static CallObject* createHollowForDebug(JSContext* cx, HandleFunction callee);
360 
361     /* True if this is for a strict mode eval frame. */
isForEval()362     bool isForEval() const {
363         if (is<ModuleEnvironmentObject>())
364             return false;
365         MOZ_ASSERT(getFixedSlot(CALLEE_SLOT).isObjectOrNull());
366         MOZ_ASSERT_IF(getFixedSlot(CALLEE_SLOT).isObject(),
367                       getFixedSlot(CALLEE_SLOT).toObject().is<JSFunction>());
368         return getFixedSlot(CALLEE_SLOT).isNull();
369     }
370 
371     /*
372      * Returns the function for which this CallObject was created. (This may
373      * only be called if !isForEval.)
374      */
callee()375     JSFunction& callee() const {
376         MOZ_ASSERT(!is<ModuleEnvironmentObject>());
377         return getFixedSlot(CALLEE_SLOT).toObject().as<JSFunction>();
378     }
379 
380     /* For jit access. */
offsetOfCallee()381     static size_t offsetOfCallee() {
382         return getFixedSlotOffset(CALLEE_SLOT);
383     }
384 
calleeSlot()385     static size_t calleeSlot() {
386         return CALLEE_SLOT;
387     }
388 };
389 
390 class ModuleEnvironmentObject : public LexicalScopeBase
391 {
392     static const uint32_t MODULE_SLOT = 1;
393 
394   public:
395     static const Class class_;
396 
397     static const uint32_t RESERVED_SLOTS = 2;
398 
399     static ModuleEnvironmentObject* create(ExclusiveContext* cx, HandleModuleObject module);
400     ModuleObject& module();
401     IndirectBindingMap& importBindings();
402 
403     bool createImportBinding(JSContext* cx, HandleAtom importName, HandleModuleObject module,
404                              HandleAtom exportName);
405 
406     bool hasImportBinding(HandlePropertyName name);
407 
408     bool lookupImport(jsid name, ModuleEnvironmentObject** envOut, Shape** shapeOut);
409 
410   private:
411     static bool lookupProperty(JSContext* cx, HandleObject obj, HandleId id,
412                                MutableHandleObject objp, MutableHandleShape propp);
413     static bool hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp);
414     static bool getProperty(JSContext* cx, HandleObject obj, HandleValue receiver, HandleId id,
415                             MutableHandleValue vp);
416     static bool setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
417                             HandleValue receiver, JS::ObjectOpResult& result);
418     static bool getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
419                                          MutableHandle<JSPropertyDescriptor> desc);
420     static bool deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
421                                ObjectOpResult& result);
422     static bool enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
423                           bool enumerableOnly);
424 };
425 
426 typedef Rooted<ModuleEnvironmentObject*> RootedModuleEnvironmentObject;
427 typedef Handle<ModuleEnvironmentObject*> HandleModuleEnvironmentObject;
428 typedef MutableHandle<ModuleEnvironmentObject*> MutableHandleModuleEnvironmentObject;
429 
430 class DeclEnvObject : public ScopeObject
431 {
432     // Pre-allocated slot for the named lambda.
433     static const uint32_t LAMBDA_SLOT = 1;
434 
435   public:
436     static const uint32_t RESERVED_SLOTS = 2;
437     static const Class class_;
438 
439     static DeclEnvObject*
440     createTemplateObject(JSContext* cx, HandleFunction fun, NewObjectKind newKind);
441 
442     static DeclEnvObject* create(JSContext* cx, HandleObject enclosing, HandleFunction callee);
443 
lambdaSlot()444     static inline size_t lambdaSlot() {
445         return LAMBDA_SLOT;
446     }
447 };
448 
449 // Static eval scope placeholder objects on the static scope chain. Created at
450 // the time of compiling the eval script, and set as its static enclosing
451 // scope.
452 class StaticEvalObject : public ScopeObject
453 {
454     static const uint32_t STRICT_SLOT = 1;
455 
456   public:
457     static const unsigned RESERVED_SLOTS = 2;
458     static const Class class_;
459 
460     static StaticEvalObject* create(JSContext* cx, HandleObject enclosing);
461 
enclosingScopeForStaticScopeIter()462     JSObject* enclosingScopeForStaticScopeIter() {
463         return getReservedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();
464     }
465 
setStrict()466     void setStrict() {
467         setReservedSlot(STRICT_SLOT, BooleanValue(true));
468     }
469 
isStrict()470     bool isStrict() const {
471         return getReservedSlot(STRICT_SLOT).isTrue();
472     }
473 
474     inline bool isNonGlobal() const;
475 };
476 
477 /*
478  * Non-syntactic scope objects
479  *
480  * A non-syntactic scope is one that was not created due to source code. On
481  * the static scope chain, a single StaticNonSyntacticScopeObjects maps to 0+
482  * non-syntactic dynamic scope objects. This is contrasted with syntactic
483  * scopes, where each syntactic static scope corresponds to 0 or 1 dynamic
484  * scope objects.
485  *
486  * There are 3 kinds of dynamic non-syntactic scopes:
487  *
488  * 1. DynamicWithObject
489  *
490  *    When the embedding compiles or executes a script, it has the option to
491  *    pass in a vector of objects to be used as the initial scope chain. Each
492  *    of those objects is wrapped by a DynamicWithObject.
493  *
494  *    The innermost scope passed in by the embedding becomes a qualified
495  *    variables object that captures 'var' bindings. That is, it wraps the
496  *    holder object of 'var' bindings.
497  *
498  *    Does not hold 'let' or 'const' bindings.
499  *
500  * 2. NonSyntacticVariablesObject
501  *
502  *    When the embedding wants qualified 'var' bindings and unqualified
503  *    bareword assignments to go on a different object than the global
504  *    object. While any object can be made into a qualified variables object,
505  *    only the GlobalObject and NonSyntacticVariablesObject are considered
506  *    unqualified variables objects.
507  *
508  *    Unlike DynamicWithObjects, this object is itself the holder of 'var'
509  *    bindings.
510  *
511  *    Does not hold 'let' or 'const' bindings.
512  *
513  * 3. ClonedBlockObject
514  *
515  *    Each non-syntactic object used as a qualified variables object needs to
516  *    enclose a non-syntactic ClonedBlockObject to hold 'let' and 'const'
517  *    bindings. There is a bijection per compartment between the non-syntactic
518  *    variables objects and their non-syntactic ClonedBlockObjects.
519  *
520  *    Does not hold 'var' bindings.
521  *
522  * The embedding (Gecko) uses non-syntactic scopes for various things, some of
523  * which are detailed below. All scope chain listings below are, from top to
524  * bottom, outermost to innermost.
525  *
526  * A. Component loading
527  *
528  * Components may be loaded in "reuse loader global" mode, where to save on
529  * memory, all JSMs and JS-implemented XPCOM modules are loaded into a single
530  * global. Each individual JSMs are compiled as functions with their own
531  * FakeBackstagePass. They have the following dynamic scope chain:
532  *
533  *   BackstagePass global
534  *       |
535  *   Global lexical scope
536  *       |
537  *   DynamicWithObject wrapping FakeBackstagePass
538  *       |
539  *   Non-syntactic lexical scope
540  *
541  * B. Subscript loading
542  *
543  * Subscripts may be loaded into a target object. They have the following
544  * dynamic scope chain:
545  *
546  *   Loader global
547  *       |
548  *   Global lexical scope
549  *       |
550  *   DynamicWithObject wrapping target
551  *       |
552  *   ClonedBlockObject
553  *
554  * C. Frame scripts
555  *
556  * XUL frame scripts are always loaded with a NonSyntacticVariablesObject as a
557  * "polluting global". This is done exclusively in
558  * js::ExecuteInGlobalAndReturnScope.
559  *
560  *   Loader global
561  *       |
562  *   Global lexical scope
563  *       |
564  *   NonSyntacticVariablesObject
565  *       |
566  *   ClonedBlockObject
567  *
568  * D. XBL
569  *
570  * XBL methods are compiled as functions with XUL elements on the scope chain.
571  * For a chain of elements e0,...,eN:
572  *
573  *      ...
574  *       |
575  *   DynamicWithObject wrapping eN
576  *       |
577  *      ...
578  *       |
579  *   DynamicWithObject wrapping e0
580  *       |
581  *   ClonedBlockObject
582  *
583  */
584 class StaticNonSyntacticScopeObjects : public ScopeObject
585 {
586   public:
587     static const unsigned RESERVED_SLOTS = 1;
588     static const Class class_;
589 
590     static StaticNonSyntacticScopeObjects* create(JSContext* cx, HandleObject enclosing);
591 
enclosingScopeForStaticScopeIter()592     JSObject* enclosingScopeForStaticScopeIter() {
593         return getReservedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();
594     }
595 };
596 
597 // A non-syntactic dynamic scope object that captures non-lexical
598 // bindings. That is, a scope object that captures both qualified var
599 // assignments and unqualified bareword assignments. Its parent is always the
600 // global lexical scope.
601 //
602 // This is used in ExecuteInGlobalAndReturnScope and sits in front of the
603 // global scope to capture 'var' and bareword asignments.
604 class NonSyntacticVariablesObject : public ScopeObject
605 {
606   public:
607     static const unsigned RESERVED_SLOTS = 1;
608     static const Class class_;
609 
610     static NonSyntacticVariablesObject* create(JSContext* cx,
611                                                Handle<ClonedBlockObject*> globalLexical);
612 };
613 
614 class NestedScopeObject : public ScopeObject
615 {
616   public:
617     /*
618      * A refinement of enclosingScope that returns nullptr if the enclosing
619      * scope is not a NestedScopeObject.
620      */
621     inline NestedScopeObject* enclosingNestedScope() const;
622 
623     // Return true if this object is a compile-time scope template.
isStatic()624     inline bool isStatic() { return !getProto(); }
625 
626     // Return the static scope corresponding to this scope chain object.
staticScope()627     inline NestedScopeObject* staticScope() {
628         MOZ_ASSERT(!isStatic());
629         return &getProto()->as<NestedScopeObject>();
630     }
631 
632     // At compile-time it's possible for the scope chain to be null.
enclosingScopeForStaticScopeIter()633     JSObject* enclosingScopeForStaticScopeIter() {
634         return getReservedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();
635     }
636 
initEnclosingScope(JSObject * obj)637     void initEnclosingScope(JSObject* obj) {
638         MOZ_ASSERT(getReservedSlot(SCOPE_CHAIN_SLOT).isUndefined());
639         setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(obj));
640     }
641 
642     /*
643      * Note: in the case of hoisting, this prev-link will not ultimately be
644      * the same as enclosingNestedScope; initEnclosingNestedScope must be
645      * called separately in the emitter. 'reset' is just for asserting
646      * stackiness.
647      */
initEnclosingScopeFromParser(JSObject * prev)648     void initEnclosingScopeFromParser(JSObject* prev) {
649         setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(prev));
650     }
651 
resetEnclosingScopeFromParser()652     void resetEnclosingScopeFromParser() {
653         setReservedSlot(SCOPE_CHAIN_SLOT, UndefinedValue());
654     }
655 };
656 
657 // With scope template objects on the static scope chain.
658 class StaticWithObject : public NestedScopeObject
659 {
660   public:
661     static const unsigned RESERVED_SLOTS = 1;
662     static const Class class_;
663 
664     static StaticWithObject* create(ExclusiveContext* cx);
665 };
666 
667 // With scope objects on the run-time scope chain.
668 class DynamicWithObject : public NestedScopeObject
669 {
670     static const unsigned OBJECT_SLOT = 1;
671     static const unsigned THIS_SLOT = 2;
672     static const unsigned KIND_SLOT = 3;
673 
674   public:
675     static const unsigned RESERVED_SLOTS = 4;
676     static const Class class_;
677 
678     enum WithKind {
679         SyntacticWith,
680         NonSyntacticWith
681     };
682 
683     static DynamicWithObject*
684     create(JSContext* cx, HandleObject object, HandleObject enclosing, HandleObject staticWith,
685            WithKind kind = SyntacticWith);
686 
staticWith()687     StaticWithObject& staticWith() const {
688         return getProto()->as<StaticWithObject>();
689     }
690 
691     /* Return the 'o' in 'with (o)'. */
object()692     JSObject& object() const {
693         return getReservedSlot(OBJECT_SLOT).toObject();
694     }
695 
696     /* Return object for GetThisValue. */
withThis()697     JSObject* withThis() const {
698         return &getReservedSlot(THIS_SLOT).toObject();
699     }
700 
701     /*
702      * Return whether this object is a syntactic with object.  If not, this is a
703      * With object we inserted between the outermost syntactic scope and the
704      * global object to wrap the scope chain someone explicitly passed via JSAPI
705      * to CompileFunction or script evaluation.
706      */
isSyntactic()707     bool isSyntactic() const {
708         return getReservedSlot(KIND_SLOT).toInt32() == SyntacticWith;
709     }
710 
objectSlot()711     static inline size_t objectSlot() {
712         return OBJECT_SLOT;
713     }
714 
thisSlot()715     static inline size_t thisSlot() {
716         return THIS_SLOT;
717     }
718 };
719 
720 class BlockObject : public NestedScopeObject
721 {
722   public:
723     static const unsigned RESERVED_SLOTS = 2;
724     static const Class class_;
725 
726     /* Return the number of variables associated with this block. */
numVariables()727     uint32_t numVariables() const {
728         // TODO: propertyCount() is O(n), use O(1) lastProperty()->slot() instead
729         return propertyCount();
730     }
731 
732     // Global lexical scopes are extensible. Non-global lexicals scopes are
733     // not.
734     bool isExtensible() const;
735 
736   protected:
737     /* Blocks contain an object slot for each slot i: 0 <= i < slotCount. */
slotValue(unsigned i)738     const Value& slotValue(unsigned i) {
739         return getSlotRef(RESERVED_SLOTS + i);
740     }
741 
setSlotValue(unsigned i,const Value & v)742     void setSlotValue(unsigned i, const Value& v) {
743         setSlot(RESERVED_SLOTS + i, v);
744     }
745 };
746 
747 class StaticBlockObject : public BlockObject
748 {
749     static const unsigned LOCAL_OFFSET_SLOT = 1;
750 
751   public:
752     static StaticBlockObject* create(ExclusiveContext* cx);
753 
754     /* See StaticScopeIter comment. */
enclosingStaticScope()755     JSObject* enclosingStaticScope() const {
756         return getFixedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();
757     }
758 
759     /*
760      * Return the index (in the range [0, numVariables()) corresponding to the
761      * given shape of a block object.
762      */
shapeToIndex(const Shape & shape)763     uint32_t shapeToIndex(const Shape& shape) {
764         uint32_t slot = shape.slot();
765         MOZ_ASSERT(slot - RESERVED_SLOTS < numVariables());
766         return slot - RESERVED_SLOTS;
767     }
768 
769     /*
770      * A refinement of enclosingStaticScope that returns nullptr if the enclosing
771      * static scope is a JSFunction.
772      */
773     inline StaticBlockObject* enclosingBlock() const;
774 
localOffset()775     uint32_t localOffset() {
776         return getReservedSlot(LOCAL_OFFSET_SLOT).toPrivateUint32();
777     }
778 
779     // Return the local corresponding to the 'var'th binding where 'var' is in the
780     // range [0, numVariables()).
blockIndexToLocalIndex(uint32_t index)781     uint32_t blockIndexToLocalIndex(uint32_t index) {
782         MOZ_ASSERT(index < numVariables());
783         return getReservedSlot(LOCAL_OFFSET_SLOT).toPrivateUint32() + index;
784     }
785 
786     // Return the slot corresponding to block index 'index', where 'index' is
787     // in the range [0, numVariables()).  The result is in the range
788     // [RESERVED_SLOTS, RESERVED_SLOTS + numVariables()).
blockIndexToSlot(uint32_t index)789     uint32_t blockIndexToSlot(uint32_t index) {
790         MOZ_ASSERT(index < numVariables());
791         return RESERVED_SLOTS + index;
792     }
793 
794     // Return the slot corresponding to local variable 'local', where 'local' is
795     // in the range [localOffset(), localOffset() + numVariables()).  The result is
796     // in the range [RESERVED_SLOTS, RESERVED_SLOTS + numVariables()).
localIndexToSlot(uint32_t local)797     uint32_t localIndexToSlot(uint32_t local) {
798         MOZ_ASSERT(local >= localOffset());
799         return blockIndexToSlot(local - localOffset());
800     }
801 
802     /*
803      * A let binding is aliased if accessed lexically by nested functions or
804      * dynamically through dynamic name lookup (eval, with, function::, etc).
805      */
isAliased(unsigned i)806     bool isAliased(unsigned i) {
807         return slotValue(i).isTrue();
808     }
809 
810     // Look up if the block has an aliased binding named |name|.
811     Shape* lookupAliasedName(PropertyName* name);
812 
813     /*
814      * A static block object is cloned (when entering the block) iff some
815      * variable of the block isAliased.
816      */
needsClone()817     bool needsClone() {
818         return numVariables() > 0 && !getSlot(RESERVED_SLOTS).isFalse();
819     }
820 
821     // Is this the static global lexical scope?
isGlobal()822     bool isGlobal() const {
823         return !enclosingStaticScope();
824     }
825 
isSyntactic()826     bool isSyntactic() const {
827         return !isExtensible() || isGlobal();
828     }
829 
830     /* Frontend-only functions ***********************************************/
831 
832     /* Initialization functions for above fields. */
setAliased(unsigned i,bool aliased)833     void setAliased(unsigned i, bool aliased) {
834         MOZ_ASSERT_IF(i > 0, slotValue(i-1).isBoolean());
835         setSlotValue(i, BooleanValue(aliased));
836         if (aliased && !needsClone()) {
837             setSlotValue(0, MagicValue(JS_BLOCK_NEEDS_CLONE));
838             MOZ_ASSERT(needsClone());
839         }
840     }
841 
setLocalOffset(uint32_t offset)842     void setLocalOffset(uint32_t offset) {
843         MOZ_ASSERT(getReservedSlot(LOCAL_OFFSET_SLOT).isUndefined());
844         initReservedSlot(LOCAL_OFFSET_SLOT, PrivateUint32Value(offset));
845     }
846 
847     /*
848      * Frontend compilation temporarily uses the object's slots to link
849      * a let var to its associated Definition parse node.
850      */
setDefinitionParseNode(unsigned i,frontend::Definition * def)851     void setDefinitionParseNode(unsigned i, frontend::Definition* def) {
852         MOZ_ASSERT(slotValue(i).isUndefined());
853         setSlotValue(i, PrivateValue(def));
854     }
855 
definitionParseNode(unsigned i)856     frontend::Definition* definitionParseNode(unsigned i) {
857         Value v = slotValue(i);
858         return reinterpret_cast<frontend::Definition*>(v.toPrivate());
859     }
860 
861     // Called by BytecodeEmitter to mark regular block scopes as
862     // non-extensible. By contrast, the global lexical scope is extensible.
863     bool makeNonExtensible(ExclusiveContext* cx);
864 
865     /*
866      * While ScopeCoordinate can generally reference up to 2^24 slots, block objects have an
867      * additional limitation that all slot indices must be storable as uint16_t short-ids in the
868      * associated Shape. If we could remove the block dependencies on shape->shortid, we could
869      * remove INDEX_LIMIT.
870      */
871     static const unsigned LOCAL_INDEX_LIMIT = JS_BIT(16);
872 
873     static Shape* addVar(ExclusiveContext* cx, Handle<StaticBlockObject*> block, HandleId id,
874                          bool constant, unsigned index, bool* redeclared);
875 };
876 
877 class ClonedBlockObject : public BlockObject
878 {
879     static const unsigned THIS_VALUE_SLOT = 1;
880 
881     static ClonedBlockObject* create(JSContext* cx, Handle<StaticBlockObject*> block,
882                                      HandleObject enclosing);
883 
884   public:
885     static ClonedBlockObject* create(JSContext* cx, Handle<StaticBlockObject*> block,
886                                      AbstractFramePtr frame);
887 
888     static ClonedBlockObject* createGlobal(JSContext* cx, Handle<GlobalObject*> global);
889 
890     static ClonedBlockObject* createNonSyntactic(JSContext* cx, HandleObject enclosingStatic,
891                                                  HandleObject enclosingScope);
892 
893     static ClonedBlockObject* createHollowForDebug(JSContext* cx,
894                                                    Handle<StaticBlockObject*> block);
895 
896     /* The static block from which this block was cloned. */
staticBlock()897     StaticBlockObject& staticBlock() const {
898         return getProto()->as<StaticBlockObject>();
899     }
900 
901     /* Assuming 'put' has been called, return the value of the ith let var. */
902     const Value& var(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) {
903         MOZ_ASSERT_IF(checkAliasing, staticBlock().isAliased(i));
904         return slotValue(i);
905     }
906 
907     void setVar(unsigned i, const Value& v, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) {
908         MOZ_ASSERT_IF(checkAliasing, staticBlock().isAliased(i));
909         setSlotValue(i, v);
910     }
911 
912     // Is this the global lexical scope?
isGlobal()913     bool isGlobal() const {
914         MOZ_ASSERT_IF(staticBlock().isGlobal(), enclosingScope().is<GlobalObject>());
915         return enclosingScope().is<GlobalObject>();
916     }
917 
global()918     GlobalObject& global() const {
919         MOZ_ASSERT(isGlobal());
920         return enclosingScope().as<GlobalObject>();
921     }
922 
isSyntactic()923     bool isSyntactic() const {
924         return !isExtensible() || isGlobal();
925     }
926 
927     /* Copy in all the unaliased formals and locals. */
928     void copyUnaliasedValues(AbstractFramePtr frame);
929 
930     /*
931      * Create a new ClonedBlockObject with the same enclosing scope and
932      * variable values as this.
933      */
934     static ClonedBlockObject* clone(JSContext* cx, Handle<ClonedBlockObject*> block);
935 
936     Value thisValue() const;
937 };
938 
939 // Internal scope object used by JSOP_BINDNAME upon encountering an
940 // uninitialized lexical slot or an assignment to a 'const' binding.
941 //
942 // ES6 lexical bindings cannot be accessed in any way (throwing
943 // ReferenceErrors) until initialized. Normally, NAME operations
944 // unconditionally check for uninitialized lexical slots. When getting or
945 // looking up names, this can be done without slowing down normal operations
946 // on the return value. When setting names, however, we do not want to pollute
947 // all set-property paths with uninitialized lexical checks. For setting names
948 // (i.e. JSOP_SETNAME), we emit an accompanying, preceding JSOP_BINDNAME which
949 // finds the right scope on which to set the name. Moreover, when the name on
950 // the scope is an uninitialized lexical, we cannot throw eagerly, as the spec
951 // demands that the error be thrown after evaluating the RHS of
952 // assignments. Instead, this sentinel scope object is pushed on the stack.
953 // Attempting to access anything on this scope throws the appropriate
954 // ReferenceError.
955 //
956 // ES6 'const' bindings induce a runtime error when assigned to outside
957 // of initialization, regardless of strictness.
958 class RuntimeLexicalErrorObject : public ScopeObject
959 {
960     static const unsigned ERROR_SLOT = 1;
961 
962   public:
963     static const unsigned RESERVED_SLOTS = 2;
964     static const Class class_;
965 
966     static RuntimeLexicalErrorObject* create(JSContext* cx, HandleObject enclosing,
967                                              unsigned errorNumber);
968 
errorNumber()969     unsigned errorNumber() {
970         return getReservedSlot(ERROR_SLOT).toInt32();
971     }
972 };
973 
974 template<XDRMode mode>
975 bool
976 XDRStaticBlockObject(XDRState<mode>* xdr, HandleObject enclosingScope,
977                      MutableHandle<StaticBlockObject*> objp);
978 
979 template<XDRMode mode>
980 bool
981 XDRStaticWithObject(XDRState<mode>* xdr, HandleObject enclosingScope,
982                     MutableHandle<StaticWithObject*> objp);
983 
984 extern JSObject*
985 CloneNestedScopeObject(JSContext* cx, HandleObject enclosingScope, Handle<NestedScopeObject*> src);
986 
987 /*****************************************************************************/
988 
989 // A scope iterator describes the active scopes starting from a dynamic scope,
990 // static scope pair. This pair may be derived from the current point of
991 // execution in a frame. If derived in such a fashion, the ScopeIter tracks
992 // whether the current scope is within the extent of this initial frame.
993 // Here, "frame" means a single activation of: a function, eval, or global
994 // code.
995 class MOZ_RAII ScopeIter
996 {
997     StaticScopeIter<CanGC> ssi_;
998     RootedObject scope_;
999     AbstractFramePtr frame_;
1000 
1001     void incrementStaticScopeIter();
1002     void settle();
1003 
1004     // No value semantics.
1005     ScopeIter(const ScopeIter& si) = delete;
1006 
1007   public:
1008     // Constructing from a copy of an existing ScopeIter.
1009     ScopeIter(JSContext* cx, const ScopeIter& si
1010               MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
1011 
1012     // Constructing from a dynamic scope, static scope pair. All scopes are
1013     // considered not to be withinInitialFrame, since no frame is given.
1014     ScopeIter(JSContext* cx, JSObject* scope, JSObject* staticScope
1015               MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
1016 
1017     // Constructing from a frame. Places the ScopeIter on the innermost scope
1018     // at pc.
1019     ScopeIter(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc
1020               MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
1021 
1022     inline bool done() const;
1023     ScopeIter& operator++();
1024 
1025     // If done():
1026     inline JSObject& enclosingScope() const;
1027 
1028     // If !done():
1029     enum Type { Module, Call, Block, With, Eval, NonSyntactic };
1030     Type type() const;
1031 
1032     inline bool hasNonSyntacticScopeObject() const;
1033     inline bool hasSyntacticScopeObject() const;
1034     inline bool hasAnyScopeObject() const;
1035     inline bool canHaveSyntacticScopeObject() const;
1036     ScopeObject& scope() const;
1037 
1038     JSObject* maybeStaticScope() const;
staticBlock()1039     StaticBlockObject& staticBlock() const { return ssi_.block(); }
staticWith()1040     StaticWithObject& staticWith() const { return ssi_.staticWith(); }
staticEval()1041     StaticEvalObject& staticEval() const { return ssi_.eval(); }
staticNonSyntactic()1042     StaticNonSyntacticScopeObjects& staticNonSyntactic() const { return ssi_.nonSyntactic(); }
fun()1043     JSFunction& fun() const { return ssi_.fun(); }
module()1044     ModuleObject& module() const { return ssi_.module(); }
1045 
withinInitialFrame()1046     bool withinInitialFrame() const { return !!frame_; }
initialFrame()1047     AbstractFramePtr initialFrame() const { MOZ_ASSERT(withinInitialFrame()); return frame_; }
maybeInitialFrame()1048     AbstractFramePtr maybeInitialFrame() const { return frame_; }
1049 
1050     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
1051 };
1052 
1053 // The key in MissingScopeMap. For live frames, maps live frames to their
1054 // synthesized scopes. For completely optimized-out scopes, maps the static
1055 // scope objects to their synthesized scopes. The scopes we synthesize for
1056 // static scope objects are read-only, and we never use their parent links, so
1057 // they don't need to be distinct.
1058 //
1059 // That is, completely optimized out scopes can't be distinguished by
1060 // frame. Note that even if the frame corresponding to the static scope is
1061 // live on the stack, it is unsound to synthesize a scope from that live
1062 // frame. In other words, the provenance of the scope chain is from allocated
1063 // closures (i.e., allocation sites) and is irrecoverable from simple stack
1064 // inspection (i.e., call sites).
1065 class MissingScopeKey
1066 {
1067     friend class LiveScopeVal;
1068 
1069     AbstractFramePtr frame_;
1070     JSObject* staticScope_;
1071 
1072   public:
MissingScopeKey(const ScopeIter & si)1073     explicit MissingScopeKey(const ScopeIter& si)
1074       : frame_(si.maybeInitialFrame()),
1075         staticScope_(si.maybeStaticScope())
1076     { }
1077 
frame()1078     AbstractFramePtr frame() const { return frame_; }
staticScope()1079     JSObject* staticScope() const { return staticScope_; }
1080 
updateStaticScope(JSObject * obj)1081     void updateStaticScope(JSObject* obj) { staticScope_ = obj; }
updateFrame(AbstractFramePtr frame)1082     void updateFrame(AbstractFramePtr frame) { frame_ = frame; }
1083 
1084     // For use as hash policy.
1085     typedef MissingScopeKey Lookup;
1086     static HashNumber hash(MissingScopeKey sk);
1087     static bool match(MissingScopeKey sk1, MissingScopeKey sk2);
1088     bool operator!=(const MissingScopeKey& other) const {
1089         return frame_ != other.frame_ || staticScope_ != other.staticScope_;
1090     }
rekey(MissingScopeKey & k,const MissingScopeKey & newKey)1091     static void rekey(MissingScopeKey& k, const MissingScopeKey& newKey) {
1092         k = newKey;
1093     }
1094 };
1095 
1096 // The value in LiveScopeMap, mapped from by live scope objects.
1097 class LiveScopeVal
1098 {
1099     friend class DebugScopes;
1100     friend class MissingScopeKey;
1101 
1102     AbstractFramePtr frame_;
1103     RelocatablePtrObject staticScope_;
1104 
1105     static void staticAsserts();
1106 
1107   public:
LiveScopeVal(const ScopeIter & si)1108     explicit LiveScopeVal(const ScopeIter& si)
1109       : frame_(si.initialFrame()),
1110         staticScope_(si.maybeStaticScope())
1111     { }
1112 
frame()1113     AbstractFramePtr frame() const { return frame_; }
staticScope()1114     JSObject* staticScope() const { return staticScope_; }
1115 
updateFrame(AbstractFramePtr frame)1116     void updateFrame(AbstractFramePtr frame) { frame_ = frame; }
1117 
1118     bool needsSweep();
1119 };
1120 
1121 /*****************************************************************************/
1122 
1123 /*
1124  * Debug scope objects
1125  *
1126  * The debugger effectively turns every opcode into a potential direct eval.
1127  * Naively, this would require creating a ScopeObject for every call/block
1128  * scope and using JSOP_GETALIASEDVAR for every access. To optimize this, the
1129  * engine assumes there is no debugger and optimizes scope access and creation
1130  * accordingly. When the debugger wants to perform an unexpected eval-in-frame
1131  * (or other, similar dynamic-scope-requiring operations), fp->scopeChain is
1132  * now incomplete: it may not contain all, or any, of the ScopeObjects to
1133  * represent the current scope.
1134  *
1135  * To resolve this, the debugger first calls GetDebugScopeFor* to synthesize a
1136  * "debug scope chain". A debug scope chain is just a chain of objects that
1137  * fill in missing scopes and protect the engine from unexpected access. (The
1138  * latter means that some debugger operations, like redefining a lexical
1139  * binding, can fail when a true eval would succeed.) To do both of these
1140  * things, GetDebugScopeFor* creates a new proxy DebugScopeObject to sit in
1141  * front of every existing ScopeObject.
1142  *
1143  * GetDebugScopeFor* ensures the invariant that the same DebugScopeObject is
1144  * always produced for the same underlying scope (optimized or not!). This is
1145  * maintained by some bookkeeping information stored in DebugScopes.
1146  */
1147 
1148 extern JSObject*
1149 GetDebugScopeForFunction(JSContext* cx, HandleFunction fun);
1150 
1151 extern JSObject*
1152 GetDebugScopeForFrame(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc);
1153 
1154 extern JSObject*
1155 GetDebugScopeForGlobalLexicalScope(JSContext* cx);
1156 
1157 /* Provides debugger access to a scope. */
1158 class DebugScopeObject : public ProxyObject
1159 {
1160     /*
1161      * The enclosing scope on the dynamic scope chain. This slot is analogous
1162      * to the SCOPE_CHAIN_SLOT of a ScopeObject.
1163      */
1164     static const unsigned ENCLOSING_EXTRA = 0;
1165 
1166     /*
1167      * NullValue or a dense array holding the unaliased variables of a function
1168      * frame that has been popped.
1169      */
1170     static const unsigned SNAPSHOT_EXTRA = 1;
1171 
1172   public:
1173     static DebugScopeObject* create(JSContext* cx, ScopeObject& scope, HandleObject enclosing);
1174 
1175     ScopeObject& scope() const;
1176     JSObject& enclosingScope() const;
1177 
1178     /* May only be called for proxies to function call objects. */
1179     ArrayObject* maybeSnapshot() const;
1180     void initSnapshot(ArrayObject& snapshot);
1181 
1182     /* Currently, the 'declarative' scopes are Call and Block. */
1183     bool isForDeclarative() const;
1184 
1185     // Get a property by 'id', but returns sentinel values instead of throwing
1186     // on exceptional cases.
1187     bool getMaybeSentinelValue(JSContext* cx, HandleId id, MutableHandleValue vp);
1188 
1189     // Returns true iff this is a function scope with its own this-binding
1190     // (all functions except arrow functions and generator expression lambdas).
1191     bool isFunctionScopeWithThis();
1192 
1193     // Does this debug scope not have a dynamic counterpart or was never live
1194     // (and thus does not have a synthesized ScopeObject or a snapshot)?
1195     bool isOptimizedOut() const;
1196 };
1197 
1198 /* Maintains per-compartment debug scope bookkeeping information. */
1199 class DebugScopes
1200 {
1201     /* The map from (non-debug) scopes to debug scopes. */
1202     ObjectWeakMap proxiedScopes;
1203 
1204     /*
1205      * The map from live frames which have optimized-away scopes to the
1206      * corresponding debug scopes.
1207      */
1208     typedef HashMap<MissingScopeKey,
1209                     ReadBarrieredDebugScopeObject,
1210                     MissingScopeKey,
1211                     RuntimeAllocPolicy> MissingScopeMap;
1212     MissingScopeMap missingScopes;
1213 
1214     /*
1215      * The map from scope objects of live frames to the live frame. This map
1216      * updated lazily whenever the debugger needs the information. In between
1217      * two lazy updates, liveScopes becomes incomplete (but not invalid, onPop*
1218      * removes scopes as they are popped). Thus, two consecutive debugger lazy
1219      * updates of liveScopes need only fill in the new scopes.
1220      */
1221     typedef GCHashMap<ReadBarriered<ScopeObject*>,
1222                       LiveScopeVal,
1223                       MovableCellHasher<ReadBarriered<ScopeObject*>>,
1224                       RuntimeAllocPolicy> LiveScopeMap;
1225     LiveScopeMap liveScopes;
1226     static MOZ_ALWAYS_INLINE void liveScopesPostWriteBarrier(JSRuntime* rt, LiveScopeMap* map,
1227                                                              ScopeObject* key);
1228 
1229   public:
1230     explicit DebugScopes(JSContext* c);
1231     ~DebugScopes();
1232 
1233   private:
1234     bool init();
1235 
1236     static DebugScopes* ensureCompartmentData(JSContext* cx);
1237 
1238   public:
1239     void mark(JSTracer* trc);
1240     void sweep(JSRuntime* rt);
1241 #ifdef JS_GC_ZEAL
1242     void checkHashTablesAfterMovingGC(JSRuntime* rt);
1243 #endif
1244 
1245     static DebugScopeObject* hasDebugScope(JSContext* cx, ScopeObject& scope);
1246     static bool addDebugScope(JSContext* cx, ScopeObject& scope, DebugScopeObject& debugScope);
1247 
1248     static DebugScopeObject* hasDebugScope(JSContext* cx, const ScopeIter& si);
1249     static bool addDebugScope(JSContext* cx, const ScopeIter& si, DebugScopeObject& debugScope);
1250 
1251     static bool updateLiveScopes(JSContext* cx);
1252     static LiveScopeVal* hasLiveScope(ScopeObject& scope);
1253     static void unsetPrevUpToDateUntil(JSContext* cx, AbstractFramePtr frame);
1254 
1255     // When a frame bails out from Ion to Baseline, there might be missing
1256     // scopes keyed on, and live scopes containing, the old
1257     // RematerializedFrame. Forward those values to the new BaselineFrame.
1258     static void forwardLiveFrame(JSContext* cx, AbstractFramePtr from, AbstractFramePtr to);
1259 
1260     // In debug-mode, these must be called whenever exiting a scope that might
1261     // have stack-allocated locals.
1262     static void onPopCall(AbstractFramePtr frame, JSContext* cx);
1263     static void onPopBlock(JSContext* cx, const ScopeIter& si);
1264     static void onPopBlock(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc);
1265     static void onPopWith(AbstractFramePtr frame);
1266     static void onPopStrictEvalScope(AbstractFramePtr frame);
1267     static void onCompartmentUnsetIsDebuggee(JSCompartment* c);
1268 };
1269 
1270 }  /* namespace js */
1271 
1272 template<>
1273 inline bool
1274 JSObject::is<js::NestedScopeObject>() const
1275 {
1276     return is<js::BlockObject>() ||
1277            is<js::StaticWithObject>() ||
1278            is<js::DynamicWithObject>();
1279 }
1280 
1281 template<>
1282 inline bool
1283 JSObject::is<js::LexicalScopeBase>() const
1284 {
1285     return is<js::CallObject>() ||
1286            is<js::ModuleEnvironmentObject>();
1287 }
1288 
1289 template<>
1290 inline bool
1291 JSObject::is<js::ScopeObject>() const
1292 {
1293     return is<js::LexicalScopeBase>() ||
1294            is<js::DeclEnvObject>() ||
1295            is<js::NestedScopeObject>() ||
1296            is<js::RuntimeLexicalErrorObject>() ||
1297            is<js::NonSyntacticVariablesObject>();
1298 }
1299 
1300 template<>
1301 bool
1302 JSObject::is<js::DebugScopeObject>() const;
1303 
1304 template<>
1305 inline bool
1306 JSObject::is<js::ClonedBlockObject>() const
1307 {
1308     return is<js::BlockObject>() && !!getProto();
1309 }
1310 
1311 template<>
1312 inline bool
1313 JSObject::is<js::StaticBlockObject>() const
1314 {
1315     return is<js::BlockObject>() && !getProto();
1316 }
1317 
1318 namespace js {
1319 
1320 inline bool
IsSyntacticScope(JSObject * scope)1321 IsSyntacticScope(JSObject* scope)
1322 {
1323     if (!scope->is<ScopeObject>())
1324         return false;
1325 
1326     if (scope->is<DynamicWithObject>())
1327         return scope->as<DynamicWithObject>().isSyntactic();
1328 
1329     if (scope->is<ClonedBlockObject>())
1330         return scope->as<ClonedBlockObject>().isSyntactic();
1331 
1332     if (scope->is<NonSyntacticVariablesObject>())
1333         return false;
1334 
1335     return true;
1336 }
1337 
1338 inline bool
IsExtensibleLexicalScope(JSObject * scope)1339 IsExtensibleLexicalScope(JSObject* scope)
1340 {
1341     return scope->is<ClonedBlockObject>() && scope->as<ClonedBlockObject>().isExtensible();
1342 }
1343 
1344 inline bool
IsGlobalLexicalScope(JSObject * scope)1345 IsGlobalLexicalScope(JSObject* scope)
1346 {
1347     return scope->is<ClonedBlockObject>() && scope->as<ClonedBlockObject>().isGlobal();
1348 }
1349 
1350 inline bool
IsStaticGlobalLexicalScope(JSObject * scope)1351 IsStaticGlobalLexicalScope(JSObject* scope)
1352 {
1353     return scope->is<StaticBlockObject>() && scope->as<StaticBlockObject>().isGlobal();
1354 }
1355 
1356 inline const Value&
aliasedVar(ScopeCoordinate sc)1357 ScopeObject::aliasedVar(ScopeCoordinate sc)
1358 {
1359     MOZ_ASSERT(is<LexicalScopeBase>() || is<ClonedBlockObject>());
1360     return getSlot(sc.slot());
1361 }
1362 
1363 inline NestedScopeObject*
enclosingNestedScope()1364 NestedScopeObject::enclosingNestedScope() const
1365 {
1366     JSObject* obj = getReservedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();
1367     return obj && obj->is<NestedScopeObject>() ? &obj->as<NestedScopeObject>() : nullptr;
1368 }
1369 
1370 inline bool
isNonGlobal()1371 StaticEvalObject::isNonGlobal() const
1372 {
1373     if (isStrict())
1374         return true;
1375     return !IsStaticGlobalLexicalScope(&getReservedSlot(SCOPE_CHAIN_SLOT).toObject());
1376 }
1377 
1378 inline bool
done()1379 ScopeIter::done() const
1380 {
1381     return ssi_.done();
1382 }
1383 
1384 inline bool
hasSyntacticScopeObject()1385 ScopeIter::hasSyntacticScopeObject() const
1386 {
1387     return ssi_.hasSyntacticDynamicScopeObject();
1388 }
1389 
1390 inline bool
hasNonSyntacticScopeObject()1391 ScopeIter::hasNonSyntacticScopeObject() const
1392 {
1393     // The case we're worrying about here is a NonSyntactic static scope which
1394     // has 0+ corresponding non-syntactic DynamicWithObject scopes, a
1395     // NonSyntacticVariablesObject, or a non-syntactic ClonedBlockObject.
1396     if (ssi_.type() == StaticScopeIter<CanGC>::NonSyntactic) {
1397         MOZ_ASSERT_IF(scope_->is<DynamicWithObject>(),
1398                       !scope_->as<DynamicWithObject>().isSyntactic());
1399         return scope_->is<ScopeObject>() && !IsSyntacticScope(scope_);
1400     }
1401     return false;
1402 }
1403 
1404 inline bool
hasAnyScopeObject()1405 ScopeIter::hasAnyScopeObject() const
1406 {
1407     return hasSyntacticScopeObject() || hasNonSyntacticScopeObject();
1408 }
1409 
1410 inline bool
canHaveSyntacticScopeObject()1411 ScopeIter::canHaveSyntacticScopeObject() const
1412 {
1413     if (ssi_.done())
1414         return false;
1415 
1416     switch (type()) {
1417       case Module:
1418       case Call:
1419       case Block:
1420       case With:
1421         return true;
1422 
1423       case Eval:
1424         // Only strict eval scopes can have dynamic scope objects.
1425         return staticEval().isStrict();
1426 
1427       case NonSyntactic:
1428         return false;
1429     }
1430 
1431     // Silence warnings.
1432     return false;
1433 }
1434 
1435 inline JSObject&
enclosingScope()1436 ScopeIter::enclosingScope() const
1437 {
1438     // As an engine invariant (maintained internally and asserted by Execute),
1439     // ScopeObjects and non-ScopeObjects cannot be interleaved on the scope
1440     // chain; every scope chain must start with zero or more ScopeObjects and
1441     // terminate with one or more non-ScopeObjects (viz., GlobalObject).
1442     MOZ_ASSERT(done());
1443     MOZ_ASSERT(!IsSyntacticScope(scope_));
1444     return *scope_;
1445 }
1446 
1447 extern bool
1448 CreateScopeObjectsForScopeChain(JSContext* cx, AutoObjectVector& scopeChain,
1449                                 HandleObject dynamicTerminatingScope,
1450                                 MutableHandleObject dynamicScopeObj);
1451 
1452 bool HasNonSyntacticStaticScopeChain(JSObject* staticScope);
1453 uint32_t StaticScopeChainLength(JSObject* staticScope);
1454 
1455 ModuleEnvironmentObject* GetModuleEnvironmentForScript(JSScript* script);
1456 
1457 bool GetThisValueForDebuggerMaybeOptimizedOut(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc,
1458                                               MutableHandleValue res);
1459 
1460 bool CheckVarNameConflict(JSContext* cx, Handle<ClonedBlockObject*> lexicalScope,
1461                           HandlePropertyName name);
1462 
1463 bool CheckLexicalNameConflict(JSContext* cx, Handle<ClonedBlockObject*> lexicalScope,
1464                               HandleObject varObj, HandlePropertyName name);
1465 
1466 bool CheckGlobalDeclarationConflicts(JSContext* cx, HandleScript script,
1467                                      Handle<ClonedBlockObject*> lexicalScope,
1468                                      HandleObject varObj);
1469 
1470 bool CheckEvalDeclarationConflicts(JSContext* cx, HandleScript script,
1471                                    HandleObject scopeChain, HandleObject varObj);
1472 
1473 #ifdef DEBUG
1474 void DumpStaticScopeChain(JSScript* script);
1475 void DumpStaticScopeChain(JSObject* staticScope);
1476 bool
1477 AnalyzeEntrainedVariables(JSContext* cx, HandleScript script);
1478 #endif
1479 
1480 } // namespace js
1481 
1482 namespace JS {
1483 
1484 template <>
1485 struct DeletePolicy<js::DebugScopeObject>
1486 {
1487     explicit DeletePolicy(JSRuntime* rt) : rt_(rt) {}
1488     void operator()(const js::DebugScopeObject* ptr);
1489 
1490   private:
1491     JSRuntime* rt_;
1492 };
1493 
1494 } // namespace JS
1495 
1496 #endif /* vm_ScopeObject_h */
1497