js/src/vm/ScopeObject.h

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #ifndef vm_ScopeObject_h
michael@0 8 #define vm_ScopeObject_h
michael@0 9
michael@0 10 #include "jscntxt.h"
michael@0 11 #include "jsobj.h"
michael@0 12 #include "jsweakmap.h"
michael@0 13
michael@0 14 #include "gc/Barrier.h"
michael@0 15 #include "vm/ProxyObject.h"
michael@0 16
michael@0 17 namespace js {
michael@0 18
michael@0 19 namespace frontend { struct Definition; }
michael@0 20
michael@0 21 class StaticWithObject;
michael@0 22
michael@0 23 /*****************************************************************************/
michael@0 24
michael@0 25 /*
michael@0 26 * All function scripts have an "enclosing static scope" that refers to the
michael@0 27 * innermost enclosing let or function in the program text. This allows full
michael@0 28 * reconstruction of the lexical scope for debugging or compiling efficient
michael@0 29 * access to variables in enclosing scopes. The static scope is represented at
michael@0 30 * runtime by a tree of compiler-created objects representing each scope:
michael@0 31 * - a StaticBlockObject is created for 'let' and 'catch' scopes
michael@0 32 * - a JSFunction+JSScript+Bindings trio is created for function scopes
michael@0 33 * (These objects are primarily used to clone objects scopes for the
michael@0 34 * dynamic scope chain.)
michael@0 35 *
michael@0 36 * There is an additional scope for named lambdas. E.g., in:
michael@0 37 *
michael@0 38 * (function f() { var x; function g() { } })
michael@0 39 *
michael@0 40 * g's innermost enclosing scope will first be the function scope containing
michael@0 41 * 'x', enclosed by a scope containing only the name 'f'. (This separate scope
michael@0 42 * is necessary due to the fact that declarations in the function scope shadow
michael@0 43 * (dynamically, in the case of 'eval') the lambda name.)
michael@0 44 *
michael@0 45 * There are two limitations to the current lexical nesting information:
michael@0 46 *
michael@0 47 * - 'with' is completely absent; this isn't a problem for the current use
michael@0 48 * cases since 'with' causes every static scope to be on the dynamic scope
michael@0 49 * chain (so the debugger can find everything) and inhibits all upvar
michael@0 50 * optimization.
michael@0 51 *
michael@0 52 * - The "enclosing static scope" chain stops at 'eval'. For example in:
michael@0 53 * let (x) { eval("function f() {}") }
michael@0 54 * f does not have an enclosing static scope. This is fine for current uses
michael@0 55 * for the same reason as 'with'.
michael@0 56 *
michael@0 57 * (See also AssertDynamicScopeMatchesStaticScope.)
michael@0 58 */
michael@0 59 template <AllowGC allowGC>
michael@0 60 class StaticScopeIter
michael@0 61 {
michael@0 62 typename MaybeRooted<JSObject*, allowGC>::RootType obj;
michael@0 63 bool onNamedLambda;
michael@0 64
michael@0 65 public:
michael@0 66 StaticScopeIter(ExclusiveContext *cx, JSObject *obj)
michael@0 67 : obj(cx, obj), onNamedLambda(false)
michael@0 68 {
michael@0 69 JS_STATIC_ASSERT(allowGC == CanGC);
michael@0 70 JS_ASSERT_IF(obj, obj->is<StaticBlockObject>() || obj->is<StaticWithObject>() ||
michael@0 71 obj->is<JSFunction>());
michael@0 72 }
michael@0 73
michael@0 74 StaticScopeIter(JSObject *obj)
michael@0 75 : obj((ExclusiveContext *) nullptr, obj), onNamedLambda(false)
michael@0 76 {
michael@0 77 JS_STATIC_ASSERT(allowGC == NoGC);
michael@0 78 JS_ASSERT_IF(obj, obj->is<StaticBlockObject>() || obj->is<StaticWithObject>() ||
michael@0 79 obj->is<JSFunction>());
michael@0 80 }
michael@0 81
michael@0 82 bool done() const;
michael@0 83 void operator++(int);
michael@0 84
michael@0 85 /* Return whether this static scope will be on the dynamic scope chain. */
michael@0 86 bool hasDynamicScopeObject() const;
michael@0 87 Shape *scopeShape() const;
michael@0 88
michael@0 89 enum Type { WITH, BLOCK, FUNCTION, NAMED_LAMBDA };
michael@0 90 Type type() const;
michael@0 91
michael@0 92 StaticBlockObject &block() const;
michael@0 93 StaticWithObject &staticWith() const;
michael@0 94 JSScript *funScript() const;
michael@0 95 };
michael@0 96
michael@0 97 /*****************************************************************************/
michael@0 98
michael@0 99 /*
michael@0 100 * A "scope coordinate" describes how to get from head of the scope chain to a
michael@0 101 * given lexically-enclosing variable. A scope coordinate has two dimensions:
michael@0 102 * - hops: the number of scope objects on the scope chain to skip
michael@0 103 * - slot: the slot on the scope object holding the variable's value
michael@0 104 */
michael@0 105 class ScopeCoordinate
michael@0 106 {
michael@0 107 uint32_t hops_;
michael@0 108 uint32_t slot_;
michael@0 109
michael@0 110 /*
michael@0 111 * Technically, hops_/slot_ are SCOPECOORD_(HOPS|SLOT)_BITS wide. Since
michael@0 112 * ScopeCoordinate is a temporary value, don't bother with a bitfield as
michael@0 113 * this only adds overhead.
michael@0 114 */
michael@0 115 static_assert(SCOPECOORD_HOPS_BITS <= 32, "We have enough bits below");
michael@0 116 static_assert(SCOPECOORD_SLOT_BITS <= 32, "We have enough bits below");
michael@0 117
michael@0 118 public:
michael@0 119 inline ScopeCoordinate(jsbytecode *pc)
michael@0 120 : hops_(GET_SCOPECOORD_HOPS(pc)), slot_(GET_SCOPECOORD_SLOT(pc + SCOPECOORD_HOPS_LEN))
michael@0 121 {
michael@0 122 JS_ASSERT(JOF_OPTYPE(*pc) == JOF_SCOPECOORD);
michael@0 123 }
michael@0 124
michael@0 125 inline ScopeCoordinate() {}
michael@0 126
michael@0 127 void setHops(uint32_t hops) { JS_ASSERT(hops < SCOPECOORD_HOPS_LIMIT); hops_ = hops; }
michael@0 128 void setSlot(uint32_t slot) { JS_ASSERT(slot < SCOPECOORD_SLOT_LIMIT); slot_ = slot; }
michael@0 129
michael@0 130 uint32_t hops() const { JS_ASSERT(hops_ < SCOPECOORD_HOPS_LIMIT); return hops_; }
michael@0 131 uint32_t slot() const { JS_ASSERT(slot_ < SCOPECOORD_SLOT_LIMIT); return slot_; }
michael@0 132 };
michael@0 133
michael@0 134 /*
michael@0 135 * Return a shape representing the static scope containing the variable
michael@0 136 * accessed by the ALIASEDVAR op at 'pc'.
michael@0 137 */
michael@0 138 extern Shape *
michael@0 139 ScopeCoordinateToStaticScopeShape(JSScript *script, jsbytecode *pc);
michael@0 140
michael@0 141 /* Return the name being accessed by the given ALIASEDVAR op. */
michael@0 142 extern PropertyName *
michael@0 143 ScopeCoordinateName(ScopeCoordinateNameCache &cache, JSScript *script, jsbytecode *pc);
michael@0 144
michael@0 145 /* Return the function script accessed by the given ALIASEDVAR op, or nullptr. */
michael@0 146 extern JSScript *
michael@0 147 ScopeCoordinateFunctionScript(JSScript *script, jsbytecode *pc);
michael@0 148
michael@0 149 /*****************************************************************************/
michael@0 150
michael@0 151 /*
michael@0 152 * Scope objects
michael@0 153 *
michael@0 154 * Scope objects are technically real JSObjects but only belong on the scope
michael@0 155 * chain (that is, fp->scopeChain() or fun->environment()). The hierarchy of
michael@0 156 * scope objects is:
michael@0 157 *
michael@0 158 * JSObject Generic object
michael@0 159 * \
michael@0 160 * ScopeObject Engine-internal scope
michael@0 161 * \ \ \
michael@0 162 * \ \ DeclEnvObject Holds name of recursive/heavyweight named lambda
michael@0 163 * \ \
michael@0 164 * \ CallObject Scope of entire function or strict eval
michael@0 165 * \
michael@0 166 * NestedScopeObject Scope created for a statement
michael@0 167 * \ \ \
michael@0 168 * \ \ StaticWithObject Template for "with" object in static scope chain
michael@0 169 * \ \
michael@0 170 * \ DynamicWithObject Run-time "with" object on scope chain
michael@0 171 * \
michael@0 172 * BlockObject Shared interface of cloned/static block objects
michael@0 173 * \ \
michael@0 174 * \ ClonedBlockObject let, switch, catch, for
michael@0 175 * \
michael@0 176 * StaticBlockObject See NB
michael@0 177 *
michael@0 178 * This hierarchy represents more than just the interface hierarchy: reserved
michael@0 179 * slots in base classes are fixed for all derived classes. Thus, for example,
michael@0 180 * ScopeObject::enclosingScope() can simply access a fixed slot without further
michael@0 181 * dynamic type information.
michael@0 182 *
michael@0 183 * NB: Static block objects are a special case: these objects are created at
michael@0 184 * compile time to hold the shape/binding information from which block objects
michael@0 185 * are cloned at runtime. These objects should never escape into the wild and
michael@0 186 * support a restricted set of ScopeObject operations.
michael@0 187 *
michael@0 188 * See also "Debug scope objects" below.
michael@0 189 */
michael@0 190
michael@0 191 class ScopeObject : public JSObject
michael@0 192 {
michael@0 193 protected:
michael@0 194 static const uint32_t SCOPE_CHAIN_SLOT = 0;
michael@0 195
michael@0 196 public:
michael@0 197 /*
michael@0 198 * Since every scope chain terminates with a global object and GlobalObject
michael@0 199 * does not derive ScopeObject (it has a completely different layout), the
michael@0 200 * enclosing scope of a ScopeObject is necessarily non-null.
michael@0 201 */
michael@0 202 inline JSObject &enclosingScope() const {
michael@0 203 return getFixedSlot(SCOPE_CHAIN_SLOT).toObject();
michael@0 204 }
michael@0 205
michael@0 206 void setEnclosingScope(HandleObject obj);
michael@0 207
michael@0 208 /*
michael@0 209 * Get or set an aliased variable contained in this scope. Unaliased
michael@0 210 * variables should instead access the stack frame. Aliased variable access
michael@0 211 * is primarily made through JOF_SCOPECOORD ops which is why these members
michael@0 212 * take a ScopeCoordinate instead of just the slot index.
michael@0 213 */
michael@0 214 inline const Value &aliasedVar(ScopeCoordinate sc);
michael@0 215
michael@0 216 inline void setAliasedVar(JSContext *cx, ScopeCoordinate sc, PropertyName *name, const Value &v);
michael@0 217
michael@0 218 /* For jit access. */
michael@0 219 static size_t offsetOfEnclosingScope() {
michael@0 220 return getFixedSlotOffset(SCOPE_CHAIN_SLOT);
michael@0 221 }
michael@0 222
michael@0 223 static size_t enclosingScopeSlot() {
michael@0 224 return SCOPE_CHAIN_SLOT;
michael@0 225 }
michael@0 226 };
michael@0 227
michael@0 228 class CallObject : public ScopeObject
michael@0 229 {
michael@0 230 static const uint32_t CALLEE_SLOT = 1;
michael@0 231
michael@0 232 static CallObject *
michael@0 233 create(JSContext *cx, HandleScript script, HandleObject enclosing, HandleFunction callee);
michael@0 234
michael@0 235 public:
michael@0 236 static const Class class_;
michael@0 237
michael@0 238 /* These functions are internal and are exposed only for JITs. */
michael@0 239
michael@0 240 /*
michael@0 241 * Construct a bare-bones call object given a shape, a non-singleton type,
michael@0 242 * and slots pointer. The call object must be further initialized to be
michael@0 243 * usable.
michael@0 244 */
michael@0 245 static CallObject *
michael@0 246 create(JSContext *cx, HandleShape shape, HandleTypeObject type, HeapSlot *slots);
michael@0 247
michael@0 248 /*
michael@0 249 * Construct a bare-bones call object given a shape and slots pointer, and
michael@0 250 * make it have singleton type. The call object must be initialized to be
michael@0 251 * usable.
michael@0 252 */
michael@0 253 static CallObject *
michael@0 254 createSingleton(JSContext *cx, HandleShape shape, HeapSlot *slots);
michael@0 255
michael@0 256 static CallObject *
michael@0 257 createTemplateObject(JSContext *cx, HandleScript script, gc::InitialHeap heap);
michael@0 258
michael@0 259 static const uint32_t RESERVED_SLOTS = 2;
michael@0 260
michael@0 261 static CallObject *createForFunction(JSContext *cx, HandleObject enclosing, HandleFunction callee);
michael@0 262
michael@0 263 static CallObject *createForFunction(JSContext *cx, AbstractFramePtr frame);
michael@0 264 static CallObject *createForStrictEval(JSContext *cx, AbstractFramePtr frame);
michael@0 265
michael@0 266 /* True if this is for a strict mode eval frame. */
michael@0 267 bool isForEval() const {
michael@0 268 JS_ASSERT(getFixedSlot(CALLEE_SLOT).isObjectOrNull());
michael@0 269 JS_ASSERT_IF(getFixedSlot(CALLEE_SLOT).isObject(),
michael@0 270 getFixedSlot(CALLEE_SLOT).toObject().is<JSFunction>());
michael@0 271 return getFixedSlot(CALLEE_SLOT).isNull();
michael@0 272 }
michael@0 273
michael@0 274 /*
michael@0 275 * Returns the function for which this CallObject was created. (This may
michael@0 276 * only be called if !isForEval.)
michael@0 277 */
michael@0 278 JSFunction &callee() const {
michael@0 279 return getFixedSlot(CALLEE_SLOT).toObject().as<JSFunction>();
michael@0 280 }
michael@0 281
michael@0 282 /* Get/set the aliased variable referred to by 'bi'. */
michael@0 283 const Value &aliasedVar(AliasedFormalIter fi) {
michael@0 284 return getSlot(fi.scopeSlot());
michael@0 285 }
michael@0 286 inline void setAliasedVar(JSContext *cx, AliasedFormalIter fi, PropertyName *name,
michael@0 287 const Value &v);
michael@0 288
michael@0 289 /*
michael@0 290 * When an aliased var (var accessed by nested closures) is also aliased by
michael@0 291 * the arguments object, it must of course exist in one canonical location
michael@0 292 * and that location is always the CallObject. For this to work, the
michael@0 293 * ArgumentsObject stores special MagicValue in its array for forwarded-to-
michael@0 294 * CallObject variables. This MagicValue's payload is the slot of the
michael@0 295 * CallObject to access.
michael@0 296 */
michael@0 297 const Value &aliasedVarFromArguments(const Value &argsValue) {
michael@0 298 return getSlot(argsValue.magicUint32());
michael@0 299 }
michael@0 300 inline void setAliasedVarFromArguments(JSContext *cx, const Value &argsValue, jsid id,
michael@0 301 const Value &v);
michael@0 302
michael@0 303 /* For jit access. */
michael@0 304 static size_t offsetOfCallee() {
michael@0 305 return getFixedSlotOffset(CALLEE_SLOT);
michael@0 306 }
michael@0 307
michael@0 308 static size_t calleeSlot() {
michael@0 309 return CALLEE_SLOT;
michael@0 310 }
michael@0 311 };
michael@0 312
michael@0 313 class DeclEnvObject : public ScopeObject
michael@0 314 {
michael@0 315 // Pre-allocated slot for the named lambda.
michael@0 316 static const uint32_t LAMBDA_SLOT = 1;
michael@0 317
michael@0 318 public:
michael@0 319 static const uint32_t RESERVED_SLOTS = 2;
michael@0 320 static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT2_BACKGROUND;
michael@0 321
michael@0 322 static const Class class_;
michael@0 323
michael@0 324 static DeclEnvObject *
michael@0 325 createTemplateObject(JSContext *cx, HandleFunction fun, gc::InitialHeap heap);
michael@0 326
michael@0 327 static DeclEnvObject *create(JSContext *cx, HandleObject enclosing, HandleFunction callee);
michael@0 328
michael@0 329 static inline size_t lambdaSlot() {
michael@0 330 return LAMBDA_SLOT;
michael@0 331 }
michael@0 332 };
michael@0 333
michael@0 334 class NestedScopeObject : public ScopeObject
michael@0 335 {
michael@0 336 public:
michael@0 337 /*
michael@0 338 * A refinement of enclosingScope that returns nullptr if the enclosing
michael@0 339 * scope is not a NestedScopeObject.
michael@0 340 */
michael@0 341 inline NestedScopeObject *enclosingNestedScope() const;
michael@0 342
michael@0 343 // Return true if this object is a compile-time scope template.
michael@0 344 inline bool isStatic() { return !getProto(); }
michael@0 345
michael@0 346 // Return the static scope corresponding to this scope chain object.
michael@0 347 inline NestedScopeObject* staticScope() {
michael@0 348 JS_ASSERT(!isStatic());
michael@0 349 return &getProto()->as<NestedScopeObject>();
michael@0 350 }
michael@0 351
michael@0 352 // At compile-time it's possible for the scope chain to be null.
michael@0 353 JSObject *enclosingScopeForStaticScopeIter() {
michael@0 354 return getReservedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();
michael@0 355 }
michael@0 356
michael@0 357 void initEnclosingNestedScope(JSObject *obj) {
michael@0 358 JS_ASSERT(getReservedSlot(SCOPE_CHAIN_SLOT).isUndefined());
michael@0 359 setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(obj));
michael@0 360 }
michael@0 361
michael@0 362 /*
michael@0 363 * The parser uses 'enclosingNestedScope' as the prev-link in the
michael@0 364 * pc->staticScope stack. Note: in the case of hoisting, this prev-link will
michael@0 365 * not ultimately be the same as enclosingNestedScope;
michael@0 366 * initEnclosingNestedScope must be called separately in the
michael@0 367 * emitter. 'reset' is just for asserting stackiness.
michael@0 368 */
michael@0 369 void initEnclosingNestedScopeFromParser(NestedScopeObject *prev) {
michael@0 370 setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(prev));
michael@0 371 }
michael@0 372
michael@0 373 void resetEnclosingNestedScopeFromParser() {
michael@0 374 setReservedSlot(SCOPE_CHAIN_SLOT, UndefinedValue());
michael@0 375 }
michael@0 376 };
michael@0 377
michael@0 378 // With scope template objects on the static scope chain.
michael@0 379 class StaticWithObject : public NestedScopeObject
michael@0 380 {
michael@0 381 public:
michael@0 382 static const unsigned RESERVED_SLOTS = 1;
michael@0 383 static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT2_BACKGROUND;
michael@0 384
michael@0 385 static const Class class_;
michael@0 386
michael@0 387 static StaticWithObject *create(ExclusiveContext *cx);
michael@0 388 };
michael@0 389
michael@0 390 // With scope objects on the run-time scope chain.
michael@0 391 class DynamicWithObject : public NestedScopeObject
michael@0 392 {
michael@0 393 static const unsigned OBJECT_SLOT = 1;
michael@0 394 static const unsigned THIS_SLOT = 2;
michael@0 395
michael@0 396 public:
michael@0 397 static const unsigned RESERVED_SLOTS = 3;
michael@0 398 static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT4_BACKGROUND;
michael@0 399
michael@0 400 static const Class class_;
michael@0 401
michael@0 402 static DynamicWithObject *
michael@0 403 create(JSContext *cx, HandleObject object, HandleObject enclosing, HandleObject staticWith);
michael@0 404
michael@0 405 StaticWithObject& staticWith() const {
michael@0 406 return getProto()->as<StaticWithObject>();
michael@0 407 }
michael@0 408
michael@0 409 /* Return the 'o' in 'with (o)'. */
michael@0 410 JSObject &object() const {
michael@0 411 return getReservedSlot(OBJECT_SLOT).toObject();
michael@0 412 }
michael@0 413
michael@0 414 /* Return object for the 'this' class hook. */
michael@0 415 JSObject &withThis() const {
michael@0 416 return getReservedSlot(THIS_SLOT).toObject();
michael@0 417 }
michael@0 418 };
michael@0 419
michael@0 420 class BlockObject : public NestedScopeObject
michael@0 421 {
michael@0 422 protected:
michael@0 423 static const unsigned DEPTH_SLOT = 1;
michael@0 424
michael@0 425 public:
michael@0 426 static const unsigned RESERVED_SLOTS = 2;
michael@0 427 static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT4_BACKGROUND;
michael@0 428
michael@0 429 static const Class class_;
michael@0 430
michael@0 431 /* Return the abstract stack depth right before entering this nested scope. */
michael@0 432 uint32_t stackDepth() const {
michael@0 433 return getReservedSlot(DEPTH_SLOT).toPrivateUint32();
michael@0 434 }
michael@0 435
michael@0 436 /* Return the number of variables associated with this block. */
michael@0 437 uint32_t numVariables() const {
michael@0 438 // TODO: propertyCount() is O(n), use O(1) lastProperty()->slot() instead
michael@0 439 return propertyCount();
michael@0 440 }
michael@0 441
michael@0 442 protected:
michael@0 443 /* Blocks contain an object slot for each slot i: 0 <= i < slotCount. */
michael@0 444 const Value &slotValue(unsigned i) {
michael@0 445 return getSlotRef(RESERVED_SLOTS + i);
michael@0 446 }
michael@0 447
michael@0 448 void setSlotValue(unsigned i, const Value &v) {
michael@0 449 setSlot(RESERVED_SLOTS + i, v);
michael@0 450 }
michael@0 451 };
michael@0 452
michael@0 453 class StaticBlockObject : public BlockObject
michael@0 454 {
michael@0 455 static const unsigned LOCAL_OFFSET_SLOT = 1;
michael@0 456
michael@0 457 public:
michael@0 458 static StaticBlockObject *create(ExclusiveContext *cx);
michael@0 459
michael@0 460 /* See StaticScopeIter comment. */
michael@0 461 JSObject *enclosingStaticScope() const {
michael@0 462 return getFixedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();
michael@0 463 }
michael@0 464
michael@0 465 /*
michael@0 466 * Return the index (in the range [0, numVariables()) corresponding to the
michael@0 467 * given shape of a block object.
michael@0 468 */
michael@0 469 uint32_t shapeToIndex(const Shape &shape) {
michael@0 470 uint32_t slot = shape.slot();
michael@0 471 JS_ASSERT(slot - RESERVED_SLOTS < numVariables());
michael@0 472 return slot - RESERVED_SLOTS;
michael@0 473 }
michael@0 474
michael@0 475 /*
michael@0 476 * A refinement of enclosingStaticScope that returns nullptr if the enclosing
michael@0 477 * static scope is a JSFunction.
michael@0 478 */
michael@0 479 inline StaticBlockObject *enclosingBlock() const;
michael@0 480
michael@0 481 uint32_t localOffset() {
michael@0 482 return getReservedSlot(LOCAL_OFFSET_SLOT).toPrivateUint32();
michael@0 483 }
michael@0 484
michael@0 485 // Return the local corresponding to the 'var'th binding where 'var' is in the
michael@0 486 // range [0, numVariables()).
michael@0 487 uint32_t blockIndexToLocalIndex(uint32_t index) {
michael@0 488 JS_ASSERT(index < numVariables());
michael@0 489 return getReservedSlot(LOCAL_OFFSET_SLOT).toPrivateUint32() + index;
michael@0 490 }
michael@0 491
michael@0 492 // Return the slot corresponding to local variable 'local', where 'local' is
michael@0 493 // in the range [localOffset(), localOffset() + numVariables()). The result is
michael@0 494 // in the range [RESERVED_SLOTS, RESERVED_SLOTS + numVariables()).
michael@0 495 uint32_t localIndexToSlot(uint32_t local) {
michael@0 496 JS_ASSERT(local >= localOffset());
michael@0 497 local -= localOffset();
michael@0 498 JS_ASSERT(local < numVariables());
michael@0 499 return RESERVED_SLOTS + local;
michael@0 500 }
michael@0 501
michael@0 502 /*
michael@0 503 * A let binding is aliased if accessed lexically by nested functions or
michael@0 504 * dynamically through dynamic name lookup (eval, with, function::, etc).
michael@0 505 */
michael@0 506 bool isAliased(unsigned i) {
michael@0 507 return slotValue(i).isTrue();
michael@0 508 }
michael@0 509
michael@0 510 /*
michael@0 511 * A static block object is cloned (when entering the block) iff some
michael@0 512 * variable of the block isAliased.
michael@0 513 */
michael@0 514 bool needsClone() {
michael@0 515 return !getFixedSlot(RESERVED_SLOTS).isFalse();
michael@0 516 }
michael@0 517
michael@0 518 /* Frontend-only functions ***********************************************/
michael@0 519
michael@0 520 /* Initialization functions for above fields. */
michael@0 521 void setAliased(unsigned i, bool aliased) {
michael@0 522 JS_ASSERT_IF(i > 0, slotValue(i-1).isBoolean());
michael@0 523 setSlotValue(i, BooleanValue(aliased));
michael@0 524 if (aliased && !needsClone()) {
michael@0 525 setSlotValue(0, MagicValue(JS_BLOCK_NEEDS_CLONE));
michael@0 526 JS_ASSERT(needsClone());
michael@0 527 }
michael@0 528 }
michael@0 529
michael@0 530 void setLocalOffset(uint32_t offset) {
michael@0 531 JS_ASSERT(getReservedSlot(LOCAL_OFFSET_SLOT).isUndefined());
michael@0 532 initReservedSlot(LOCAL_OFFSET_SLOT, PrivateUint32Value(offset));
michael@0 533 }
michael@0 534
michael@0 535 /*
michael@0 536 * Frontend compilation temporarily uses the object's slots to link
michael@0 537 * a let var to its associated Definition parse node.
michael@0 538 */
michael@0 539 void setDefinitionParseNode(unsigned i, frontend::Definition *def) {
michael@0 540 JS_ASSERT(slotValue(i).isUndefined());
michael@0 541 setSlotValue(i, PrivateValue(def));
michael@0 542 }
michael@0 543
michael@0 544 frontend::Definition *definitionParseNode(unsigned i) {
michael@0 545 Value v = slotValue(i);
michael@0 546 return reinterpret_cast<frontend::Definition *>(v.toPrivate());
michael@0 547 }
michael@0 548
michael@0 549 /*
michael@0 550 * While ScopeCoordinate can generally reference up to 2^24 slots, block objects have an
michael@0 551 * additional limitation that all slot indices must be storable as uint16_t short-ids in the
michael@0 552 * associated Shape. If we could remove the block dependencies on shape->shortid, we could
michael@0 553 * remove INDEX_LIMIT.
michael@0 554 */
michael@0 555 static const unsigned LOCAL_INDEX_LIMIT = JS_BIT(16);
michael@0 556
michael@0 557 static Shape *addVar(ExclusiveContext *cx, Handle<StaticBlockObject*> block, HandleId id,
michael@0 558 unsigned index, bool *redeclared);
michael@0 559 };
michael@0 560
michael@0 561 class ClonedBlockObject : public BlockObject
michael@0 562 {
michael@0 563 public:
michael@0 564 static ClonedBlockObject *create(JSContext *cx, Handle<StaticBlockObject *> block,
michael@0 565 AbstractFramePtr frame);
michael@0 566
michael@0 567 /* The static block from which this block was cloned. */
michael@0 568 StaticBlockObject &staticBlock() const {
michael@0 569 return getProto()->as<StaticBlockObject>();
michael@0 570 }
michael@0 571
michael@0 572 /* Assuming 'put' has been called, return the value of the ith let var. */
michael@0 573 const Value &var(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) {
michael@0 574 JS_ASSERT_IF(checkAliasing, staticBlock().isAliased(i));
michael@0 575 return slotValue(i);
michael@0 576 }
michael@0 577
michael@0 578 void setVar(unsigned i, const Value &v, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) {
michael@0 579 JS_ASSERT_IF(checkAliasing, staticBlock().isAliased(i));
michael@0 580 setSlotValue(i, v);
michael@0 581 }
michael@0 582
michael@0 583 /* Copy in all the unaliased formals and locals. */
michael@0 584 void copyUnaliasedValues(AbstractFramePtr frame);
michael@0 585 };
michael@0 586
michael@0 587 template<XDRMode mode>
michael@0 588 bool
michael@0 589 XDRStaticBlockObject(XDRState<mode> *xdr, HandleObject enclosingScope,
michael@0 590 StaticBlockObject **objp);
michael@0 591
michael@0 592 template<XDRMode mode>
michael@0 593 bool
michael@0 594 XDRStaticWithObject(XDRState<mode> *xdr, HandleObject enclosingScope,
michael@0 595 StaticWithObject **objp);
michael@0 596
michael@0 597 extern JSObject *
michael@0 598 CloneNestedScopeObject(JSContext *cx, HandleObject enclosingScope, Handle<NestedScopeObject*> src);
michael@0 599
michael@0 600 /*****************************************************************************/
michael@0 601
michael@0 602 class ScopeIterKey;
michael@0 603 class ScopeIterVal;
michael@0 604
michael@0 605 /*
michael@0 606 * A scope iterator describes the active scopes enclosing the current point of
michael@0 607 * execution for a single frame, proceeding from inner to outer. Here, "frame"
michael@0 608 * means a single activation of: a function, eval, or global code. By design,
michael@0 609 * ScopeIter exposes *all* scopes, even those that have been optimized away
michael@0 610 * (i.e., no ScopeObject was created when entering the scope and thus there is
michael@0 611 * no ScopeObject on fp->scopeChain representing the scope).
michael@0 612 *
michael@0 613 * Note: ScopeIter iterates over all scopes *within* a frame which means that
michael@0 614 * all scopes are ScopeObjects. In particular, the GlobalObject enclosing
michael@0 615 * global code (and any random objects passed as scopes to Execute) will not
michael@0 616 * be included.
michael@0 617 */
michael@0 618 class ScopeIter
michael@0 619 {
michael@0 620 friend class ScopeIterKey;
michael@0 621 friend class ScopeIterVal;
michael@0 622
michael@0 623 public:
michael@0 624 enum Type { Call, Block, With, StrictEvalScope };
michael@0 625
michael@0 626 private:
michael@0 627 JSContext *cx;
michael@0 628 AbstractFramePtr frame_;
michael@0 629 RootedObject cur_;
michael@0 630 Rooted<NestedScopeObject *> staticScope_;
michael@0 631 Type type_;
michael@0 632 bool hasScopeObject_;
michael@0 633
michael@0 634 void settle();
michael@0 635
michael@0 636 /* ScopeIter does not have value semantics. */
michael@0 637 ScopeIter(const ScopeIter &si) MOZ_DELETE;
michael@0 638
michael@0 639 ScopeIter(JSContext *cx) MOZ_DELETE;
michael@0 640
michael@0 641 public:
michael@0 642
michael@0 643 /* Constructing from a copy of an existing ScopeIter. */
michael@0 644 ScopeIter(const ScopeIter &si, JSContext *cx
michael@0 645 MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
michael@0 646
michael@0 647 /* Constructing from AbstractFramePtr places ScopeIter on the innermost scope. */
michael@0 648 ScopeIter(AbstractFramePtr frame, jsbytecode *pc, JSContext *cx
michael@0 649 MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
michael@0 650
michael@0 651 /*
michael@0 652 * Without a stack frame, the resulting ScopeIter is done() with
michael@0 653 * enclosingScope() as given.
michael@0 654 */
michael@0 655 ScopeIter(JSObject &enclosingScope, JSContext *cx
michael@0 656 MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
michael@0 657
michael@0 658 ScopeIter(const ScopeIterVal &hashVal, JSContext *cx
michael@0 659 MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
michael@0 660
michael@0 661 bool done() const { return !frame_; }
michael@0 662
michael@0 663 /* If done(): */
michael@0 664
michael@0 665 JSObject &enclosingScope() const { JS_ASSERT(done()); return *cur_; }
michael@0 666
michael@0 667 /* If !done(): */
michael@0 668
michael@0 669 ScopeIter &operator++();
michael@0 670
michael@0 671 AbstractFramePtr frame() const { JS_ASSERT(!done()); return frame_; }
michael@0 672 Type type() const { JS_ASSERT(!done()); return type_; }
michael@0 673 bool hasScopeObject() const { JS_ASSERT(!done()); return hasScopeObject_; }
michael@0 674 ScopeObject &scope() const;
michael@0 675 NestedScopeObject* staticScope() const { return staticScope_; }
michael@0 676
michael@0 677 StaticBlockObject &staticBlock() const {
michael@0 678 JS_ASSERT(type() == Block);
michael@0 679 return staticScope_->as<StaticBlockObject>();
michael@0 680 }
michael@0 681
michael@0 682 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
michael@0 683 };
michael@0 684
michael@0 685 class ScopeIterKey
michael@0 686 {
michael@0 687 friend class ScopeIterVal;
michael@0 688
michael@0 689 AbstractFramePtr frame_;
michael@0 690 JSObject *cur_;
michael@0 691 NestedScopeObject *staticScope_;
michael@0 692 ScopeIter::Type type_;
michael@0 693 bool hasScopeObject_;
michael@0 694
michael@0 695 public:
michael@0 696 ScopeIterKey(const ScopeIter &si)
michael@0 697 : frame_(si.frame()), cur_(si.cur_), staticScope_(si.staticScope_), type_(si.type_),
michael@0 698 hasScopeObject_(si.hasScopeObject_) {}
michael@0 699
michael@0 700 AbstractFramePtr frame() const { return frame_; }
michael@0 701 JSObject *cur() const { return cur_; }
michael@0 702 NestedScopeObject *staticScope() const { return staticScope_; }
michael@0 703 ScopeIter::Type type() const { return type_; }
michael@0 704 bool hasScopeObject() const { return hasScopeObject_; }
michael@0 705 JSObject *enclosingScope() const { return cur_; }
michael@0 706 JSObject *&enclosingScope() { return cur_; }
michael@0 707
michael@0 708 /* For use as hash policy */
michael@0 709 typedef ScopeIterKey Lookup;
michael@0 710 static HashNumber hash(ScopeIterKey si);
michael@0 711 static bool match(ScopeIterKey si1, ScopeIterKey si2);
michael@0 712 bool operator!=(const ScopeIterKey &other) const {
michael@0 713 return frame_ != other.frame_ ||
michael@0 714 cur_ != other.cur_ ||
michael@0 715 staticScope_ != other.staticScope_ ||
michael@0 716 type_ != other.type_;
michael@0 717 }
michael@0 718 static void rekey(ScopeIterKey &k, const ScopeIterKey& newKey) {
michael@0 719 k = newKey;
michael@0 720 }
michael@0 721 };
michael@0 722
michael@0 723 class ScopeIterVal
michael@0 724 {
michael@0 725 friend class ScopeIter;
michael@0 726 friend class DebugScopes;
michael@0 727
michael@0 728 AbstractFramePtr frame_;
michael@0 729 RelocatablePtr<JSObject> cur_;
michael@0 730 RelocatablePtr<NestedScopeObject> staticScope_;
michael@0 731 ScopeIter::Type type_;
michael@0 732 bool hasScopeObject_;
michael@0 733
michael@0 734 static void staticAsserts();
michael@0 735
michael@0 736 public:
michael@0 737 ScopeIterVal(const ScopeIter &si)
michael@0 738 : frame_(si.frame()), cur_(si.cur_), staticScope_(si.staticScope_), type_(si.type_),
michael@0 739 hasScopeObject_(si.hasScopeObject_) {}
michael@0 740
michael@0 741 AbstractFramePtr frame() const { return frame_; }
michael@0 742 };
michael@0 743
michael@0 744 /*****************************************************************************/
michael@0 745
michael@0 746 /*
michael@0 747 * Debug scope objects
michael@0 748 *
michael@0 749 * The debugger effectively turns every opcode into a potential direct eval.
michael@0 750 * Naively, this would require creating a ScopeObject for every call/block
michael@0 751 * scope and using JSOP_GETALIASEDVAR for every access. To optimize this, the
michael@0 752 * engine assumes there is no debugger and optimizes scope access and creation
michael@0 753 * accordingly. When the debugger wants to perform an unexpected eval-in-frame
michael@0 754 * (or other, similar dynamic-scope-requiring operations), fp->scopeChain is
michael@0 755 * now incomplete: it may not contain all, or any, of the ScopeObjects to
michael@0 756 * represent the current scope.
michael@0 757 *
michael@0 758 * To resolve this, the debugger first calls GetDebugScopeFor(Function|Frame)
michael@0 759 * to synthesize a "debug scope chain". A debug scope chain is just a chain of
michael@0 760 * objects that fill in missing scopes and protect the engine from unexpected
michael@0 761 * access. (The latter means that some debugger operations, like redefining a
michael@0 762 * lexical binding, can fail when a true eval would succeed.) To do both of
michael@0 763 * these things, GetDebugScopeFor* creates a new proxy DebugScopeObject to sit
michael@0 764 * in front of every existing ScopeObject.
michael@0 765 *
michael@0 766 * GetDebugScopeFor* ensures the invariant that the same DebugScopeObject is
michael@0 767 * always produced for the same underlying scope (optimized or not!). This is
michael@0 768 * maintained by some bookkeeping information stored in DebugScopes.
michael@0 769 */
michael@0 770
michael@0 771 extern JSObject *
michael@0 772 GetDebugScopeForFunction(JSContext *cx, HandleFunction fun);
michael@0 773
michael@0 774 extern JSObject *
michael@0 775 GetDebugScopeForFrame(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc);
michael@0 776
michael@0 777 /* Provides debugger access to a scope. */
michael@0 778 class DebugScopeObject : public ProxyObject
michael@0 779 {
michael@0 780 /*
michael@0 781 * The enclosing scope on the dynamic scope chain. This slot is analogous
michael@0 782 * to the SCOPE_CHAIN_SLOT of a ScopeObject.
michael@0 783 */
michael@0 784 static const unsigned ENCLOSING_EXTRA = 0;
michael@0 785
michael@0 786 /*
michael@0 787 * NullValue or a dense array holding the unaliased variables of a function
michael@0 788 * frame that has been popped.
michael@0 789 */
michael@0 790 static const unsigned SNAPSHOT_EXTRA = 1;
michael@0 791
michael@0 792 public:
michael@0 793 static DebugScopeObject *create(JSContext *cx, ScopeObject &scope, HandleObject enclosing);
michael@0 794
michael@0 795 ScopeObject &scope() const;
michael@0 796 JSObject &enclosingScope() const;
michael@0 797
michael@0 798 /* May only be called for proxies to function call objects. */
michael@0 799 JSObject *maybeSnapshot() const;
michael@0 800 void initSnapshot(JSObject &snapshot);
michael@0 801
michael@0 802 /* Currently, the 'declarative' scopes are Call and Block. */
michael@0 803 bool isForDeclarative() const;
michael@0 804
michael@0 805 // Get a property by 'id', but returns sentinel values instead of throwing
michael@0 806 // on exceptional cases.
michael@0 807 bool getMaybeSentinelValue(JSContext *cx, HandleId id, MutableHandleValue vp);
michael@0 808 };
michael@0 809
michael@0 810 /* Maintains per-compartment debug scope bookkeeping information. */
michael@0 811 class DebugScopes
michael@0 812 {
michael@0 813 /* The map from (non-debug) scopes to debug scopes. */
michael@0 814 typedef WeakMap<EncapsulatedPtrObject, RelocatablePtrObject> ObjectWeakMap;
michael@0 815 ObjectWeakMap proxiedScopes;
michael@0 816 static MOZ_ALWAYS_INLINE void proxiedScopesPostWriteBarrier(JSRuntime *rt, ObjectWeakMap *map,
michael@0 817 const EncapsulatedPtrObject &key);
michael@0 818
michael@0 819 /*
michael@0 820 * The map from live frames which have optimized-away scopes to the
michael@0 821 * corresponding debug scopes.
michael@0 822 */
michael@0 823 typedef HashMap<ScopeIterKey,
michael@0 824 ReadBarriered<DebugScopeObject>,
michael@0 825 ScopeIterKey,
michael@0 826 RuntimeAllocPolicy> MissingScopeMap;
michael@0 827 MissingScopeMap missingScopes;
michael@0 828 class MissingScopesRef;
michael@0 829 static MOZ_ALWAYS_INLINE void missingScopesPostWriteBarrier(JSRuntime *rt, MissingScopeMap *map,
michael@0 830 const ScopeIterKey &key);
michael@0 831
michael@0 832 /*
michael@0 833 * The map from scope objects of live frames to the live frame. This map
michael@0 834 * updated lazily whenever the debugger needs the information. In between
michael@0 835 * two lazy updates, liveScopes becomes incomplete (but not invalid, onPop*
michael@0 836 * removes scopes as they are popped). Thus, two consecutive debugger lazy
michael@0 837 * updates of liveScopes need only fill in the new scopes.
michael@0 838 */
michael@0 839 typedef HashMap<ScopeObject *,
michael@0 840 ScopeIterVal,
michael@0 841 DefaultHasher<ScopeObject *>,
michael@0 842 RuntimeAllocPolicy> LiveScopeMap;
michael@0 843 LiveScopeMap liveScopes;
michael@0 844 static MOZ_ALWAYS_INLINE void liveScopesPostWriteBarrier(JSRuntime *rt, LiveScopeMap *map,
michael@0 845 ScopeObject *key);
michael@0 846
michael@0 847 public:
michael@0 848 DebugScopes(JSContext *c);
michael@0 849 ~DebugScopes();
michael@0 850
michael@0 851 private:
michael@0 852 bool init();
michael@0 853
michael@0 854 static DebugScopes *ensureCompartmentData(JSContext *cx);
michael@0 855
michael@0 856 public:
michael@0 857 void mark(JSTracer *trc);
michael@0 858 void sweep(JSRuntime *rt);
michael@0 859 #if defined(JSGC_GENERATIONAL) && defined(JS_GC_ZEAL)
michael@0 860 void checkHashTablesAfterMovingGC(JSRuntime *rt);
michael@0 861 #endif
michael@0 862
michael@0 863 static DebugScopeObject *hasDebugScope(JSContext *cx, ScopeObject &scope);
michael@0 864 static bool addDebugScope(JSContext *cx, ScopeObject &scope, DebugScopeObject &debugScope);
michael@0 865
michael@0 866 static DebugScopeObject *hasDebugScope(JSContext *cx, const ScopeIter &si);
michael@0 867 static bool addDebugScope(JSContext *cx, const ScopeIter &si, DebugScopeObject &debugScope);
michael@0 868
michael@0 869 static bool updateLiveScopes(JSContext *cx);
michael@0 870 static ScopeIterVal *hasLiveScope(ScopeObject &scope);
michael@0 871
michael@0 872 // In debug-mode, these must be called whenever exiting a scope that might
michael@0 873 // have stack-allocated locals.
michael@0 874 static void onPopCall(AbstractFramePtr frame, JSContext *cx);
michael@0 875 static void onPopBlock(JSContext *cx, const ScopeIter &si);
michael@0 876 static void onPopBlock(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc);
michael@0 877 static void onPopWith(AbstractFramePtr frame);
michael@0 878 static void onPopStrictEvalScope(AbstractFramePtr frame);
michael@0 879 static void onCompartmentLeaveDebugMode(JSCompartment *c);
michael@0 880 };
michael@0 881
michael@0 882 } /* namespace js */
michael@0 883
michael@0 884 template<>
michael@0 885 inline bool
michael@0 886 JSObject::is<js::NestedScopeObject>() const
michael@0 887 {
michael@0 888 return is<js::BlockObject>() || is<js::StaticWithObject>() || is<js::DynamicWithObject>();
michael@0 889 }
michael@0 890
michael@0 891 template<>
michael@0 892 inline bool
michael@0 893 JSObject::is<js::ScopeObject>() const
michael@0 894 {
michael@0 895 return is<js::CallObject>() || is<js::DeclEnvObject>() || is<js::NestedScopeObject>();
michael@0 896 }
michael@0 897
michael@0 898 template<>
michael@0 899 inline bool
michael@0 900 JSObject::is<js::DebugScopeObject>() const
michael@0 901 {
michael@0 902 extern bool js_IsDebugScopeSlow(js::ProxyObject *proxy);
michael@0 903
michael@0 904 // Note: don't use is<ProxyObject>() here -- it also matches subclasses!
michael@0 905 return hasClass(&js::ProxyObject::uncallableClass_) &&
michael@0 906 js_IsDebugScopeSlow(&const_cast<JSObject*>(this)->as<js::ProxyObject>());
michael@0 907 }
michael@0 908
michael@0 909 template<>
michael@0 910 inline bool
michael@0 911 JSObject::is<js::ClonedBlockObject>() const
michael@0 912 {
michael@0 913 return is<js::BlockObject>() && !!getProto();
michael@0 914 }
michael@0 915
michael@0 916 template<>
michael@0 917 inline bool
michael@0 918 JSObject::is<js::StaticBlockObject>() const
michael@0 919 {
michael@0 920 return is<js::BlockObject>() && !getProto();
michael@0 921 }
michael@0 922
michael@0 923 inline JSObject *
michael@0 924 JSObject::enclosingScope()
michael@0 925 {
michael@0 926 return is<js::ScopeObject>()
michael@0 927 ? &as<js::ScopeObject>().enclosingScope()
michael@0 928 : is<js::DebugScopeObject>()
michael@0 929 ? &as<js::DebugScopeObject>().enclosingScope()
michael@0 930 : getParent();
michael@0 931 }
michael@0 932
michael@0 933 namespace js {
michael@0 934
michael@0 935 inline const Value &
michael@0 936 ScopeObject::aliasedVar(ScopeCoordinate sc)
michael@0 937 {
michael@0 938 JS_ASSERT(is<CallObject>() || is<ClonedBlockObject>());
michael@0 939 return getSlot(sc.slot());
michael@0 940 }
michael@0 941
michael@0 942 inline NestedScopeObject *
michael@0 943 NestedScopeObject::enclosingNestedScope() const
michael@0 944 {
michael@0 945 JSObject *obj = getReservedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();
michael@0 946 return obj && obj->is<NestedScopeObject>() ? &obj->as<NestedScopeObject>() : nullptr;
michael@0 947 }
michael@0 948
michael@0 949 #ifdef DEBUG
michael@0 950 bool
michael@0 951 AnalyzeEntrainedVariables(JSContext *cx, HandleScript script);
michael@0 952 #endif
michael@0 953
michael@0 954 } // namespace js
michael@0 955
michael@0 956 #endif /* vm_ScopeObject_h */

mercurial