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