1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/vm/ScopeObject.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,956 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#ifndef vm_ScopeObject_h 1.11 +#define vm_ScopeObject_h 1.12 + 1.13 +#include "jscntxt.h" 1.14 +#include "jsobj.h" 1.15 +#include "jsweakmap.h" 1.16 + 1.17 +#include "gc/Barrier.h" 1.18 +#include "vm/ProxyObject.h" 1.19 + 1.20 +namespace js { 1.21 + 1.22 +namespace frontend { struct Definition; } 1.23 + 1.24 +class StaticWithObject; 1.25 + 1.26 +/*****************************************************************************/ 1.27 + 1.28 +/* 1.29 + * All function scripts have an "enclosing static scope" that refers to the 1.30 + * innermost enclosing let or function in the program text. This allows full 1.31 + * reconstruction of the lexical scope for debugging or compiling efficient 1.32 + * access to variables in enclosing scopes. The static scope is represented at 1.33 + * runtime by a tree of compiler-created objects representing each scope: 1.34 + * - a StaticBlockObject is created for 'let' and 'catch' scopes 1.35 + * - a JSFunction+JSScript+Bindings trio is created for function scopes 1.36 + * (These objects are primarily used to clone objects scopes for the 1.37 + * dynamic scope chain.) 1.38 + * 1.39 + * There is an additional scope for named lambdas. E.g., in: 1.40 + * 1.41 + * (function f() { var x; function g() { } }) 1.42 + * 1.43 + * g's innermost enclosing scope will first be the function scope containing 1.44 + * 'x', enclosed by a scope containing only the name 'f'. (This separate scope 1.45 + * is necessary due to the fact that declarations in the function scope shadow 1.46 + * (dynamically, in the case of 'eval') the lambda name.) 1.47 + * 1.48 + * There are two limitations to the current lexical nesting information: 1.49 + * 1.50 + * - 'with' is completely absent; this isn't a problem for the current use 1.51 + * cases since 'with' causes every static scope to be on the dynamic scope 1.52 + * chain (so the debugger can find everything) and inhibits all upvar 1.53 + * optimization. 1.54 + * 1.55 + * - The "enclosing static scope" chain stops at 'eval'. For example in: 1.56 + * let (x) { eval("function f() {}") } 1.57 + * f does not have an enclosing static scope. This is fine for current uses 1.58 + * for the same reason as 'with'. 1.59 + * 1.60 + * (See also AssertDynamicScopeMatchesStaticScope.) 1.61 + */ 1.62 +template <AllowGC allowGC> 1.63 +class StaticScopeIter 1.64 +{ 1.65 + typename MaybeRooted<JSObject*, allowGC>::RootType obj; 1.66 + bool onNamedLambda; 1.67 + 1.68 + public: 1.69 + StaticScopeIter(ExclusiveContext *cx, JSObject *obj) 1.70 + : obj(cx, obj), onNamedLambda(false) 1.71 + { 1.72 + JS_STATIC_ASSERT(allowGC == CanGC); 1.73 + JS_ASSERT_IF(obj, obj->is<StaticBlockObject>() || obj->is<StaticWithObject>() || 1.74 + obj->is<JSFunction>()); 1.75 + } 1.76 + 1.77 + StaticScopeIter(JSObject *obj) 1.78 + : obj((ExclusiveContext *) nullptr, obj), onNamedLambda(false) 1.79 + { 1.80 + JS_STATIC_ASSERT(allowGC == NoGC); 1.81 + JS_ASSERT_IF(obj, obj->is<StaticBlockObject>() || obj->is<StaticWithObject>() || 1.82 + obj->is<JSFunction>()); 1.83 + } 1.84 + 1.85 + bool done() const; 1.86 + void operator++(int); 1.87 + 1.88 + /* Return whether this static scope will be on the dynamic scope chain. */ 1.89 + bool hasDynamicScopeObject() const; 1.90 + Shape *scopeShape() const; 1.91 + 1.92 + enum Type { WITH, BLOCK, FUNCTION, NAMED_LAMBDA }; 1.93 + Type type() const; 1.94 + 1.95 + StaticBlockObject &block() const; 1.96 + StaticWithObject &staticWith() const; 1.97 + JSScript *funScript() const; 1.98 +}; 1.99 + 1.100 +/*****************************************************************************/ 1.101 + 1.102 +/* 1.103 + * A "scope coordinate" describes how to get from head of the scope chain to a 1.104 + * given lexically-enclosing variable. A scope coordinate has two dimensions: 1.105 + * - hops: the number of scope objects on the scope chain to skip 1.106 + * - slot: the slot on the scope object holding the variable's value 1.107 + */ 1.108 +class ScopeCoordinate 1.109 +{ 1.110 + uint32_t hops_; 1.111 + uint32_t slot_; 1.112 + 1.113 + /* 1.114 + * Technically, hops_/slot_ are SCOPECOORD_(HOPS|SLOT)_BITS wide. Since 1.115 + * ScopeCoordinate is a temporary value, don't bother with a bitfield as 1.116 + * this only adds overhead. 1.117 + */ 1.118 + static_assert(SCOPECOORD_HOPS_BITS <= 32, "We have enough bits below"); 1.119 + static_assert(SCOPECOORD_SLOT_BITS <= 32, "We have enough bits below"); 1.120 + 1.121 + public: 1.122 + inline ScopeCoordinate(jsbytecode *pc) 1.123 + : hops_(GET_SCOPECOORD_HOPS(pc)), slot_(GET_SCOPECOORD_SLOT(pc + SCOPECOORD_HOPS_LEN)) 1.124 + { 1.125 + JS_ASSERT(JOF_OPTYPE(*pc) == JOF_SCOPECOORD); 1.126 + } 1.127 + 1.128 + inline ScopeCoordinate() {} 1.129 + 1.130 + void setHops(uint32_t hops) { JS_ASSERT(hops < SCOPECOORD_HOPS_LIMIT); hops_ = hops; } 1.131 + void setSlot(uint32_t slot) { JS_ASSERT(slot < SCOPECOORD_SLOT_LIMIT); slot_ = slot; } 1.132 + 1.133 + uint32_t hops() const { JS_ASSERT(hops_ < SCOPECOORD_HOPS_LIMIT); return hops_; } 1.134 + uint32_t slot() const { JS_ASSERT(slot_ < SCOPECOORD_SLOT_LIMIT); return slot_; } 1.135 +}; 1.136 + 1.137 +/* 1.138 + * Return a shape representing the static scope containing the variable 1.139 + * accessed by the ALIASEDVAR op at 'pc'. 1.140 + */ 1.141 +extern Shape * 1.142 +ScopeCoordinateToStaticScopeShape(JSScript *script, jsbytecode *pc); 1.143 + 1.144 +/* Return the name being accessed by the given ALIASEDVAR op. */ 1.145 +extern PropertyName * 1.146 +ScopeCoordinateName(ScopeCoordinateNameCache &cache, JSScript *script, jsbytecode *pc); 1.147 + 1.148 +/* Return the function script accessed by the given ALIASEDVAR op, or nullptr. */ 1.149 +extern JSScript * 1.150 +ScopeCoordinateFunctionScript(JSScript *script, jsbytecode *pc); 1.151 + 1.152 +/*****************************************************************************/ 1.153 + 1.154 +/* 1.155 + * Scope objects 1.156 + * 1.157 + * Scope objects are technically real JSObjects but only belong on the scope 1.158 + * chain (that is, fp->scopeChain() or fun->environment()). The hierarchy of 1.159 + * scope objects is: 1.160 + * 1.161 + * JSObject Generic object 1.162 + * \ 1.163 + * ScopeObject Engine-internal scope 1.164 + * \ \ \ 1.165 + * \ \ DeclEnvObject Holds name of recursive/heavyweight named lambda 1.166 + * \ \ 1.167 + * \ CallObject Scope of entire function or strict eval 1.168 + * \ 1.169 + * NestedScopeObject Scope created for a statement 1.170 + * \ \ \ 1.171 + * \ \ StaticWithObject Template for "with" object in static scope chain 1.172 + * \ \ 1.173 + * \ DynamicWithObject Run-time "with" object on scope chain 1.174 + * \ 1.175 + * BlockObject Shared interface of cloned/static block objects 1.176 + * \ \ 1.177 + * \ ClonedBlockObject let, switch, catch, for 1.178 + * \ 1.179 + * StaticBlockObject See NB 1.180 + * 1.181 + * This hierarchy represents more than just the interface hierarchy: reserved 1.182 + * slots in base classes are fixed for all derived classes. Thus, for example, 1.183 + * ScopeObject::enclosingScope() can simply access a fixed slot without further 1.184 + * dynamic type information. 1.185 + * 1.186 + * NB: Static block objects are a special case: these objects are created at 1.187 + * compile time to hold the shape/binding information from which block objects 1.188 + * are cloned at runtime. These objects should never escape into the wild and 1.189 + * support a restricted set of ScopeObject operations. 1.190 + * 1.191 + * See also "Debug scope objects" below. 1.192 + */ 1.193 + 1.194 +class ScopeObject : public JSObject 1.195 +{ 1.196 + protected: 1.197 + static const uint32_t SCOPE_CHAIN_SLOT = 0; 1.198 + 1.199 + public: 1.200 + /* 1.201 + * Since every scope chain terminates with a global object and GlobalObject 1.202 + * does not derive ScopeObject (it has a completely different layout), the 1.203 + * enclosing scope of a ScopeObject is necessarily non-null. 1.204 + */ 1.205 + inline JSObject &enclosingScope() const { 1.206 + return getFixedSlot(SCOPE_CHAIN_SLOT).toObject(); 1.207 + } 1.208 + 1.209 + void setEnclosingScope(HandleObject obj); 1.210 + 1.211 + /* 1.212 + * Get or set an aliased variable contained in this scope. Unaliased 1.213 + * variables should instead access the stack frame. Aliased variable access 1.214 + * is primarily made through JOF_SCOPECOORD ops which is why these members 1.215 + * take a ScopeCoordinate instead of just the slot index. 1.216 + */ 1.217 + inline const Value &aliasedVar(ScopeCoordinate sc); 1.218 + 1.219 + inline void setAliasedVar(JSContext *cx, ScopeCoordinate sc, PropertyName *name, const Value &v); 1.220 + 1.221 + /* For jit access. */ 1.222 + static size_t offsetOfEnclosingScope() { 1.223 + return getFixedSlotOffset(SCOPE_CHAIN_SLOT); 1.224 + } 1.225 + 1.226 + static size_t enclosingScopeSlot() { 1.227 + return SCOPE_CHAIN_SLOT; 1.228 + } 1.229 +}; 1.230 + 1.231 +class CallObject : public ScopeObject 1.232 +{ 1.233 + static const uint32_t CALLEE_SLOT = 1; 1.234 + 1.235 + static CallObject * 1.236 + create(JSContext *cx, HandleScript script, HandleObject enclosing, HandleFunction callee); 1.237 + 1.238 + public: 1.239 + static const Class class_; 1.240 + 1.241 + /* These functions are internal and are exposed only for JITs. */ 1.242 + 1.243 + /* 1.244 + * Construct a bare-bones call object given a shape, a non-singleton type, 1.245 + * and slots pointer. The call object must be further initialized to be 1.246 + * usable. 1.247 + */ 1.248 + static CallObject * 1.249 + create(JSContext *cx, HandleShape shape, HandleTypeObject type, HeapSlot *slots); 1.250 + 1.251 + /* 1.252 + * Construct a bare-bones call object given a shape and slots pointer, and 1.253 + * make it have singleton type. The call object must be initialized to be 1.254 + * usable. 1.255 + */ 1.256 + static CallObject * 1.257 + createSingleton(JSContext *cx, HandleShape shape, HeapSlot *slots); 1.258 + 1.259 + static CallObject * 1.260 + createTemplateObject(JSContext *cx, HandleScript script, gc::InitialHeap heap); 1.261 + 1.262 + static const uint32_t RESERVED_SLOTS = 2; 1.263 + 1.264 + static CallObject *createForFunction(JSContext *cx, HandleObject enclosing, HandleFunction callee); 1.265 + 1.266 + static CallObject *createForFunction(JSContext *cx, AbstractFramePtr frame); 1.267 + static CallObject *createForStrictEval(JSContext *cx, AbstractFramePtr frame); 1.268 + 1.269 + /* True if this is for a strict mode eval frame. */ 1.270 + bool isForEval() const { 1.271 + JS_ASSERT(getFixedSlot(CALLEE_SLOT).isObjectOrNull()); 1.272 + JS_ASSERT_IF(getFixedSlot(CALLEE_SLOT).isObject(), 1.273 + getFixedSlot(CALLEE_SLOT).toObject().is<JSFunction>()); 1.274 + return getFixedSlot(CALLEE_SLOT).isNull(); 1.275 + } 1.276 + 1.277 + /* 1.278 + * Returns the function for which this CallObject was created. (This may 1.279 + * only be called if !isForEval.) 1.280 + */ 1.281 + JSFunction &callee() const { 1.282 + return getFixedSlot(CALLEE_SLOT).toObject().as<JSFunction>(); 1.283 + } 1.284 + 1.285 + /* Get/set the aliased variable referred to by 'bi'. */ 1.286 + const Value &aliasedVar(AliasedFormalIter fi) { 1.287 + return getSlot(fi.scopeSlot()); 1.288 + } 1.289 + inline void setAliasedVar(JSContext *cx, AliasedFormalIter fi, PropertyName *name, 1.290 + const Value &v); 1.291 + 1.292 + /* 1.293 + * When an aliased var (var accessed by nested closures) is also aliased by 1.294 + * the arguments object, it must of course exist in one canonical location 1.295 + * and that location is always the CallObject. For this to work, the 1.296 + * ArgumentsObject stores special MagicValue in its array for forwarded-to- 1.297 + * CallObject variables. This MagicValue's payload is the slot of the 1.298 + * CallObject to access. 1.299 + */ 1.300 + const Value &aliasedVarFromArguments(const Value &argsValue) { 1.301 + return getSlot(argsValue.magicUint32()); 1.302 + } 1.303 + inline void setAliasedVarFromArguments(JSContext *cx, const Value &argsValue, jsid id, 1.304 + const Value &v); 1.305 + 1.306 + /* For jit access. */ 1.307 + static size_t offsetOfCallee() { 1.308 + return getFixedSlotOffset(CALLEE_SLOT); 1.309 + } 1.310 + 1.311 + static size_t calleeSlot() { 1.312 + return CALLEE_SLOT; 1.313 + } 1.314 +}; 1.315 + 1.316 +class DeclEnvObject : public ScopeObject 1.317 +{ 1.318 + // Pre-allocated slot for the named lambda. 1.319 + static const uint32_t LAMBDA_SLOT = 1; 1.320 + 1.321 + public: 1.322 + static const uint32_t RESERVED_SLOTS = 2; 1.323 + static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT2_BACKGROUND; 1.324 + 1.325 + static const Class class_; 1.326 + 1.327 + static DeclEnvObject * 1.328 + createTemplateObject(JSContext *cx, HandleFunction fun, gc::InitialHeap heap); 1.329 + 1.330 + static DeclEnvObject *create(JSContext *cx, HandleObject enclosing, HandleFunction callee); 1.331 + 1.332 + static inline size_t lambdaSlot() { 1.333 + return LAMBDA_SLOT; 1.334 + } 1.335 +}; 1.336 + 1.337 +class NestedScopeObject : public ScopeObject 1.338 +{ 1.339 + public: 1.340 + /* 1.341 + * A refinement of enclosingScope that returns nullptr if the enclosing 1.342 + * scope is not a NestedScopeObject. 1.343 + */ 1.344 + inline NestedScopeObject *enclosingNestedScope() const; 1.345 + 1.346 + // Return true if this object is a compile-time scope template. 1.347 + inline bool isStatic() { return !getProto(); } 1.348 + 1.349 + // Return the static scope corresponding to this scope chain object. 1.350 + inline NestedScopeObject* staticScope() { 1.351 + JS_ASSERT(!isStatic()); 1.352 + return &getProto()->as<NestedScopeObject>(); 1.353 + } 1.354 + 1.355 + // At compile-time it's possible for the scope chain to be null. 1.356 + JSObject *enclosingScopeForStaticScopeIter() { 1.357 + return getReservedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull(); 1.358 + } 1.359 + 1.360 + void initEnclosingNestedScope(JSObject *obj) { 1.361 + JS_ASSERT(getReservedSlot(SCOPE_CHAIN_SLOT).isUndefined()); 1.362 + setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(obj)); 1.363 + } 1.364 + 1.365 + /* 1.366 + * The parser uses 'enclosingNestedScope' as the prev-link in the 1.367 + * pc->staticScope stack. Note: in the case of hoisting, this prev-link will 1.368 + * not ultimately be the same as enclosingNestedScope; 1.369 + * initEnclosingNestedScope must be called separately in the 1.370 + * emitter. 'reset' is just for asserting stackiness. 1.371 + */ 1.372 + void initEnclosingNestedScopeFromParser(NestedScopeObject *prev) { 1.373 + setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(prev)); 1.374 + } 1.375 + 1.376 + void resetEnclosingNestedScopeFromParser() { 1.377 + setReservedSlot(SCOPE_CHAIN_SLOT, UndefinedValue()); 1.378 + } 1.379 +}; 1.380 + 1.381 +// With scope template objects on the static scope chain. 1.382 +class StaticWithObject : public NestedScopeObject 1.383 +{ 1.384 + public: 1.385 + static const unsigned RESERVED_SLOTS = 1; 1.386 + static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT2_BACKGROUND; 1.387 + 1.388 + static const Class class_; 1.389 + 1.390 + static StaticWithObject *create(ExclusiveContext *cx); 1.391 +}; 1.392 + 1.393 +// With scope objects on the run-time scope chain. 1.394 +class DynamicWithObject : public NestedScopeObject 1.395 +{ 1.396 + static const unsigned OBJECT_SLOT = 1; 1.397 + static const unsigned THIS_SLOT = 2; 1.398 + 1.399 + public: 1.400 + static const unsigned RESERVED_SLOTS = 3; 1.401 + static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT4_BACKGROUND; 1.402 + 1.403 + static const Class class_; 1.404 + 1.405 + static DynamicWithObject * 1.406 + create(JSContext *cx, HandleObject object, HandleObject enclosing, HandleObject staticWith); 1.407 + 1.408 + StaticWithObject& staticWith() const { 1.409 + return getProto()->as<StaticWithObject>(); 1.410 + } 1.411 + 1.412 + /* Return the 'o' in 'with (o)'. */ 1.413 + JSObject &object() const { 1.414 + return getReservedSlot(OBJECT_SLOT).toObject(); 1.415 + } 1.416 + 1.417 + /* Return object for the 'this' class hook. */ 1.418 + JSObject &withThis() const { 1.419 + return getReservedSlot(THIS_SLOT).toObject(); 1.420 + } 1.421 +}; 1.422 + 1.423 +class BlockObject : public NestedScopeObject 1.424 +{ 1.425 + protected: 1.426 + static const unsigned DEPTH_SLOT = 1; 1.427 + 1.428 + public: 1.429 + static const unsigned RESERVED_SLOTS = 2; 1.430 + static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT4_BACKGROUND; 1.431 + 1.432 + static const Class class_; 1.433 + 1.434 + /* Return the abstract stack depth right before entering this nested scope. */ 1.435 + uint32_t stackDepth() const { 1.436 + return getReservedSlot(DEPTH_SLOT).toPrivateUint32(); 1.437 + } 1.438 + 1.439 + /* Return the number of variables associated with this block. */ 1.440 + uint32_t numVariables() const { 1.441 + // TODO: propertyCount() is O(n), use O(1) lastProperty()->slot() instead 1.442 + return propertyCount(); 1.443 + } 1.444 + 1.445 + protected: 1.446 + /* Blocks contain an object slot for each slot i: 0 <= i < slotCount. */ 1.447 + const Value &slotValue(unsigned i) { 1.448 + return getSlotRef(RESERVED_SLOTS + i); 1.449 + } 1.450 + 1.451 + void setSlotValue(unsigned i, const Value &v) { 1.452 + setSlot(RESERVED_SLOTS + i, v); 1.453 + } 1.454 +}; 1.455 + 1.456 +class StaticBlockObject : public BlockObject 1.457 +{ 1.458 + static const unsigned LOCAL_OFFSET_SLOT = 1; 1.459 + 1.460 + public: 1.461 + static StaticBlockObject *create(ExclusiveContext *cx); 1.462 + 1.463 + /* See StaticScopeIter comment. */ 1.464 + JSObject *enclosingStaticScope() const { 1.465 + return getFixedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull(); 1.466 + } 1.467 + 1.468 + /* 1.469 + * Return the index (in the range [0, numVariables()) corresponding to the 1.470 + * given shape of a block object. 1.471 + */ 1.472 + uint32_t shapeToIndex(const Shape &shape) { 1.473 + uint32_t slot = shape.slot(); 1.474 + JS_ASSERT(slot - RESERVED_SLOTS < numVariables()); 1.475 + return slot - RESERVED_SLOTS; 1.476 + } 1.477 + 1.478 + /* 1.479 + * A refinement of enclosingStaticScope that returns nullptr if the enclosing 1.480 + * static scope is a JSFunction. 1.481 + */ 1.482 + inline StaticBlockObject *enclosingBlock() const; 1.483 + 1.484 + uint32_t localOffset() { 1.485 + return getReservedSlot(LOCAL_OFFSET_SLOT).toPrivateUint32(); 1.486 + } 1.487 + 1.488 + // Return the local corresponding to the 'var'th binding where 'var' is in the 1.489 + // range [0, numVariables()). 1.490 + uint32_t blockIndexToLocalIndex(uint32_t index) { 1.491 + JS_ASSERT(index < numVariables()); 1.492 + return getReservedSlot(LOCAL_OFFSET_SLOT).toPrivateUint32() + index; 1.493 + } 1.494 + 1.495 + // Return the slot corresponding to local variable 'local', where 'local' is 1.496 + // in the range [localOffset(), localOffset() + numVariables()). The result is 1.497 + // in the range [RESERVED_SLOTS, RESERVED_SLOTS + numVariables()). 1.498 + uint32_t localIndexToSlot(uint32_t local) { 1.499 + JS_ASSERT(local >= localOffset()); 1.500 + local -= localOffset(); 1.501 + JS_ASSERT(local < numVariables()); 1.502 + return RESERVED_SLOTS + local; 1.503 + } 1.504 + 1.505 + /* 1.506 + * A let binding is aliased if accessed lexically by nested functions or 1.507 + * dynamically through dynamic name lookup (eval, with, function::, etc). 1.508 + */ 1.509 + bool isAliased(unsigned i) { 1.510 + return slotValue(i).isTrue(); 1.511 + } 1.512 + 1.513 + /* 1.514 + * A static block object is cloned (when entering the block) iff some 1.515 + * variable of the block isAliased. 1.516 + */ 1.517 + bool needsClone() { 1.518 + return !getFixedSlot(RESERVED_SLOTS).isFalse(); 1.519 + } 1.520 + 1.521 + /* Frontend-only functions ***********************************************/ 1.522 + 1.523 + /* Initialization functions for above fields. */ 1.524 + void setAliased(unsigned i, bool aliased) { 1.525 + JS_ASSERT_IF(i > 0, slotValue(i-1).isBoolean()); 1.526 + setSlotValue(i, BooleanValue(aliased)); 1.527 + if (aliased && !needsClone()) { 1.528 + setSlotValue(0, MagicValue(JS_BLOCK_NEEDS_CLONE)); 1.529 + JS_ASSERT(needsClone()); 1.530 + } 1.531 + } 1.532 + 1.533 + void setLocalOffset(uint32_t offset) { 1.534 + JS_ASSERT(getReservedSlot(LOCAL_OFFSET_SLOT).isUndefined()); 1.535 + initReservedSlot(LOCAL_OFFSET_SLOT, PrivateUint32Value(offset)); 1.536 + } 1.537 + 1.538 + /* 1.539 + * Frontend compilation temporarily uses the object's slots to link 1.540 + * a let var to its associated Definition parse node. 1.541 + */ 1.542 + void setDefinitionParseNode(unsigned i, frontend::Definition *def) { 1.543 + JS_ASSERT(slotValue(i).isUndefined()); 1.544 + setSlotValue(i, PrivateValue(def)); 1.545 + } 1.546 + 1.547 + frontend::Definition *definitionParseNode(unsigned i) { 1.548 + Value v = slotValue(i); 1.549 + return reinterpret_cast<frontend::Definition *>(v.toPrivate()); 1.550 + } 1.551 + 1.552 + /* 1.553 + * While ScopeCoordinate can generally reference up to 2^24 slots, block objects have an 1.554 + * additional limitation that all slot indices must be storable as uint16_t short-ids in the 1.555 + * associated Shape. If we could remove the block dependencies on shape->shortid, we could 1.556 + * remove INDEX_LIMIT. 1.557 + */ 1.558 + static const unsigned LOCAL_INDEX_LIMIT = JS_BIT(16); 1.559 + 1.560 + static Shape *addVar(ExclusiveContext *cx, Handle<StaticBlockObject*> block, HandleId id, 1.561 + unsigned index, bool *redeclared); 1.562 +}; 1.563 + 1.564 +class ClonedBlockObject : public BlockObject 1.565 +{ 1.566 + public: 1.567 + static ClonedBlockObject *create(JSContext *cx, Handle<StaticBlockObject *> block, 1.568 + AbstractFramePtr frame); 1.569 + 1.570 + /* The static block from which this block was cloned. */ 1.571 + StaticBlockObject &staticBlock() const { 1.572 + return getProto()->as<StaticBlockObject>(); 1.573 + } 1.574 + 1.575 + /* Assuming 'put' has been called, return the value of the ith let var. */ 1.576 + const Value &var(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) { 1.577 + JS_ASSERT_IF(checkAliasing, staticBlock().isAliased(i)); 1.578 + return slotValue(i); 1.579 + } 1.580 + 1.581 + void setVar(unsigned i, const Value &v, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) { 1.582 + JS_ASSERT_IF(checkAliasing, staticBlock().isAliased(i)); 1.583 + setSlotValue(i, v); 1.584 + } 1.585 + 1.586 + /* Copy in all the unaliased formals and locals. */ 1.587 + void copyUnaliasedValues(AbstractFramePtr frame); 1.588 +}; 1.589 + 1.590 +template<XDRMode mode> 1.591 +bool 1.592 +XDRStaticBlockObject(XDRState<mode> *xdr, HandleObject enclosingScope, 1.593 + StaticBlockObject **objp); 1.594 + 1.595 +template<XDRMode mode> 1.596 +bool 1.597 +XDRStaticWithObject(XDRState<mode> *xdr, HandleObject enclosingScope, 1.598 + StaticWithObject **objp); 1.599 + 1.600 +extern JSObject * 1.601 +CloneNestedScopeObject(JSContext *cx, HandleObject enclosingScope, Handle<NestedScopeObject*> src); 1.602 + 1.603 +/*****************************************************************************/ 1.604 + 1.605 +class ScopeIterKey; 1.606 +class ScopeIterVal; 1.607 + 1.608 +/* 1.609 + * A scope iterator describes the active scopes enclosing the current point of 1.610 + * execution for a single frame, proceeding from inner to outer. Here, "frame" 1.611 + * means a single activation of: a function, eval, or global code. By design, 1.612 + * ScopeIter exposes *all* scopes, even those that have been optimized away 1.613 + * (i.e., no ScopeObject was created when entering the scope and thus there is 1.614 + * no ScopeObject on fp->scopeChain representing the scope). 1.615 + * 1.616 + * Note: ScopeIter iterates over all scopes *within* a frame which means that 1.617 + * all scopes are ScopeObjects. In particular, the GlobalObject enclosing 1.618 + * global code (and any random objects passed as scopes to Execute) will not 1.619 + * be included. 1.620 + */ 1.621 +class ScopeIter 1.622 +{ 1.623 + friend class ScopeIterKey; 1.624 + friend class ScopeIterVal; 1.625 + 1.626 + public: 1.627 + enum Type { Call, Block, With, StrictEvalScope }; 1.628 + 1.629 + private: 1.630 + JSContext *cx; 1.631 + AbstractFramePtr frame_; 1.632 + RootedObject cur_; 1.633 + Rooted<NestedScopeObject *> staticScope_; 1.634 + Type type_; 1.635 + bool hasScopeObject_; 1.636 + 1.637 + void settle(); 1.638 + 1.639 + /* ScopeIter does not have value semantics. */ 1.640 + ScopeIter(const ScopeIter &si) MOZ_DELETE; 1.641 + 1.642 + ScopeIter(JSContext *cx) MOZ_DELETE; 1.643 + 1.644 + public: 1.645 + 1.646 + /* Constructing from a copy of an existing ScopeIter. */ 1.647 + ScopeIter(const ScopeIter &si, JSContext *cx 1.648 + MOZ_GUARD_OBJECT_NOTIFIER_PARAM); 1.649 + 1.650 + /* Constructing from AbstractFramePtr places ScopeIter on the innermost scope. */ 1.651 + ScopeIter(AbstractFramePtr frame, jsbytecode *pc, JSContext *cx 1.652 + MOZ_GUARD_OBJECT_NOTIFIER_PARAM); 1.653 + 1.654 + /* 1.655 + * Without a stack frame, the resulting ScopeIter is done() with 1.656 + * enclosingScope() as given. 1.657 + */ 1.658 + ScopeIter(JSObject &enclosingScope, JSContext *cx 1.659 + MOZ_GUARD_OBJECT_NOTIFIER_PARAM); 1.660 + 1.661 + ScopeIter(const ScopeIterVal &hashVal, JSContext *cx 1.662 + MOZ_GUARD_OBJECT_NOTIFIER_PARAM); 1.663 + 1.664 + bool done() const { return !frame_; } 1.665 + 1.666 + /* If done(): */ 1.667 + 1.668 + JSObject &enclosingScope() const { JS_ASSERT(done()); return *cur_; } 1.669 + 1.670 + /* If !done(): */ 1.671 + 1.672 + ScopeIter &operator++(); 1.673 + 1.674 + AbstractFramePtr frame() const { JS_ASSERT(!done()); return frame_; } 1.675 + Type type() const { JS_ASSERT(!done()); return type_; } 1.676 + bool hasScopeObject() const { JS_ASSERT(!done()); return hasScopeObject_; } 1.677 + ScopeObject &scope() const; 1.678 + NestedScopeObject* staticScope() const { return staticScope_; } 1.679 + 1.680 + StaticBlockObject &staticBlock() const { 1.681 + JS_ASSERT(type() == Block); 1.682 + return staticScope_->as<StaticBlockObject>(); 1.683 + } 1.684 + 1.685 + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER 1.686 +}; 1.687 + 1.688 +class ScopeIterKey 1.689 +{ 1.690 + friend class ScopeIterVal; 1.691 + 1.692 + AbstractFramePtr frame_; 1.693 + JSObject *cur_; 1.694 + NestedScopeObject *staticScope_; 1.695 + ScopeIter::Type type_; 1.696 + bool hasScopeObject_; 1.697 + 1.698 + public: 1.699 + ScopeIterKey(const ScopeIter &si) 1.700 + : frame_(si.frame()), cur_(si.cur_), staticScope_(si.staticScope_), type_(si.type_), 1.701 + hasScopeObject_(si.hasScopeObject_) {} 1.702 + 1.703 + AbstractFramePtr frame() const { return frame_; } 1.704 + JSObject *cur() const { return cur_; } 1.705 + NestedScopeObject *staticScope() const { return staticScope_; } 1.706 + ScopeIter::Type type() const { return type_; } 1.707 + bool hasScopeObject() const { return hasScopeObject_; } 1.708 + JSObject *enclosingScope() const { return cur_; } 1.709 + JSObject *&enclosingScope() { return cur_; } 1.710 + 1.711 + /* For use as hash policy */ 1.712 + typedef ScopeIterKey Lookup; 1.713 + static HashNumber hash(ScopeIterKey si); 1.714 + static bool match(ScopeIterKey si1, ScopeIterKey si2); 1.715 + bool operator!=(const ScopeIterKey &other) const { 1.716 + return frame_ != other.frame_ || 1.717 + cur_ != other.cur_ || 1.718 + staticScope_ != other.staticScope_ || 1.719 + type_ != other.type_; 1.720 + } 1.721 + static void rekey(ScopeIterKey &k, const ScopeIterKey& newKey) { 1.722 + k = newKey; 1.723 + } 1.724 +}; 1.725 + 1.726 +class ScopeIterVal 1.727 +{ 1.728 + friend class ScopeIter; 1.729 + friend class DebugScopes; 1.730 + 1.731 + AbstractFramePtr frame_; 1.732 + RelocatablePtr<JSObject> cur_; 1.733 + RelocatablePtr<NestedScopeObject> staticScope_; 1.734 + ScopeIter::Type type_; 1.735 + bool hasScopeObject_; 1.736 + 1.737 + static void staticAsserts(); 1.738 + 1.739 + public: 1.740 + ScopeIterVal(const ScopeIter &si) 1.741 + : frame_(si.frame()), cur_(si.cur_), staticScope_(si.staticScope_), type_(si.type_), 1.742 + hasScopeObject_(si.hasScopeObject_) {} 1.743 + 1.744 + AbstractFramePtr frame() const { return frame_; } 1.745 +}; 1.746 + 1.747 +/*****************************************************************************/ 1.748 + 1.749 +/* 1.750 + * Debug scope objects 1.751 + * 1.752 + * The debugger effectively turns every opcode into a potential direct eval. 1.753 + * Naively, this would require creating a ScopeObject for every call/block 1.754 + * scope and using JSOP_GETALIASEDVAR for every access. To optimize this, the 1.755 + * engine assumes there is no debugger and optimizes scope access and creation 1.756 + * accordingly. When the debugger wants to perform an unexpected eval-in-frame 1.757 + * (or other, similar dynamic-scope-requiring operations), fp->scopeChain is 1.758 + * now incomplete: it may not contain all, or any, of the ScopeObjects to 1.759 + * represent the current scope. 1.760 + * 1.761 + * To resolve this, the debugger first calls GetDebugScopeFor(Function|Frame) 1.762 + * to synthesize a "debug scope chain". A debug scope chain is just a chain of 1.763 + * objects that fill in missing scopes and protect the engine from unexpected 1.764 + * access. (The latter means that some debugger operations, like redefining a 1.765 + * lexical binding, can fail when a true eval would succeed.) To do both of 1.766 + * these things, GetDebugScopeFor* creates a new proxy DebugScopeObject to sit 1.767 + * in front of every existing ScopeObject. 1.768 + * 1.769 + * GetDebugScopeFor* ensures the invariant that the same DebugScopeObject is 1.770 + * always produced for the same underlying scope (optimized or not!). This is 1.771 + * maintained by some bookkeeping information stored in DebugScopes. 1.772 + */ 1.773 + 1.774 +extern JSObject * 1.775 +GetDebugScopeForFunction(JSContext *cx, HandleFunction fun); 1.776 + 1.777 +extern JSObject * 1.778 +GetDebugScopeForFrame(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc); 1.779 + 1.780 +/* Provides debugger access to a scope. */ 1.781 +class DebugScopeObject : public ProxyObject 1.782 +{ 1.783 + /* 1.784 + * The enclosing scope on the dynamic scope chain. This slot is analogous 1.785 + * to the SCOPE_CHAIN_SLOT of a ScopeObject. 1.786 + */ 1.787 + static const unsigned ENCLOSING_EXTRA = 0; 1.788 + 1.789 + /* 1.790 + * NullValue or a dense array holding the unaliased variables of a function 1.791 + * frame that has been popped. 1.792 + */ 1.793 + static const unsigned SNAPSHOT_EXTRA = 1; 1.794 + 1.795 + public: 1.796 + static DebugScopeObject *create(JSContext *cx, ScopeObject &scope, HandleObject enclosing); 1.797 + 1.798 + ScopeObject &scope() const; 1.799 + JSObject &enclosingScope() const; 1.800 + 1.801 + /* May only be called for proxies to function call objects. */ 1.802 + JSObject *maybeSnapshot() const; 1.803 + void initSnapshot(JSObject &snapshot); 1.804 + 1.805 + /* Currently, the 'declarative' scopes are Call and Block. */ 1.806 + bool isForDeclarative() const; 1.807 + 1.808 + // Get a property by 'id', but returns sentinel values instead of throwing 1.809 + // on exceptional cases. 1.810 + bool getMaybeSentinelValue(JSContext *cx, HandleId id, MutableHandleValue vp); 1.811 +}; 1.812 + 1.813 +/* Maintains per-compartment debug scope bookkeeping information. */ 1.814 +class DebugScopes 1.815 +{ 1.816 + /* The map from (non-debug) scopes to debug scopes. */ 1.817 + typedef WeakMap<EncapsulatedPtrObject, RelocatablePtrObject> ObjectWeakMap; 1.818 + ObjectWeakMap proxiedScopes; 1.819 + static MOZ_ALWAYS_INLINE void proxiedScopesPostWriteBarrier(JSRuntime *rt, ObjectWeakMap *map, 1.820 + const EncapsulatedPtrObject &key); 1.821 + 1.822 + /* 1.823 + * The map from live frames which have optimized-away scopes to the 1.824 + * corresponding debug scopes. 1.825 + */ 1.826 + typedef HashMap<ScopeIterKey, 1.827 + ReadBarriered<DebugScopeObject>, 1.828 + ScopeIterKey, 1.829 + RuntimeAllocPolicy> MissingScopeMap; 1.830 + MissingScopeMap missingScopes; 1.831 + class MissingScopesRef; 1.832 + static MOZ_ALWAYS_INLINE void missingScopesPostWriteBarrier(JSRuntime *rt, MissingScopeMap *map, 1.833 + const ScopeIterKey &key); 1.834 + 1.835 + /* 1.836 + * The map from scope objects of live frames to the live frame. This map 1.837 + * updated lazily whenever the debugger needs the information. In between 1.838 + * two lazy updates, liveScopes becomes incomplete (but not invalid, onPop* 1.839 + * removes scopes as they are popped). Thus, two consecutive debugger lazy 1.840 + * updates of liveScopes need only fill in the new scopes. 1.841 + */ 1.842 + typedef HashMap<ScopeObject *, 1.843 + ScopeIterVal, 1.844 + DefaultHasher<ScopeObject *>, 1.845 + RuntimeAllocPolicy> LiveScopeMap; 1.846 + LiveScopeMap liveScopes; 1.847 + static MOZ_ALWAYS_INLINE void liveScopesPostWriteBarrier(JSRuntime *rt, LiveScopeMap *map, 1.848 + ScopeObject *key); 1.849 + 1.850 + public: 1.851 + DebugScopes(JSContext *c); 1.852 + ~DebugScopes(); 1.853 + 1.854 + private: 1.855 + bool init(); 1.856 + 1.857 + static DebugScopes *ensureCompartmentData(JSContext *cx); 1.858 + 1.859 + public: 1.860 + void mark(JSTracer *trc); 1.861 + void sweep(JSRuntime *rt); 1.862 +#if defined(JSGC_GENERATIONAL) && defined(JS_GC_ZEAL) 1.863 + void checkHashTablesAfterMovingGC(JSRuntime *rt); 1.864 +#endif 1.865 + 1.866 + static DebugScopeObject *hasDebugScope(JSContext *cx, ScopeObject &scope); 1.867 + static bool addDebugScope(JSContext *cx, ScopeObject &scope, DebugScopeObject &debugScope); 1.868 + 1.869 + static DebugScopeObject *hasDebugScope(JSContext *cx, const ScopeIter &si); 1.870 + static bool addDebugScope(JSContext *cx, const ScopeIter &si, DebugScopeObject &debugScope); 1.871 + 1.872 + static bool updateLiveScopes(JSContext *cx); 1.873 + static ScopeIterVal *hasLiveScope(ScopeObject &scope); 1.874 + 1.875 + // In debug-mode, these must be called whenever exiting a scope that might 1.876 + // have stack-allocated locals. 1.877 + static void onPopCall(AbstractFramePtr frame, JSContext *cx); 1.878 + static void onPopBlock(JSContext *cx, const ScopeIter &si); 1.879 + static void onPopBlock(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc); 1.880 + static void onPopWith(AbstractFramePtr frame); 1.881 + static void onPopStrictEvalScope(AbstractFramePtr frame); 1.882 + static void onCompartmentLeaveDebugMode(JSCompartment *c); 1.883 +}; 1.884 + 1.885 +} /* namespace js */ 1.886 + 1.887 +template<> 1.888 +inline bool 1.889 +JSObject::is<js::NestedScopeObject>() const 1.890 +{ 1.891 + return is<js::BlockObject>() || is<js::StaticWithObject>() || is<js::DynamicWithObject>(); 1.892 +} 1.893 + 1.894 +template<> 1.895 +inline bool 1.896 +JSObject::is<js::ScopeObject>() const 1.897 +{ 1.898 + return is<js::CallObject>() || is<js::DeclEnvObject>() || is<js::NestedScopeObject>(); 1.899 +} 1.900 + 1.901 +template<> 1.902 +inline bool 1.903 +JSObject::is<js::DebugScopeObject>() const 1.904 +{ 1.905 + extern bool js_IsDebugScopeSlow(js::ProxyObject *proxy); 1.906 + 1.907 + // Note: don't use is<ProxyObject>() here -- it also matches subclasses! 1.908 + return hasClass(&js::ProxyObject::uncallableClass_) && 1.909 + js_IsDebugScopeSlow(&const_cast<JSObject*>(this)->as<js::ProxyObject>()); 1.910 +} 1.911 + 1.912 +template<> 1.913 +inline bool 1.914 +JSObject::is<js::ClonedBlockObject>() const 1.915 +{ 1.916 + return is<js::BlockObject>() && !!getProto(); 1.917 +} 1.918 + 1.919 +template<> 1.920 +inline bool 1.921 +JSObject::is<js::StaticBlockObject>() const 1.922 +{ 1.923 + return is<js::BlockObject>() && !getProto(); 1.924 +} 1.925 + 1.926 +inline JSObject * 1.927 +JSObject::enclosingScope() 1.928 +{ 1.929 + return is<js::ScopeObject>() 1.930 + ? &as<js::ScopeObject>().enclosingScope() 1.931 + : is<js::DebugScopeObject>() 1.932 + ? &as<js::DebugScopeObject>().enclosingScope() 1.933 + : getParent(); 1.934 +} 1.935 + 1.936 +namespace js { 1.937 + 1.938 +inline const Value & 1.939 +ScopeObject::aliasedVar(ScopeCoordinate sc) 1.940 +{ 1.941 + JS_ASSERT(is<CallObject>() || is<ClonedBlockObject>()); 1.942 + return getSlot(sc.slot()); 1.943 +} 1.944 + 1.945 +inline NestedScopeObject * 1.946 +NestedScopeObject::enclosingNestedScope() const 1.947 +{ 1.948 + JSObject *obj = getReservedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull(); 1.949 + return obj && obj->is<NestedScopeObject>() ? &obj->as<NestedScopeObject>() : nullptr; 1.950 +} 1.951 + 1.952 +#ifdef DEBUG 1.953 +bool 1.954 +AnalyzeEntrainedVariables(JSContext *cx, HandleScript script); 1.955 +#endif 1.956 + 1.957 +} // namespace js 1.958 + 1.959 +#endif /* vm_ScopeObject_h */