js/src/vm/ScopeObject.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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 #include "vm/ScopeObject-inl.h"
michael@0 8
michael@0 9 #include "mozilla/PodOperations.h"
michael@0 10
michael@0 11 #include "jscompartment.h"
michael@0 12 #include "jsiter.h"
michael@0 13
michael@0 14 #include "vm/ArgumentsObject.h"
michael@0 15 #include "vm/GlobalObject.h"
michael@0 16 #include "vm/ProxyObject.h"
michael@0 17 #include "vm/Shape.h"
michael@0 18 #include "vm/Xdr.h"
michael@0 19
michael@0 20 #include "jsatominlines.h"
michael@0 21 #include "jsobjinlines.h"
michael@0 22 #include "jsscriptinlines.h"
michael@0 23
michael@0 24 #include "vm/Stack-inl.h"
michael@0 25
michael@0 26 using namespace js;
michael@0 27 using namespace js::types;
michael@0 28
michael@0 29 using mozilla::PodZero;
michael@0 30
michael@0 31 typedef Rooted<ArgumentsObject *> RootedArgumentsObject;
michael@0 32 typedef MutableHandle<ArgumentsObject *> MutableHandleArgumentsObject;
michael@0 33
michael@0 34 /*****************************************************************************/
michael@0 35
michael@0 36 static JSObject *
michael@0 37 InnermostStaticScope(JSScript *script, jsbytecode *pc)
michael@0 38 {
michael@0 39 JS_ASSERT(script->containsPC(pc));
michael@0 40 JS_ASSERT(JOF_OPTYPE(*pc) == JOF_SCOPECOORD);
michael@0 41
michael@0 42 NestedScopeObject *scope = script->getStaticScope(pc);
michael@0 43 if (scope)
michael@0 44 return scope;
michael@0 45 return script->functionNonDelazifying();
michael@0 46 }
michael@0 47
michael@0 48 Shape *
michael@0 49 js::ScopeCoordinateToStaticScopeShape(JSScript *script, jsbytecode *pc)
michael@0 50 {
michael@0 51 StaticScopeIter<NoGC> ssi(InnermostStaticScope(script, pc));
michael@0 52 uint32_t hops = ScopeCoordinate(pc).hops();
michael@0 53 while (true) {
michael@0 54 JS_ASSERT(!ssi.done());
michael@0 55 if (ssi.hasDynamicScopeObject()) {
michael@0 56 if (!hops)
michael@0 57 break;
michael@0 58 hops--;
michael@0 59 }
michael@0 60 ssi++;
michael@0 61 }
michael@0 62 return ssi.scopeShape();
michael@0 63 }
michael@0 64
michael@0 65 static const uint32_t SCOPE_COORDINATE_NAME_THRESHOLD = 20;
michael@0 66
michael@0 67 void
michael@0 68 ScopeCoordinateNameCache::purge()
michael@0 69 {
michael@0 70 shape = nullptr;
michael@0 71 if (map.initialized())
michael@0 72 map.finish();
michael@0 73 }
michael@0 74
michael@0 75 PropertyName *
michael@0 76 js::ScopeCoordinateName(ScopeCoordinateNameCache &cache, JSScript *script, jsbytecode *pc)
michael@0 77 {
michael@0 78 Shape *shape = ScopeCoordinateToStaticScopeShape(script, pc);
michael@0 79 if (shape != cache.shape && shape->slot() >= SCOPE_COORDINATE_NAME_THRESHOLD) {
michael@0 80 cache.purge();
michael@0 81 if (cache.map.init(shape->slot())) {
michael@0 82 cache.shape = shape;
michael@0 83 Shape::Range<NoGC> r(shape);
michael@0 84 while (!r.empty()) {
michael@0 85 if (!cache.map.putNew(r.front().slot(), r.front().propid())) {
michael@0 86 cache.purge();
michael@0 87 break;
michael@0 88 }
michael@0 89 r.popFront();
michael@0 90 }
michael@0 91 }
michael@0 92 }
michael@0 93
michael@0 94 jsid id;
michael@0 95 ScopeCoordinate sc(pc);
michael@0 96 if (shape == cache.shape) {
michael@0 97 ScopeCoordinateNameCache::Map::Ptr p = cache.map.lookup(sc.slot());
michael@0 98 id = p->value();
michael@0 99 } else {
michael@0 100 Shape::Range<NoGC> r(shape);
michael@0 101 while (r.front().slot() != sc.slot())
michael@0 102 r.popFront();
michael@0 103 id = r.front().propidRaw();
michael@0 104 }
michael@0 105
michael@0 106 /* Beware nameless destructuring formal. */
michael@0 107 if (!JSID_IS_ATOM(id))
michael@0 108 return script->runtimeFromAnyThread()->commonNames->empty;
michael@0 109 return JSID_TO_ATOM(id)->asPropertyName();
michael@0 110 }
michael@0 111
michael@0 112 JSScript *
michael@0 113 js::ScopeCoordinateFunctionScript(JSScript *script, jsbytecode *pc)
michael@0 114 {
michael@0 115 StaticScopeIter<NoGC> ssi(InnermostStaticScope(script, pc));
michael@0 116 uint32_t hops = ScopeCoordinate(pc).hops();
michael@0 117 while (true) {
michael@0 118 if (ssi.hasDynamicScopeObject()) {
michael@0 119 if (!hops)
michael@0 120 break;
michael@0 121 hops--;
michael@0 122 }
michael@0 123 ssi++;
michael@0 124 }
michael@0 125 if (ssi.type() != StaticScopeIter<NoGC>::FUNCTION)
michael@0 126 return nullptr;
michael@0 127 return ssi.funScript();
michael@0 128 }
michael@0 129
michael@0 130 /*****************************************************************************/
michael@0 131
michael@0 132 void
michael@0 133 ScopeObject::setEnclosingScope(HandleObject obj)
michael@0 134 {
michael@0 135 JS_ASSERT_IF(obj->is<CallObject>() || obj->is<DeclEnvObject>() || obj->is<BlockObject>(),
michael@0 136 obj->isDelegate());
michael@0 137 setFixedSlot(SCOPE_CHAIN_SLOT, ObjectValue(*obj));
michael@0 138 }
michael@0 139
michael@0 140 CallObject *
michael@0 141 CallObject::create(JSContext *cx, HandleShape shape, HandleTypeObject type, HeapSlot *slots)
michael@0 142 {
michael@0 143 MOZ_ASSERT(!type->singleton(),
michael@0 144 "passed a singleton type to create() (use createSingleton() "
michael@0 145 "instead)");
michael@0 146 gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
michael@0 147 MOZ_ASSERT(CanBeFinalizedInBackground(kind, &CallObject::class_));
michael@0 148 kind = gc::GetBackgroundAllocKind(kind);
michael@0 149
michael@0 150 JSObject *obj = JSObject::create(cx, kind, gc::DefaultHeap, shape, type, slots);
michael@0 151 if (!obj)
michael@0 152 return nullptr;
michael@0 153
michael@0 154 return &obj->as<CallObject>();
michael@0 155 }
michael@0 156
michael@0 157 CallObject *
michael@0 158 CallObject::createSingleton(JSContext *cx, HandleShape shape, HeapSlot *slots)
michael@0 159 {
michael@0 160 gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
michael@0 161 MOZ_ASSERT(CanBeFinalizedInBackground(kind, &CallObject::class_));
michael@0 162 kind = gc::GetBackgroundAllocKind(kind);
michael@0 163
michael@0 164 RootedTypeObject type(cx, cx->getSingletonType(&class_, nullptr));
michael@0 165 if (!type)
michael@0 166 return nullptr;
michael@0 167 RootedObject obj(cx, JSObject::create(cx, kind, gc::TenuredHeap, shape, type, slots));
michael@0 168 if (!obj)
michael@0 169 return nullptr;
michael@0 170
michael@0 171 MOZ_ASSERT(obj->hasSingletonType(),
michael@0 172 "type created inline above must be a singleton");
michael@0 173
michael@0 174 return &obj->as<CallObject>();
michael@0 175 }
michael@0 176
michael@0 177 /*
michael@0 178 * Create a CallObject for a JSScript that is not initialized to any particular
michael@0 179 * callsite. This object can either be initialized (with an enclosing scope and
michael@0 180 * callee) or used as a template for jit compilation.
michael@0 181 */
michael@0 182 CallObject *
michael@0 183 CallObject::createTemplateObject(JSContext *cx, HandleScript script, gc::InitialHeap heap)
michael@0 184 {
michael@0 185 RootedShape shape(cx, script->bindings.callObjShape());
michael@0 186 JS_ASSERT(shape->getObjectClass() == &class_);
michael@0 187
michael@0 188 RootedTypeObject type(cx, cx->getNewType(&class_, nullptr));
michael@0 189 if (!type)
michael@0 190 return nullptr;
michael@0 191
michael@0 192 gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
michael@0 193 JS_ASSERT(CanBeFinalizedInBackground(kind, &class_));
michael@0 194 kind = gc::GetBackgroundAllocKind(kind);
michael@0 195
michael@0 196 JSObject *obj = JSObject::create(cx, kind, heap, shape, type);
michael@0 197 if (!obj)
michael@0 198 return nullptr;
michael@0 199
michael@0 200 return &obj->as<CallObject>();
michael@0 201 }
michael@0 202
michael@0 203 /*
michael@0 204 * Construct a call object for the given bindings. If this is a call object
michael@0 205 * for a function invocation, callee should be the function being called.
michael@0 206 * Otherwise it must be a call object for eval of strict mode code, and callee
michael@0 207 * must be null.
michael@0 208 */
michael@0 209 CallObject *
michael@0 210 CallObject::create(JSContext *cx, HandleScript script, HandleObject enclosing, HandleFunction callee)
michael@0 211 {
michael@0 212 gc::InitialHeap heap = script->treatAsRunOnce() ? gc::TenuredHeap : gc::DefaultHeap;
michael@0 213 CallObject *callobj = CallObject::createTemplateObject(cx, script, heap);
michael@0 214 if (!callobj)
michael@0 215 return nullptr;
michael@0 216
michael@0 217 callobj->as<ScopeObject>().setEnclosingScope(enclosing);
michael@0 218 callobj->initFixedSlot(CALLEE_SLOT, ObjectOrNullValue(callee));
michael@0 219
michael@0 220 if (script->treatAsRunOnce()) {
michael@0 221 Rooted<CallObject*> ncallobj(cx, callobj);
michael@0 222 if (!JSObject::setSingletonType(cx, ncallobj))
michael@0 223 return nullptr;
michael@0 224 return ncallobj;
michael@0 225 }
michael@0 226
michael@0 227 return callobj;
michael@0 228 }
michael@0 229
michael@0 230 CallObject *
michael@0 231 CallObject::createForFunction(JSContext *cx, HandleObject enclosing, HandleFunction callee)
michael@0 232 {
michael@0 233 RootedObject scopeChain(cx, enclosing);
michael@0 234 JS_ASSERT(scopeChain);
michael@0 235
michael@0 236 /*
michael@0 237 * For a named function expression Call's parent points to an environment
michael@0 238 * object holding function's name.
michael@0 239 */
michael@0 240 if (callee->isNamedLambda()) {
michael@0 241 scopeChain = DeclEnvObject::create(cx, scopeChain, callee);
michael@0 242 if (!scopeChain)
michael@0 243 return nullptr;
michael@0 244 }
michael@0 245
michael@0 246 RootedScript script(cx, callee->nonLazyScript());
michael@0 247 return create(cx, script, scopeChain, callee);
michael@0 248 }
michael@0 249
michael@0 250 CallObject *
michael@0 251 CallObject::createForFunction(JSContext *cx, AbstractFramePtr frame)
michael@0 252 {
michael@0 253 JS_ASSERT(frame.isNonEvalFunctionFrame());
michael@0 254 assertSameCompartment(cx, frame);
michael@0 255
michael@0 256 RootedObject scopeChain(cx, frame.scopeChain());
michael@0 257 RootedFunction callee(cx, frame.callee());
michael@0 258
michael@0 259 CallObject *callobj = createForFunction(cx, scopeChain, callee);
michael@0 260 if (!callobj)
michael@0 261 return nullptr;
michael@0 262
michael@0 263 /* Copy in the closed-over formal arguments. */
michael@0 264 for (AliasedFormalIter i(frame.script()); i; i++) {
michael@0 265 callobj->setAliasedVar(cx, i, i->name(),
michael@0 266 frame.unaliasedFormal(i.frameIndex(), DONT_CHECK_ALIASING));
michael@0 267 }
michael@0 268
michael@0 269 return callobj;
michael@0 270 }
michael@0 271
michael@0 272 CallObject *
michael@0 273 CallObject::createForStrictEval(JSContext *cx, AbstractFramePtr frame)
michael@0 274 {
michael@0 275 JS_ASSERT(frame.isStrictEvalFrame());
michael@0 276 JS_ASSERT_IF(frame.isInterpreterFrame(), cx->interpreterFrame() == frame.asInterpreterFrame());
michael@0 277 JS_ASSERT_IF(frame.isInterpreterFrame(), cx->interpreterRegs().pc == frame.script()->code());
michael@0 278
michael@0 279 RootedFunction callee(cx);
michael@0 280 RootedScript script(cx, frame.script());
michael@0 281 RootedObject scopeChain(cx, frame.scopeChain());
michael@0 282 return create(cx, script, scopeChain, callee);
michael@0 283 }
michael@0 284
michael@0 285 const Class CallObject::class_ = {
michael@0 286 "Call",
michael@0 287 JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_RESERVED_SLOTS(CallObject::RESERVED_SLOTS),
michael@0 288 JS_PropertyStub, /* addProperty */
michael@0 289 JS_DeletePropertyStub, /* delProperty */
michael@0 290 JS_PropertyStub, /* getProperty */
michael@0 291 JS_StrictPropertyStub, /* setProperty */
michael@0 292 JS_EnumerateStub,
michael@0 293 JS_ResolveStub,
michael@0 294 nullptr /* convert: Leave it nullptr so we notice if calls ever escape */
michael@0 295 };
michael@0 296
michael@0 297 const Class DeclEnvObject::class_ = {
michael@0 298 js_Object_str,
michael@0 299 JSCLASS_HAS_RESERVED_SLOTS(DeclEnvObject::RESERVED_SLOTS) |
michael@0 300 JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
michael@0 301 JS_PropertyStub, /* addProperty */
michael@0 302 JS_DeletePropertyStub, /* delProperty */
michael@0 303 JS_PropertyStub, /* getProperty */
michael@0 304 JS_StrictPropertyStub, /* setProperty */
michael@0 305 JS_EnumerateStub,
michael@0 306 JS_ResolveStub,
michael@0 307 JS_ConvertStub
michael@0 308 };
michael@0 309
michael@0 310 /*
michael@0 311 * Create a DeclEnvObject for a JSScript that is not initialized to any
michael@0 312 * particular callsite. This object can either be initialized (with an enclosing
michael@0 313 * scope and callee) or used as a template for jit compilation.
michael@0 314 */
michael@0 315 DeclEnvObject *
michael@0 316 DeclEnvObject::createTemplateObject(JSContext *cx, HandleFunction fun, gc::InitialHeap heap)
michael@0 317 {
michael@0 318 JS_ASSERT(IsNurseryAllocable(FINALIZE_KIND));
michael@0 319
michael@0 320 RootedTypeObject type(cx, cx->getNewType(&class_, nullptr));
michael@0 321 if (!type)
michael@0 322 return nullptr;
michael@0 323
michael@0 324 RootedShape emptyDeclEnvShape(cx);
michael@0 325 emptyDeclEnvShape = EmptyShape::getInitialShape(cx, &class_, nullptr,
michael@0 326 cx->global(), nullptr, FINALIZE_KIND,
michael@0 327 BaseShape::DELEGATE);
michael@0 328 if (!emptyDeclEnvShape)
michael@0 329 return nullptr;
michael@0 330
michael@0 331 RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, heap, emptyDeclEnvShape, type));
michael@0 332 if (!obj)
michael@0 333 return nullptr;
michael@0 334
michael@0 335 // Assign a fixed slot to a property with the same name as the lambda.
michael@0 336 Rooted<jsid> id(cx, AtomToId(fun->atom()));
michael@0 337 const Class *clasp = obj->getClass();
michael@0 338 unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY;
michael@0 339 if (!JSObject::putProperty<SequentialExecution>(cx, obj, id, clasp->getProperty,
michael@0 340 clasp->setProperty, lambdaSlot(), attrs, 0)) {
michael@0 341 return nullptr;
michael@0 342 }
michael@0 343
michael@0 344 JS_ASSERT(!obj->hasDynamicSlots());
michael@0 345 return &obj->as<DeclEnvObject>();
michael@0 346 }
michael@0 347
michael@0 348 DeclEnvObject *
michael@0 349 DeclEnvObject::create(JSContext *cx, HandleObject enclosing, HandleFunction callee)
michael@0 350 {
michael@0 351 RootedObject obj(cx, createTemplateObject(cx, callee, gc::DefaultHeap));
michael@0 352 if (!obj)
michael@0 353 return nullptr;
michael@0 354
michael@0 355 obj->as<ScopeObject>().setEnclosingScope(enclosing);
michael@0 356 obj->setFixedSlot(lambdaSlot(), ObjectValue(*callee));
michael@0 357 return &obj->as<DeclEnvObject>();
michael@0 358 }
michael@0 359
michael@0 360 template<XDRMode mode>
michael@0 361 bool
michael@0 362 js::XDRStaticWithObject(XDRState<mode> *xdr, HandleObject enclosingScope, StaticWithObject **objp)
michael@0 363 {
michael@0 364 if (mode == XDR_DECODE) {
michael@0 365 JSContext *cx = xdr->cx();
michael@0 366 Rooted<StaticWithObject*> obj(cx, StaticWithObject::create(cx));
michael@0 367 if (!obj)
michael@0 368 return false;
michael@0 369 obj->initEnclosingNestedScope(enclosingScope);
michael@0 370 *objp = obj;
michael@0 371 }
michael@0 372 // For encoding, there is nothing to do. The only information that is
michael@0 373 // encoded by a StaticWithObject is its presence on the scope chain, and the
michael@0 374 // script XDR handler already takes care of that.
michael@0 375
michael@0 376 return true;
michael@0 377 }
michael@0 378
michael@0 379 template bool
michael@0 380 js::XDRStaticWithObject(XDRState<XDR_ENCODE> *, HandleObject, StaticWithObject **);
michael@0 381
michael@0 382 template bool
michael@0 383 js::XDRStaticWithObject(XDRState<XDR_DECODE> *, HandleObject, StaticWithObject **);
michael@0 384
michael@0 385 StaticWithObject *
michael@0 386 StaticWithObject::create(ExclusiveContext *cx)
michael@0 387 {
michael@0 388 RootedTypeObject type(cx, cx->getNewType(&class_, nullptr));
michael@0 389 if (!type)
michael@0 390 return nullptr;
michael@0 391
michael@0 392 RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(nullptr),
michael@0 393 nullptr, nullptr, FINALIZE_KIND));
michael@0 394 if (!shape)
michael@0 395 return nullptr;
michael@0 396
michael@0 397 RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, gc::TenuredHeap, shape, type));
michael@0 398 if (!obj)
michael@0 399 return nullptr;
michael@0 400
michael@0 401 return &obj->as<StaticWithObject>();
michael@0 402 }
michael@0 403
michael@0 404 static JSObject *
michael@0 405 CloneStaticWithObject(JSContext *cx, HandleObject enclosingScope, Handle<StaticWithObject*> srcWith)
michael@0 406 {
michael@0 407 Rooted<StaticWithObject*> clone(cx, StaticWithObject::create(cx));
michael@0 408 if (!clone)
michael@0 409 return nullptr;
michael@0 410
michael@0 411 clone->initEnclosingNestedScope(enclosingScope);
michael@0 412
michael@0 413 return clone;
michael@0 414 }
michael@0 415
michael@0 416 DynamicWithObject *
michael@0 417 DynamicWithObject::create(JSContext *cx, HandleObject object, HandleObject enclosing,
michael@0 418 HandleObject staticWith)
michael@0 419 {
michael@0 420 JS_ASSERT(staticWith->is<StaticWithObject>());
michael@0 421 RootedTypeObject type(cx, cx->getNewType(&class_, staticWith.get()));
michael@0 422 if (!type)
michael@0 423 return nullptr;
michael@0 424
michael@0 425 RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(staticWith),
michael@0 426 &enclosing->global(), nullptr,
michael@0 427 FINALIZE_KIND));
michael@0 428 if (!shape)
michael@0 429 return nullptr;
michael@0 430
michael@0 431 RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, gc::DefaultHeap, shape, type));
michael@0 432 if (!obj)
michael@0 433 return nullptr;
michael@0 434
michael@0 435 JSObject *thisp = JSObject::thisObject(cx, object);
michael@0 436 if (!thisp)
michael@0 437 return nullptr;
michael@0 438
michael@0 439 obj->as<ScopeObject>().setEnclosingScope(enclosing);
michael@0 440 obj->setFixedSlot(OBJECT_SLOT, ObjectValue(*object));
michael@0 441 obj->setFixedSlot(THIS_SLOT, ObjectValue(*thisp));
michael@0 442
michael@0 443 return &obj->as<DynamicWithObject>();
michael@0 444 }
michael@0 445
michael@0 446 static bool
michael@0 447 with_LookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
michael@0 448 MutableHandleObject objp, MutableHandleShape propp)
michael@0 449 {
michael@0 450 RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
michael@0 451 return JSObject::lookupGeneric(cx, actual, id, objp, propp);
michael@0 452 }
michael@0 453
michael@0 454 static bool
michael@0 455 with_LookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
michael@0 456 MutableHandleObject objp, MutableHandleShape propp)
michael@0 457 {
michael@0 458 Rooted<jsid> id(cx, NameToId(name));
michael@0 459 return with_LookupGeneric(cx, obj, id, objp, propp);
michael@0 460 }
michael@0 461
michael@0 462 static bool
michael@0 463 with_LookupElement(JSContext *cx, HandleObject obj, uint32_t index,
michael@0 464 MutableHandleObject objp, MutableHandleShape propp)
michael@0 465 {
michael@0 466 RootedId id(cx);
michael@0 467 if (!IndexToId(cx, index, &id))
michael@0 468 return false;
michael@0 469 return with_LookupGeneric(cx, obj, id, objp, propp);
michael@0 470 }
michael@0 471
michael@0 472 static bool
michael@0 473 with_GetGeneric(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id,
michael@0 474 MutableHandleValue vp)
michael@0 475 {
michael@0 476 RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
michael@0 477 return JSObject::getGeneric(cx, actual, actual, id, vp);
michael@0 478 }
michael@0 479
michael@0 480 static bool
michael@0 481 with_GetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandlePropertyName name,
michael@0 482 MutableHandleValue vp)
michael@0 483 {
michael@0 484 RootedId id(cx, NameToId(name));
michael@0 485 return with_GetGeneric(cx, obj, receiver, id, vp);
michael@0 486 }
michael@0 487
michael@0 488 static bool
michael@0 489 with_GetElement(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index,
michael@0 490 MutableHandleValue vp)
michael@0 491 {
michael@0 492 RootedId id(cx);
michael@0 493 if (!IndexToId(cx, index, &id))
michael@0 494 return false;
michael@0 495 return with_GetGeneric(cx, obj, receiver, id, vp);
michael@0 496 }
michael@0 497
michael@0 498 static bool
michael@0 499 with_SetGeneric(JSContext *cx, HandleObject obj, HandleId id,
michael@0 500 MutableHandleValue vp, bool strict)
michael@0 501 {
michael@0 502 RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
michael@0 503 return JSObject::setGeneric(cx, actual, actual, id, vp, strict);
michael@0 504 }
michael@0 505
michael@0 506 static bool
michael@0 507 with_SetProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
michael@0 508 MutableHandleValue vp, bool strict)
michael@0 509 {
michael@0 510 RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
michael@0 511 return JSObject::setProperty(cx, actual, actual, name, vp, strict);
michael@0 512 }
michael@0 513
michael@0 514 static bool
michael@0 515 with_SetElement(JSContext *cx, HandleObject obj, uint32_t index,
michael@0 516 MutableHandleValue vp, bool strict)
michael@0 517 {
michael@0 518 RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
michael@0 519 return JSObject::setElement(cx, actual, actual, index, vp, strict);
michael@0 520 }
michael@0 521
michael@0 522 static bool
michael@0 523 with_GetGenericAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
michael@0 524 {
michael@0 525 RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
michael@0 526 return JSObject::getGenericAttributes(cx, actual, id, attrsp);
michael@0 527 }
michael@0 528
michael@0 529 static bool
michael@0 530 with_SetGenericAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
michael@0 531 {
michael@0 532 RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
michael@0 533 return JSObject::setGenericAttributes(cx, actual, id, attrsp);
michael@0 534 }
michael@0 535
michael@0 536 static bool
michael@0 537 with_DeleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
michael@0 538 bool *succeeded)
michael@0 539 {
michael@0 540 RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
michael@0 541 return JSObject::deleteProperty(cx, actual, name, succeeded);
michael@0 542 }
michael@0 543
michael@0 544 static bool
michael@0 545 with_DeleteElement(JSContext *cx, HandleObject obj, uint32_t index,
michael@0 546 bool *succeeded)
michael@0 547 {
michael@0 548 RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
michael@0 549 return JSObject::deleteElement(cx, actual, index, succeeded);
michael@0 550 }
michael@0 551
michael@0 552 static JSObject *
michael@0 553 with_ThisObject(JSContext *cx, HandleObject obj)
michael@0 554 {
michael@0 555 return &obj->as<DynamicWithObject>().withThis();
michael@0 556 }
michael@0 557
michael@0 558 const Class StaticWithObject::class_ = {
michael@0 559 "WithTemplate",
michael@0 560 JSCLASS_IMPLEMENTS_BARRIERS |
michael@0 561 JSCLASS_HAS_RESERVED_SLOTS(StaticWithObject::RESERVED_SLOTS) |
michael@0 562 JSCLASS_IS_ANONYMOUS,
michael@0 563 JS_PropertyStub, /* addProperty */
michael@0 564 JS_DeletePropertyStub, /* delProperty */
michael@0 565 JS_PropertyStub, /* getProperty */
michael@0 566 JS_StrictPropertyStub, /* setProperty */
michael@0 567 JS_EnumerateStub,
michael@0 568 JS_ResolveStub,
michael@0 569 JS_ConvertStub
michael@0 570 };
michael@0 571
michael@0 572 const Class DynamicWithObject::class_ = {
michael@0 573 "With",
michael@0 574 JSCLASS_HAS_RESERVED_SLOTS(DynamicWithObject::RESERVED_SLOTS) |
michael@0 575 JSCLASS_IS_ANONYMOUS,
michael@0 576 JS_PropertyStub, /* addProperty */
michael@0 577 JS_DeletePropertyStub, /* delProperty */
michael@0 578 JS_PropertyStub, /* getProperty */
michael@0 579 JS_StrictPropertyStub, /* setProperty */
michael@0 580 JS_EnumerateStub,
michael@0 581 JS_ResolveStub,
michael@0 582 JS_ConvertStub,
michael@0 583 nullptr, /* finalize */
michael@0 584 nullptr, /* call */
michael@0 585 nullptr, /* hasInstance */
michael@0 586 nullptr, /* construct */
michael@0 587 nullptr, /* trace */
michael@0 588 JS_NULL_CLASS_SPEC,
michael@0 589 JS_NULL_CLASS_EXT,
michael@0 590 {
michael@0 591 with_LookupGeneric,
michael@0 592 with_LookupProperty,
michael@0 593 with_LookupElement,
michael@0 594 nullptr, /* defineGeneric */
michael@0 595 nullptr, /* defineProperty */
michael@0 596 nullptr, /* defineElement */
michael@0 597 with_GetGeneric,
michael@0 598 with_GetProperty,
michael@0 599 with_GetElement,
michael@0 600 with_SetGeneric,
michael@0 601 with_SetProperty,
michael@0 602 with_SetElement,
michael@0 603 with_GetGenericAttributes,
michael@0 604 with_SetGenericAttributes,
michael@0 605 with_DeleteProperty,
michael@0 606 with_DeleteElement,
michael@0 607 nullptr, nullptr, /* watch/unwatch */
michael@0 608 nullptr, /* slice */
michael@0 609 nullptr, /* enumerate (native enumeration of target doesn't work) */
michael@0 610 with_ThisObject,
michael@0 611 }
michael@0 612 };
michael@0 613
michael@0 614 /*****************************************************************************/
michael@0 615
michael@0 616 ClonedBlockObject *
michael@0 617 ClonedBlockObject::create(JSContext *cx, Handle<StaticBlockObject *> block, AbstractFramePtr frame)
michael@0 618 {
michael@0 619 assertSameCompartment(cx, frame);
michael@0 620 JS_ASSERT(block->getClass() == &BlockObject::class_);
michael@0 621
michael@0 622 RootedTypeObject type(cx, cx->getNewType(&BlockObject::class_, block.get()));
michael@0 623 if (!type)
michael@0 624 return nullptr;
michael@0 625
michael@0 626 RootedShape shape(cx, block->lastProperty());
michael@0 627
michael@0 628 RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, gc::TenuredHeap, shape, type));
michael@0 629 if (!obj)
michael@0 630 return nullptr;
michael@0 631
michael@0 632 /* Set the parent if necessary, as for call objects. */
michael@0 633 if (&frame.scopeChain()->global() != obj->getParent()) {
michael@0 634 JS_ASSERT(obj->getParent() == nullptr);
michael@0 635 Rooted<GlobalObject*> global(cx, &frame.scopeChain()->global());
michael@0 636 if (!JSObject::setParent(cx, obj, global))
michael@0 637 return nullptr;
michael@0 638 }
michael@0 639
michael@0 640 JS_ASSERT(!obj->inDictionaryMode());
michael@0 641 JS_ASSERT(obj->slotSpan() >= block->numVariables() + RESERVED_SLOTS);
michael@0 642
michael@0 643 obj->setReservedSlot(SCOPE_CHAIN_SLOT, ObjectValue(*frame.scopeChain()));
michael@0 644
michael@0 645 /*
michael@0 646 * Copy in the closed-over locals. Closed-over locals don't need
michael@0 647 * any fixup since the initial value is 'undefined'.
michael@0 648 */
michael@0 649 unsigned nvars = block->numVariables();
michael@0 650 for (unsigned i = 0; i < nvars; ++i) {
michael@0 651 if (block->isAliased(i)) {
michael@0 652 Value &val = frame.unaliasedLocal(block->blockIndexToLocalIndex(i));
michael@0 653 obj->as<ClonedBlockObject>().setVar(i, val);
michael@0 654 }
michael@0 655 }
michael@0 656
michael@0 657 JS_ASSERT(obj->isDelegate());
michael@0 658
michael@0 659 return &obj->as<ClonedBlockObject>();
michael@0 660 }
michael@0 661
michael@0 662 void
michael@0 663 ClonedBlockObject::copyUnaliasedValues(AbstractFramePtr frame)
michael@0 664 {
michael@0 665 StaticBlockObject &block = staticBlock();
michael@0 666 for (unsigned i = 0; i < numVariables(); ++i) {
michael@0 667 if (!block.isAliased(i)) {
michael@0 668 Value &val = frame.unaliasedLocal(block.blockIndexToLocalIndex(i));
michael@0 669 setVar(i, val, DONT_CHECK_ALIASING);
michael@0 670 }
michael@0 671 }
michael@0 672 }
michael@0 673
michael@0 674 StaticBlockObject *
michael@0 675 StaticBlockObject::create(ExclusiveContext *cx)
michael@0 676 {
michael@0 677 RootedTypeObject type(cx, cx->getNewType(&BlockObject::class_, nullptr));
michael@0 678 if (!type)
michael@0 679 return nullptr;
michael@0 680
michael@0 681 RootedShape emptyBlockShape(cx);
michael@0 682 emptyBlockShape = EmptyShape::getInitialShape(cx, &BlockObject::class_, nullptr, nullptr,
michael@0 683 nullptr, FINALIZE_KIND, BaseShape::DELEGATE);
michael@0 684 if (!emptyBlockShape)
michael@0 685 return nullptr;
michael@0 686
michael@0 687 JSObject *obj = JSObject::create(cx, FINALIZE_KIND, gc::TenuredHeap, emptyBlockShape, type);
michael@0 688 if (!obj)
michael@0 689 return nullptr;
michael@0 690
michael@0 691 return &obj->as<StaticBlockObject>();
michael@0 692 }
michael@0 693
michael@0 694 /* static */ Shape *
michael@0 695 StaticBlockObject::addVar(ExclusiveContext *cx, Handle<StaticBlockObject*> block, HandleId id,
michael@0 696 unsigned index, bool *redeclared)
michael@0 697 {
michael@0 698 JS_ASSERT(JSID_IS_ATOM(id));
michael@0 699 JS_ASSERT(index < LOCAL_INDEX_LIMIT);
michael@0 700
michael@0 701 *redeclared = false;
michael@0 702
michael@0 703 /* Inline JSObject::addProperty in order to trap the redefinition case. */
michael@0 704 Shape **spp;
michael@0 705 if (Shape::search(cx, block->lastProperty(), id, &spp, true)) {
michael@0 706 *redeclared = true;
michael@0 707 return nullptr;
michael@0 708 }
michael@0 709
michael@0 710 /*
michael@0 711 * Don't convert this object to dictionary mode so that we can clone the
michael@0 712 * block's shape later.
michael@0 713 */
michael@0 714 uint32_t slot = JSSLOT_FREE(&BlockObject::class_) + index;
michael@0 715 return JSObject::addPropertyInternal<SequentialExecution>(cx, block, id,
michael@0 716 /* getter = */ nullptr,
michael@0 717 /* setter = */ nullptr,
michael@0 718 slot,
michael@0 719 JSPROP_ENUMERATE | JSPROP_PERMANENT,
michael@0 720 /* attrs = */ 0,
michael@0 721 spp,
michael@0 722 /* allowDictionary = */ false);
michael@0 723 }
michael@0 724
michael@0 725 const Class BlockObject::class_ = {
michael@0 726 "Block",
michael@0 727 JSCLASS_IMPLEMENTS_BARRIERS |
michael@0 728 JSCLASS_HAS_RESERVED_SLOTS(BlockObject::RESERVED_SLOTS) |
michael@0 729 JSCLASS_IS_ANONYMOUS,
michael@0 730 JS_PropertyStub, /* addProperty */
michael@0 731 JS_DeletePropertyStub, /* delProperty */
michael@0 732 JS_PropertyStub, /* getProperty */
michael@0 733 JS_StrictPropertyStub, /* setProperty */
michael@0 734 JS_EnumerateStub,
michael@0 735 JS_ResolveStub,
michael@0 736 JS_ConvertStub
michael@0 737 };
michael@0 738
michael@0 739 template<XDRMode mode>
michael@0 740 bool
michael@0 741 js::XDRStaticBlockObject(XDRState<mode> *xdr, HandleObject enclosingScope,
michael@0 742 StaticBlockObject **objp)
michael@0 743 {
michael@0 744 /* NB: Keep this in sync with CloneStaticBlockObject. */
michael@0 745
michael@0 746 JSContext *cx = xdr->cx();
michael@0 747
michael@0 748 Rooted<StaticBlockObject*> obj(cx);
michael@0 749 uint32_t count = 0, offset = 0;
michael@0 750
michael@0 751 if (mode == XDR_ENCODE) {
michael@0 752 obj = *objp;
michael@0 753 count = obj->numVariables();
michael@0 754 offset = obj->localOffset();
michael@0 755 }
michael@0 756
michael@0 757 if (mode == XDR_DECODE) {
michael@0 758 obj = StaticBlockObject::create(cx);
michael@0 759 if (!obj)
michael@0 760 return false;
michael@0 761 obj->initEnclosingNestedScope(enclosingScope);
michael@0 762 *objp = obj;
michael@0 763 }
michael@0 764
michael@0 765 if (!xdr->codeUint32(&count))
michael@0 766 return false;
michael@0 767 if (!xdr->codeUint32(&offset))
michael@0 768 return false;
michael@0 769
michael@0 770 /*
michael@0 771 * XDR the block object's properties. We know that there are 'count'
michael@0 772 * properties to XDR, stored as id/aliased pairs. (The empty string as
michael@0 773 * id indicates an int id.)
michael@0 774 */
michael@0 775 if (mode == XDR_DECODE) {
michael@0 776 obj->setLocalOffset(offset);
michael@0 777
michael@0 778 for (unsigned i = 0; i < count; i++) {
michael@0 779 RootedAtom atom(cx);
michael@0 780 if (!XDRAtom(xdr, &atom))
michael@0 781 return false;
michael@0 782
michael@0 783 RootedId id(cx, atom != cx->runtime()->emptyString
michael@0 784 ? AtomToId(atom)
michael@0 785 : INT_TO_JSID(i));
michael@0 786
michael@0 787 bool redeclared;
michael@0 788 if (!StaticBlockObject::addVar(cx, obj, id, i, &redeclared)) {
michael@0 789 JS_ASSERT(!redeclared);
michael@0 790 return false;
michael@0 791 }
michael@0 792
michael@0 793 uint32_t aliased;
michael@0 794 if (!xdr->codeUint32(&aliased))
michael@0 795 return false;
michael@0 796
michael@0 797 JS_ASSERT(aliased == 0 || aliased == 1);
michael@0 798 obj->setAliased(i, !!aliased);
michael@0 799 }
michael@0 800 } else {
michael@0 801 AutoShapeVector shapes(cx);
michael@0 802 if (!shapes.growBy(count))
michael@0 803 return false;
michael@0 804
michael@0 805 for (Shape::Range<NoGC> r(obj->lastProperty()); !r.empty(); r.popFront())
michael@0 806 shapes[obj->shapeToIndex(r.front())] = &r.front();
michael@0 807
michael@0 808 RootedShape shape(cx);
michael@0 809 RootedId propid(cx);
michael@0 810 RootedAtom atom(cx);
michael@0 811 for (unsigned i = 0; i < count; i++) {
michael@0 812 shape = shapes[i];
michael@0 813 JS_ASSERT(shape->hasDefaultGetter());
michael@0 814 JS_ASSERT(obj->shapeToIndex(*shape) == i);
michael@0 815
michael@0 816 propid = shape->propid();
michael@0 817 JS_ASSERT(JSID_IS_ATOM(propid) || JSID_IS_INT(propid));
michael@0 818
michael@0 819 atom = JSID_IS_ATOM(propid)
michael@0 820 ? JSID_TO_ATOM(propid)
michael@0 821 : cx->runtime()->emptyString;
michael@0 822 if (!XDRAtom(xdr, &atom))
michael@0 823 return false;
michael@0 824
michael@0 825 uint32_t aliased = obj->isAliased(i);
michael@0 826 if (!xdr->codeUint32(&aliased))
michael@0 827 return false;
michael@0 828 }
michael@0 829 }
michael@0 830 return true;
michael@0 831 }
michael@0 832
michael@0 833 template bool
michael@0 834 js::XDRStaticBlockObject(XDRState<XDR_ENCODE> *, HandleObject, StaticBlockObject **);
michael@0 835
michael@0 836 template bool
michael@0 837 js::XDRStaticBlockObject(XDRState<XDR_DECODE> *, HandleObject, StaticBlockObject **);
michael@0 838
michael@0 839 static JSObject *
michael@0 840 CloneStaticBlockObject(JSContext *cx, HandleObject enclosingScope, Handle<StaticBlockObject*> srcBlock)
michael@0 841 {
michael@0 842 /* NB: Keep this in sync with XDRStaticBlockObject. */
michael@0 843
michael@0 844 Rooted<StaticBlockObject*> clone(cx, StaticBlockObject::create(cx));
michael@0 845 if (!clone)
michael@0 846 return nullptr;
michael@0 847
michael@0 848 clone->initEnclosingNestedScope(enclosingScope);
michael@0 849 clone->setLocalOffset(srcBlock->localOffset());
michael@0 850
michael@0 851 /* Shape::Range is reverse order, so build a list in forward order. */
michael@0 852 AutoShapeVector shapes(cx);
michael@0 853 if (!shapes.growBy(srcBlock->numVariables()))
michael@0 854 return nullptr;
michael@0 855
michael@0 856 for (Shape::Range<NoGC> r(srcBlock->lastProperty()); !r.empty(); r.popFront())
michael@0 857 shapes[srcBlock->shapeToIndex(r.front())] = &r.front();
michael@0 858
michael@0 859 for (Shape **p = shapes.begin(); p != shapes.end(); ++p) {
michael@0 860 RootedId id(cx, (*p)->propid());
michael@0 861 unsigned i = srcBlock->shapeToIndex(**p);
michael@0 862
michael@0 863 bool redeclared;
michael@0 864 if (!StaticBlockObject::addVar(cx, clone, id, i, &redeclared)) {
michael@0 865 JS_ASSERT(!redeclared);
michael@0 866 return nullptr;
michael@0 867 }
michael@0 868
michael@0 869 clone->setAliased(i, srcBlock->isAliased(i));
michael@0 870 }
michael@0 871
michael@0 872 return clone;
michael@0 873 }
michael@0 874
michael@0 875 JSObject *
michael@0 876 js::CloneNestedScopeObject(JSContext *cx, HandleObject enclosingScope, Handle<NestedScopeObject*> srcBlock)
michael@0 877 {
michael@0 878 if (srcBlock->is<StaticBlockObject>()) {
michael@0 879 Rooted<StaticBlockObject *> blockObj(cx, &srcBlock->as<StaticBlockObject>());
michael@0 880 return CloneStaticBlockObject(cx, enclosingScope, blockObj);
michael@0 881 } else {
michael@0 882 Rooted<StaticWithObject *> withObj(cx, &srcBlock->as<StaticWithObject>());
michael@0 883 return CloneStaticWithObject(cx, enclosingScope, withObj);
michael@0 884 }
michael@0 885 }
michael@0 886
michael@0 887 /*****************************************************************************/
michael@0 888
michael@0 889 // Any name atom for a function which will be added as a DeclEnv object to the
michael@0 890 // scope chain above call objects for fun.
michael@0 891 static inline JSAtom *
michael@0 892 CallObjectLambdaName(JSFunction &fun)
michael@0 893 {
michael@0 894 return fun.isNamedLambda() ? fun.atom() : nullptr;
michael@0 895 }
michael@0 896
michael@0 897 ScopeIter::ScopeIter(const ScopeIter &si, JSContext *cx
michael@0 898 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
michael@0 899 : cx(cx),
michael@0 900 frame_(si.frame_),
michael@0 901 cur_(cx, si.cur_),
michael@0 902 staticScope_(cx, si.staticScope_),
michael@0 903 type_(si.type_),
michael@0 904 hasScopeObject_(si.hasScopeObject_)
michael@0 905 {
michael@0 906 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
michael@0 907 }
michael@0 908
michael@0 909 ScopeIter::ScopeIter(JSObject &enclosingScope, JSContext *cx
michael@0 910 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
michael@0 911 : cx(cx),
michael@0 912 frame_(NullFramePtr()),
michael@0 913 cur_(cx, &enclosingScope),
michael@0 914 staticScope_(cx, nullptr),
michael@0 915 type_(Type(-1))
michael@0 916 {
michael@0 917 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
michael@0 918 }
michael@0 919
michael@0 920 ScopeIter::ScopeIter(AbstractFramePtr frame, jsbytecode *pc, JSContext *cx
michael@0 921 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
michael@0 922 : cx(cx),
michael@0 923 frame_(frame),
michael@0 924 cur_(cx, frame.scopeChain()),
michael@0 925 staticScope_(cx, frame.script()->getStaticScope(pc))
michael@0 926 {
michael@0 927 assertSameCompartment(cx, frame);
michael@0 928 settle();
michael@0 929 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
michael@0 930 }
michael@0 931
michael@0 932 ScopeIter::ScopeIter(const ScopeIterVal &val, JSContext *cx
michael@0 933 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
michael@0 934 : cx(cx),
michael@0 935 frame_(val.frame_),
michael@0 936 cur_(cx, val.cur_),
michael@0 937 staticScope_(cx, val.staticScope_),
michael@0 938 type_(val.type_),
michael@0 939 hasScopeObject_(val.hasScopeObject_)
michael@0 940 {
michael@0 941 assertSameCompartment(cx, val.frame_);
michael@0 942 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
michael@0 943 }
michael@0 944
michael@0 945 ScopeObject &
michael@0 946 ScopeIter::scope() const
michael@0 947 {
michael@0 948 JS_ASSERT(hasScopeObject());
michael@0 949 return cur_->as<ScopeObject>();
michael@0 950 }
michael@0 951
michael@0 952 ScopeIter &
michael@0 953 ScopeIter::operator++()
michael@0 954 {
michael@0 955 JS_ASSERT(!done());
michael@0 956 switch (type_) {
michael@0 957 case Call:
michael@0 958 if (hasScopeObject_) {
michael@0 959 cur_ = &cur_->as<CallObject>().enclosingScope();
michael@0 960 if (CallObjectLambdaName(*frame_.fun()))
michael@0 961 cur_ = &cur_->as<DeclEnvObject>().enclosingScope();
michael@0 962 }
michael@0 963 frame_ = NullFramePtr();
michael@0 964 break;
michael@0 965 case Block:
michael@0 966 JS_ASSERT(staticScope_ && staticScope_->is<StaticBlockObject>());
michael@0 967 staticScope_ = staticScope_->as<StaticBlockObject>().enclosingNestedScope();
michael@0 968 if (hasScopeObject_)
michael@0 969 cur_ = &cur_->as<ClonedBlockObject>().enclosingScope();
michael@0 970 settle();
michael@0 971 break;
michael@0 972 case With:
michael@0 973 JS_ASSERT(staticScope_ && staticScope_->is<StaticWithObject>());
michael@0 974 JS_ASSERT(hasScopeObject_);
michael@0 975 staticScope_ = staticScope_->as<StaticWithObject>().enclosingNestedScope();
michael@0 976 cur_ = &cur_->as<DynamicWithObject>().enclosingScope();
michael@0 977 settle();
michael@0 978 break;
michael@0 979 case StrictEvalScope:
michael@0 980 if (hasScopeObject_)
michael@0 981 cur_ = &cur_->as<CallObject>().enclosingScope();
michael@0 982 frame_ = NullFramePtr();
michael@0 983 break;
michael@0 984 }
michael@0 985 return *this;
michael@0 986 }
michael@0 987
michael@0 988 void
michael@0 989 ScopeIter::settle()
michael@0 990 {
michael@0 991 /*
michael@0 992 * Given an iterator state (cur_, staticScope_), figure out which (potentially
michael@0 993 * optimized) scope the iterator should report. Thus, the result is a pair
michael@0 994 * (type_, hasScopeObject_) where hasScopeObject_ indicates whether the
michael@0 995 * scope object has been optimized away and does not exist on the scope
michael@0 996 * chain. Beware: while ScopeIter iterates over the scopes of a single
michael@0 997 * frame, the scope chain (pointed to by cur_) continues into the scopes of
michael@0 998 * enclosing frames. Thus, it is important not to look at cur_ until it is
michael@0 999 * certain that cur_ points to a scope object in the current frame. In
michael@0 1000 * particular, there are three tricky corner cases:
michael@0 1001 * - non-heavyweight functions;
michael@0 1002 * - non-strict direct eval.
michael@0 1003 * - heavyweight functions observed before the prologue has finished;
michael@0 1004 * In all cases, cur_ can already be pointing into an enclosing frame's
michael@0 1005 * scope chain. Furthermore, in the first two cases: even if cur_ points
michael@0 1006 * into an enclosing frame's scope chain, the current frame may still have
michael@0 1007 * uncloned blocks. In the last case, since we haven't entered the
michael@0 1008 * function, we simply return a ScopeIter where done() == true.
michael@0 1009 *
michael@0 1010 * Note: DebugScopeObject falls nicely into this plan: since they are only
michael@0 1011 * ever introduced as the *enclosing* scope of a frame, they should never
michael@0 1012 * show up in scope iteration and fall into the final non-scope case.
michael@0 1013 */
michael@0 1014 if (frame_.isNonEvalFunctionFrame() && !frame_.fun()->isHeavyweight()) {
michael@0 1015 if (staticScope_) {
michael@0 1016 // If staticScope_ were a StaticWithObject, the function would be
michael@0 1017 // heavyweight.
michael@0 1018 JS_ASSERT(staticScope_->is<StaticBlockObject>());
michael@0 1019 type_ = Block;
michael@0 1020 hasScopeObject_ = staticScope_->as<StaticBlockObject>().needsClone();
michael@0 1021 } else {
michael@0 1022 type_ = Call;
michael@0 1023 hasScopeObject_ = false;
michael@0 1024 }
michael@0 1025 } else if (frame_.isNonStrictDirectEvalFrame() && cur_ == frame_.evalPrevScopeChain(cx)) {
michael@0 1026 if (staticScope_) {
michael@0 1027 JS_ASSERT(staticScope_->is<StaticBlockObject>());
michael@0 1028 JS_ASSERT(!staticScope_->as<StaticBlockObject>().needsClone());
michael@0 1029 type_ = Block;
michael@0 1030 hasScopeObject_ = false;
michael@0 1031 } else {
michael@0 1032 frame_ = NullFramePtr();
michael@0 1033 }
michael@0 1034 } else if (frame_.isNonEvalFunctionFrame() && !frame_.hasCallObj()) {
michael@0 1035 JS_ASSERT(cur_ == frame_.fun()->environment());
michael@0 1036 frame_ = NullFramePtr();
michael@0 1037 } else if (frame_.isStrictEvalFrame() && !frame_.hasCallObj()) {
michael@0 1038 JS_ASSERT(cur_ == frame_.evalPrevScopeChain(cx));
michael@0 1039 frame_ = NullFramePtr();
michael@0 1040 } else if (staticScope_) {
michael@0 1041 if (staticScope_->is<StaticWithObject>()) {
michael@0 1042 JS_ASSERT(cur_);
michael@0 1043 JS_ASSERT(cur_->as<DynamicWithObject>().staticScope() == staticScope_);
michael@0 1044 type_ = With;
michael@0 1045 hasScopeObject_ = true;
michael@0 1046 } else {
michael@0 1047 type_ = Block;
michael@0 1048 hasScopeObject_ = staticScope_->as<StaticBlockObject>().needsClone();
michael@0 1049 JS_ASSERT_IF(hasScopeObject_,
michael@0 1050 cur_->as<ClonedBlockObject>().staticBlock() == *staticScope_);
michael@0 1051 }
michael@0 1052 } else if (cur_->is<CallObject>()) {
michael@0 1053 CallObject &callobj = cur_->as<CallObject>();
michael@0 1054 type_ = callobj.isForEval() ? StrictEvalScope : Call;
michael@0 1055 hasScopeObject_ = true;
michael@0 1056 JS_ASSERT_IF(type_ == Call, callobj.callee().nonLazyScript() == frame_.script());
michael@0 1057 } else {
michael@0 1058 JS_ASSERT(!cur_->is<ScopeObject>());
michael@0 1059 JS_ASSERT(frame_.isGlobalFrame() || frame_.isDebuggerFrame());
michael@0 1060 frame_ = NullFramePtr();
michael@0 1061 }
michael@0 1062 }
michael@0 1063
michael@0 1064 /* static */ HashNumber
michael@0 1065 ScopeIterKey::hash(ScopeIterKey si)
michael@0 1066 {
michael@0 1067 /* hasScopeObject_ is determined by the other fields. */
michael@0 1068 return size_t(si.frame_.raw()) ^ size_t(si.cur_) ^ size_t(si.staticScope_) ^ si.type_;
michael@0 1069 }
michael@0 1070
michael@0 1071 /* static */ bool
michael@0 1072 ScopeIterKey::match(ScopeIterKey si1, ScopeIterKey si2)
michael@0 1073 {
michael@0 1074 /* hasScopeObject_ is determined by the other fields. */
michael@0 1075 return si1.frame_ == si2.frame_ &&
michael@0 1076 (!si1.frame_ ||
michael@0 1077 (si1.cur_ == si2.cur_ &&
michael@0 1078 si1.staticScope_ == si2.staticScope_ &&
michael@0 1079 si1.type_ == si2.type_));
michael@0 1080 }
michael@0 1081
michael@0 1082 // Live ScopeIter values may be added to DebugScopes::liveScopes, as
michael@0 1083 // ScopeIterVal instances. They need to have write barriers when they are added
michael@0 1084 // to the hash table, but no barriers when rehashing inside GC. It's a nasty
michael@0 1085 // hack, but the important thing is that ScopeIterKey and ScopeIterVal need to
michael@0 1086 // alias each other.
michael@0 1087 void ScopeIterVal::staticAsserts() {
michael@0 1088 static_assert(sizeof(ScopeIterVal) == sizeof(ScopeIterKey),
michael@0 1089 "ScopeIterVal must be same size of ScopeIterKey");
michael@0 1090 static_assert(offsetof(ScopeIterVal, cur_) == offsetof(ScopeIterKey, cur_),
michael@0 1091 "ScopeIterVal.cur_ must alias ScopeIterKey.cur_");
michael@0 1092 static_assert(offsetof(ScopeIterVal, staticScope_) == offsetof(ScopeIterKey, staticScope_),
michael@0 1093 "ScopeIterVal.staticScope_ must alias ScopeIterKey.staticScope_");
michael@0 1094 }
michael@0 1095
michael@0 1096 /*****************************************************************************/
michael@0 1097
michael@0 1098 namespace {
michael@0 1099
michael@0 1100 /*
michael@0 1101 * DebugScopeProxy is the handler for DebugScopeObject proxy objects. Having a
michael@0 1102 * custom handler (rather than trying to reuse js::Wrapper) gives us several
michael@0 1103 * important abilities:
michael@0 1104 * - We want to pass the ScopeObject as the receiver to forwarded scope
michael@0 1105 * property ops on aliased variables so that Call/Block/With ops do not all
michael@0 1106 * require a 'normalization' step.
michael@0 1107 * - The debug scope proxy can directly manipulate the stack frame to allow
michael@0 1108 * the debugger to read/write args/locals that were otherwise unaliased.
michael@0 1109 * - The debug scope proxy can store unaliased variables after the stack frame
michael@0 1110 * is popped so that they may still be read/written by the debugger.
michael@0 1111 * - The engine has made certain assumptions about the possible reads/writes
michael@0 1112 * in a scope. DebugScopeProxy allows us to prevent the debugger from
michael@0 1113 * breaking those assumptions.
michael@0 1114 * - The engine makes optimizations that are observable to the debugger. The
michael@0 1115 * proxy can either hide these optimizations or make the situation more
michael@0 1116 * clear to the debugger. An example is 'arguments'.
michael@0 1117 */
michael@0 1118 class DebugScopeProxy : public BaseProxyHandler
michael@0 1119 {
michael@0 1120 enum Action { SET, GET };
michael@0 1121
michael@0 1122 enum AccessResult {
michael@0 1123 ACCESS_UNALIASED,
michael@0 1124 ACCESS_GENERIC,
michael@0 1125 ACCESS_LOST
michael@0 1126 };
michael@0 1127
michael@0 1128 /*
michael@0 1129 * This function handles access to unaliased locals/formals. Since they are
michael@0 1130 * unaliased, the values of these variables are not stored in the slots of
michael@0 1131 * the normal Call/BlockObject scope objects and thus must be recovered
michael@0 1132 * from somewhere else:
michael@0 1133 * + if the invocation for which the scope was created is still executing,
michael@0 1134 * there is a JS frame live on the stack holding the values;
michael@0 1135 * + if the invocation for which the scope was created finished executing:
michael@0 1136 * - and there was a DebugScopeObject associated with scope, then the
michael@0 1137 * DebugScopes::onPop(Call|Block) handler copied out the unaliased
michael@0 1138 * variables:
michael@0 1139 * . for block scopes, the unaliased values were copied directly
michael@0 1140 * into the block object, since there is a slot allocated for every
michael@0 1141 * block binding, regardless of whether it is aliased;
michael@0 1142 * . for function scopes, a dense array is created in onPopCall to hold
michael@0 1143 * the unaliased values and attached to the DebugScopeObject;
michael@0 1144 * - and there was not a DebugScopeObject yet associated with the
michael@0 1145 * scope, then the unaliased values are lost and not recoverable.
michael@0 1146 *
michael@0 1147 * Callers should check accessResult for non-failure results:
michael@0 1148 * - ACCESS_UNALIASED if the access was unaliased and completed
michael@0 1149 * - ACCESS_GENERIC if the access was aliased or the property not found
michael@0 1150 * - ACCESS_LOST if the value has been lost to the debugger
michael@0 1151 */
michael@0 1152 bool handleUnaliasedAccess(JSContext *cx, Handle<DebugScopeObject*> debugScope,
michael@0 1153 Handle<ScopeObject*> scope, jsid id, Action action,
michael@0 1154 MutableHandleValue vp, AccessResult *accessResult)
michael@0 1155 {
michael@0 1156 JS_ASSERT(&debugScope->scope() == scope);
michael@0 1157 *accessResult = ACCESS_GENERIC;
michael@0 1158 ScopeIterVal *maybeLiveScope = DebugScopes::hasLiveScope(*scope);
michael@0 1159
michael@0 1160 /* Handle unaliased formals, vars, and consts at function scope. */
michael@0 1161 if (scope->is<CallObject>() && !scope->as<CallObject>().isForEval()) {
michael@0 1162 CallObject &callobj = scope->as<CallObject>();
michael@0 1163 RootedScript script(cx, callobj.callee().nonLazyScript());
michael@0 1164 if (!script->ensureHasTypes(cx) || !script->ensureHasAnalyzedArgsUsage(cx))
michael@0 1165 return false;
michael@0 1166
michael@0 1167 Bindings &bindings = script->bindings;
michael@0 1168 BindingIter bi(script);
michael@0 1169 while (bi && NameToId(bi->name()) != id)
michael@0 1170 bi++;
michael@0 1171 if (!bi)
michael@0 1172 return true;
michael@0 1173
michael@0 1174 if (bi->kind() == Binding::VARIABLE || bi->kind() == Binding::CONSTANT) {
michael@0 1175 uint32_t i = bi.frameIndex();
michael@0 1176 if (script->varIsAliased(i))
michael@0 1177 return true;
michael@0 1178
michael@0 1179 if (maybeLiveScope) {
michael@0 1180 AbstractFramePtr frame = maybeLiveScope->frame();
michael@0 1181 if (action == GET)
michael@0 1182 vp.set(frame.unaliasedVar(i));
michael@0 1183 else
michael@0 1184 frame.unaliasedVar(i) = vp;
michael@0 1185 } else if (JSObject *snapshot = debugScope->maybeSnapshot()) {
michael@0 1186 if (action == GET)
michael@0 1187 vp.set(snapshot->getDenseElement(bindings.numArgs() + i));
michael@0 1188 else
michael@0 1189 snapshot->setDenseElement(bindings.numArgs() + i, vp);
michael@0 1190 } else {
michael@0 1191 /* The unaliased value has been lost to the debugger. */
michael@0 1192 if (action == GET) {
michael@0 1193 *accessResult = ACCESS_LOST;
michael@0 1194 return true;
michael@0 1195 }
michael@0 1196 }
michael@0 1197 } else {
michael@0 1198 JS_ASSERT(bi->kind() == Binding::ARGUMENT);
michael@0 1199 unsigned i = bi.frameIndex();
michael@0 1200 if (script->formalIsAliased(i))
michael@0 1201 return true;
michael@0 1202
michael@0 1203 if (maybeLiveScope) {
michael@0 1204 AbstractFramePtr frame = maybeLiveScope->frame();
michael@0 1205 if (script->argsObjAliasesFormals() && frame.hasArgsObj()) {
michael@0 1206 if (action == GET)
michael@0 1207 vp.set(frame.argsObj().arg(i));
michael@0 1208 else
michael@0 1209 frame.argsObj().setArg(i, vp);
michael@0 1210 } else {
michael@0 1211 if (action == GET)
michael@0 1212 vp.set(frame.unaliasedFormal(i, DONT_CHECK_ALIASING));
michael@0 1213 else
michael@0 1214 frame.unaliasedFormal(i, DONT_CHECK_ALIASING) = vp;
michael@0 1215 }
michael@0 1216 } else if (JSObject *snapshot = debugScope->maybeSnapshot()) {
michael@0 1217 if (action == GET)
michael@0 1218 vp.set(snapshot->getDenseElement(i));
michael@0 1219 else
michael@0 1220 snapshot->setDenseElement(i, vp);
michael@0 1221 } else {
michael@0 1222 /* The unaliased value has been lost to the debugger. */
michael@0 1223 if (action == GET) {
michael@0 1224 *accessResult = ACCESS_LOST;
michael@0 1225 return true;
michael@0 1226 }
michael@0 1227 }
michael@0 1228
michael@0 1229 if (action == SET)
michael@0 1230 TypeScript::SetArgument(cx, script, i, vp);
michael@0 1231 }
michael@0 1232
michael@0 1233 *accessResult = ACCESS_UNALIASED;
michael@0 1234 return true;
michael@0 1235 }
michael@0 1236
michael@0 1237 /* Handle unaliased let and catch bindings at block scope. */
michael@0 1238 if (scope->is<ClonedBlockObject>()) {
michael@0 1239 Rooted<ClonedBlockObject *> block(cx, &scope->as<ClonedBlockObject>());
michael@0 1240 Shape *shape = block->lastProperty()->search(cx, id);
michael@0 1241 if (!shape)
michael@0 1242 return true;
michael@0 1243
michael@0 1244 unsigned i = block->staticBlock().shapeToIndex(*shape);
michael@0 1245 if (block->staticBlock().isAliased(i))
michael@0 1246 return true;
michael@0 1247
michael@0 1248 if (maybeLiveScope) {
michael@0 1249 AbstractFramePtr frame = maybeLiveScope->frame();
michael@0 1250 uint32_t local = block->staticBlock().blockIndexToLocalIndex(i);
michael@0 1251 JS_ASSERT(local < frame.script()->nfixed());
michael@0 1252 if (action == GET)
michael@0 1253 vp.set(frame.unaliasedLocal(local));
michael@0 1254 else
michael@0 1255 frame.unaliasedLocal(local) = vp;
michael@0 1256 } else {
michael@0 1257 if (action == GET)
michael@0 1258 vp.set(block->var(i, DONT_CHECK_ALIASING));
michael@0 1259 else
michael@0 1260 block->setVar(i, vp, DONT_CHECK_ALIASING);
michael@0 1261 }
michael@0 1262
michael@0 1263 *accessResult = ACCESS_UNALIASED;
michael@0 1264 return true;
michael@0 1265 }
michael@0 1266
michael@0 1267 /* The rest of the internal scopes do not have unaliased vars. */
michael@0 1268 JS_ASSERT(scope->is<DeclEnvObject>() || scope->is<DynamicWithObject>() ||
michael@0 1269 scope->as<CallObject>().isForEval());
michael@0 1270 return true;
michael@0 1271 }
michael@0 1272
michael@0 1273 static bool isArguments(JSContext *cx, jsid id)
michael@0 1274 {
michael@0 1275 return id == NameToId(cx->names().arguments);
michael@0 1276 }
michael@0 1277
michael@0 1278 static bool isFunctionScope(ScopeObject &scope)
michael@0 1279 {
michael@0 1280 return scope.is<CallObject>() && !scope.as<CallObject>().isForEval();
michael@0 1281 }
michael@0 1282
michael@0 1283 /*
michael@0 1284 * In theory, every function scope contains an 'arguments' bindings.
michael@0 1285 * However, the engine only adds a binding if 'arguments' is used in the
michael@0 1286 * function body. Thus, from the debugger's perspective, 'arguments' may be
michael@0 1287 * missing from the list of bindings.
michael@0 1288 */
michael@0 1289 static bool isMissingArgumentsBinding(ScopeObject &scope)
michael@0 1290 {
michael@0 1291 return isFunctionScope(scope) &&
michael@0 1292 !scope.as<CallObject>().callee().nonLazyScript()->argumentsHasVarBinding();
michael@0 1293 }
michael@0 1294
michael@0 1295 /*
michael@0 1296 * This function checks if an arguments object needs to be created when
michael@0 1297 * the debugger requests 'arguments' for a function scope where the
michael@0 1298 * arguments object has been optimized away (either because the binding is
michael@0 1299 * missing altogether or because !ScriptAnalysis::needsArgsObj).
michael@0 1300 */
michael@0 1301 static bool isMissingArguments(JSContext *cx, jsid id, ScopeObject &scope)
michael@0 1302 {
michael@0 1303 return isArguments(cx, id) && isFunctionScope(scope) &&
michael@0 1304 !scope.as<CallObject>().callee().nonLazyScript()->needsArgsObj();
michael@0 1305 }
michael@0 1306
michael@0 1307 /*
michael@0 1308 * Create a missing arguments object. If the function returns true but
michael@0 1309 * argsObj is null, it means the scope is dead.
michael@0 1310 */
michael@0 1311 static bool createMissingArguments(JSContext *cx, jsid id, ScopeObject &scope,
michael@0 1312 MutableHandleArgumentsObject argsObj)
michael@0 1313 {
michael@0 1314 MOZ_ASSERT(isMissingArguments(cx, id, scope));
michael@0 1315 argsObj.set(nullptr);
michael@0 1316
michael@0 1317 ScopeIterVal *maybeScope = DebugScopes::hasLiveScope(scope);
michael@0 1318 if (!maybeScope)
michael@0 1319 return true;
michael@0 1320
michael@0 1321 argsObj.set(ArgumentsObject::createUnexpected(cx, maybeScope->frame()));
michael@0 1322 return !!argsObj;
michael@0 1323 }
michael@0 1324
michael@0 1325 public:
michael@0 1326 static int family;
michael@0 1327 static DebugScopeProxy singleton;
michael@0 1328
michael@0 1329 DebugScopeProxy() : BaseProxyHandler(&family) {}
michael@0 1330
michael@0 1331 bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) MOZ_OVERRIDE
michael@0 1332 {
michael@0 1333 // always [[Extensible]], can't be made non-[[Extensible]], like most
michael@0 1334 // proxies
michael@0 1335 *extensible = true;
michael@0 1336 return true;
michael@0 1337 }
michael@0 1338
michael@0 1339 bool preventExtensions(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE
michael@0 1340 {
michael@0 1341 // See above.
michael@0 1342 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CHANGE_EXTENSIBILITY);
michael@0 1343 return false;
michael@0 1344 }
michael@0 1345
michael@0 1346 bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
michael@0 1347 MutableHandle<PropertyDescriptor> desc) MOZ_OVERRIDE
michael@0 1348 {
michael@0 1349 return getOwnPropertyDescriptor(cx, proxy, id, desc);
michael@0 1350 }
michael@0 1351
michael@0 1352 bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
michael@0 1353 MutableHandle<PropertyDescriptor> desc) MOZ_OVERRIDE
michael@0 1354 {
michael@0 1355 Rooted<DebugScopeObject*> debugScope(cx, &proxy->as<DebugScopeObject>());
michael@0 1356 Rooted<ScopeObject*> scope(cx, &debugScope->scope());
michael@0 1357
michael@0 1358 if (isMissingArguments(cx, id, *scope)) {
michael@0 1359 RootedArgumentsObject argsObj(cx);
michael@0 1360 if (!createMissingArguments(cx, id, *scope, &argsObj))
michael@0 1361 return false;
michael@0 1362
michael@0 1363 if (!argsObj) {
michael@0 1364 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_LIVE,
michael@0 1365 "Debugger scope");
michael@0 1366 return false;
michael@0 1367 }
michael@0 1368
michael@0 1369 desc.object().set(debugScope);
michael@0 1370 desc.setAttributes(JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT);
michael@0 1371 desc.value().setObject(*argsObj);
michael@0 1372 desc.setGetter(nullptr);
michael@0 1373 desc.setSetter(nullptr);
michael@0 1374 return true;
michael@0 1375 }
michael@0 1376
michael@0 1377 RootedValue v(cx);
michael@0 1378 AccessResult access;
michael@0 1379 if (!handleUnaliasedAccess(cx, debugScope, scope, id, GET, &v, &access))
michael@0 1380 return false;
michael@0 1381
michael@0 1382 switch (access) {
michael@0 1383 case ACCESS_UNALIASED:
michael@0 1384 desc.object().set(debugScope);
michael@0 1385 desc.setAttributes(JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT);
michael@0 1386 desc.value().set(v);
michael@0 1387 desc.setGetter(nullptr);
michael@0 1388 desc.setSetter(nullptr);
michael@0 1389 return true;
michael@0 1390 case ACCESS_GENERIC:
michael@0 1391 return JS_GetOwnPropertyDescriptorById(cx, scope, id, desc);
michael@0 1392 case ACCESS_LOST:
michael@0 1393 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_OPTIMIZED_OUT);
michael@0 1394 return false;
michael@0 1395 default:
michael@0 1396 MOZ_ASSUME_UNREACHABLE("bad AccessResult");
michael@0 1397 }
michael@0 1398 }
michael@0 1399
michael@0 1400 bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
michael@0 1401 MutableHandleValue vp) MOZ_OVERRIDE
michael@0 1402 {
michael@0 1403 Rooted<DebugScopeObject*> debugScope(cx, &proxy->as<DebugScopeObject>());
michael@0 1404 Rooted<ScopeObject*> scope(cx, &proxy->as<DebugScopeObject>().scope());
michael@0 1405
michael@0 1406 if (isMissingArguments(cx, id, *scope)) {
michael@0 1407 RootedArgumentsObject argsObj(cx);
michael@0 1408 if (!createMissingArguments(cx, id, *scope, &argsObj))
michael@0 1409 return false;
michael@0 1410
michael@0 1411 if (!argsObj) {
michael@0 1412 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_LIVE,
michael@0 1413 "Debugger scope");
michael@0 1414 return false;
michael@0 1415 }
michael@0 1416
michael@0 1417 vp.setObject(*argsObj);
michael@0 1418 return true;
michael@0 1419 }
michael@0 1420
michael@0 1421 AccessResult access;
michael@0 1422 if (!handleUnaliasedAccess(cx, debugScope, scope, id, GET, vp, &access))
michael@0 1423 return false;
michael@0 1424
michael@0 1425 switch (access) {
michael@0 1426 case ACCESS_UNALIASED:
michael@0 1427 return true;
michael@0 1428 case ACCESS_GENERIC:
michael@0 1429 return JSObject::getGeneric(cx, scope, scope, id, vp);
michael@0 1430 case ACCESS_LOST:
michael@0 1431 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_OPTIMIZED_OUT);
michael@0 1432 return false;
michael@0 1433 default:
michael@0 1434 MOZ_ASSUME_UNREACHABLE("bad AccessResult");
michael@0 1435 }
michael@0 1436 }
michael@0 1437
michael@0 1438 /*
michael@0 1439 * Like 'get', but returns sentinel values instead of throwing on
michael@0 1440 * exceptional cases.
michael@0 1441 */
michael@0 1442 bool getMaybeSentinelValue(JSContext *cx, Handle<DebugScopeObject *> debugScope, HandleId id,
michael@0 1443 MutableHandleValue vp)
michael@0 1444 {
michael@0 1445 Rooted<ScopeObject*> scope(cx, &debugScope->scope());
michael@0 1446
michael@0 1447 if (isMissingArguments(cx, id, *scope)) {
michael@0 1448 RootedArgumentsObject argsObj(cx);
michael@0 1449 if (!createMissingArguments(cx, id, *scope, &argsObj))
michael@0 1450 return false;
michael@0 1451 vp.set(argsObj ? ObjectValue(*argsObj) : MagicValue(JS_OPTIMIZED_ARGUMENTS));
michael@0 1452 return true;
michael@0 1453 }
michael@0 1454
michael@0 1455 AccessResult access;
michael@0 1456 if (!handleUnaliasedAccess(cx, debugScope, scope, id, GET, vp, &access))
michael@0 1457 return false;
michael@0 1458
michael@0 1459 switch (access) {
michael@0 1460 case ACCESS_UNALIASED:
michael@0 1461 return true;
michael@0 1462 case ACCESS_GENERIC:
michael@0 1463 return JSObject::getGeneric(cx, scope, scope, id, vp);
michael@0 1464 case ACCESS_LOST:
michael@0 1465 vp.setMagic(JS_OPTIMIZED_OUT);
michael@0 1466 return true;
michael@0 1467 default:
michael@0 1468 MOZ_ASSUME_UNREACHABLE("bad AccessResult");
michael@0 1469 }
michael@0 1470 }
michael@0 1471
michael@0 1472 bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, bool strict,
michael@0 1473 MutableHandleValue vp) MOZ_OVERRIDE
michael@0 1474 {
michael@0 1475 Rooted<DebugScopeObject*> debugScope(cx, &proxy->as<DebugScopeObject>());
michael@0 1476 Rooted<ScopeObject*> scope(cx, &proxy->as<DebugScopeObject>().scope());
michael@0 1477
michael@0 1478 AccessResult access;
michael@0 1479 if (!handleUnaliasedAccess(cx, debugScope, scope, id, SET, vp, &access))
michael@0 1480 return false;
michael@0 1481
michael@0 1482 switch (access) {
michael@0 1483 case ACCESS_UNALIASED:
michael@0 1484 return true;
michael@0 1485 case ACCESS_GENERIC:
michael@0 1486 return JSObject::setGeneric(cx, scope, scope, id, vp, strict);
michael@0 1487 default:
michael@0 1488 MOZ_ASSUME_UNREACHABLE("bad AccessResult");
michael@0 1489 }
michael@0 1490 }
michael@0 1491
michael@0 1492 bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
michael@0 1493 MutableHandle<PropertyDescriptor> desc) MOZ_OVERRIDE
michael@0 1494 {
michael@0 1495 Rooted<ScopeObject*> scope(cx, &proxy->as<DebugScopeObject>().scope());
michael@0 1496
michael@0 1497 bool found;
michael@0 1498 if (!has(cx, proxy, id, &found))
michael@0 1499 return false;
michael@0 1500 if (found)
michael@0 1501 return Throw(cx, id, JSMSG_CANT_REDEFINE_PROP);
michael@0 1502
michael@0 1503 return JS_DefinePropertyById(cx, scope, id, desc.value(), desc.getter(), desc.setter(),
michael@0 1504 desc.attributes());
michael@0 1505 }
michael@0 1506
michael@0 1507 bool getScopePropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props,
michael@0 1508 unsigned flags)
michael@0 1509 {
michael@0 1510 Rooted<ScopeObject*> scope(cx, &proxy->as<DebugScopeObject>().scope());
michael@0 1511
michael@0 1512 if (isMissingArgumentsBinding(*scope)) {
michael@0 1513 if (!props.append(NameToId(cx->names().arguments)))
michael@0 1514 return false;
michael@0 1515 }
michael@0 1516
michael@0 1517 // DynamicWithObject isn't a very good proxy. It doesn't have a
michael@0 1518 // JSNewEnumerateOp implementation, because if it just delegated to the
michael@0 1519 // target object, the object would indicate that native enumeration is
michael@0 1520 // the thing to do, but native enumeration over the DynamicWithObject
michael@0 1521 // wrapper yields no properties. So instead here we hack around the
michael@0 1522 // issue, and punch a hole through to the with object target.
michael@0 1523 Rooted<JSObject*> target(cx, (scope->is<DynamicWithObject>()
michael@0 1524 ? &scope->as<DynamicWithObject>().object() : scope));
michael@0 1525 if (!GetPropertyNames(cx, target, flags, &props))
michael@0 1526 return false;
michael@0 1527
michael@0 1528 /*
michael@0 1529 * Function scopes are optimized to not contain unaliased variables so
michael@0 1530 * they must be manually appended here.
michael@0 1531 */
michael@0 1532 if (scope->is<CallObject>() && !scope->as<CallObject>().isForEval()) {
michael@0 1533 RootedScript script(cx, scope->as<CallObject>().callee().nonLazyScript());
michael@0 1534 for (BindingIter bi(script); bi; bi++) {
michael@0 1535 if (!bi->aliased() && !props.append(NameToId(bi->name())))
michael@0 1536 return false;
michael@0 1537 }
michael@0 1538 }
michael@0 1539
michael@0 1540 return true;
michael@0 1541 }
michael@0 1542
michael@0 1543 bool getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props) MOZ_OVERRIDE
michael@0 1544 {
michael@0 1545 return getScopePropertyNames(cx, proxy, props, JSITER_OWNONLY);
michael@0 1546 }
michael@0 1547
michael@0 1548 bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) MOZ_OVERRIDE
michael@0 1549 {
michael@0 1550 return getScopePropertyNames(cx, proxy, props, 0);
michael@0 1551 }
michael@0 1552
michael@0 1553 bool has(JSContext *cx, HandleObject proxy, HandleId id_, bool *bp) MOZ_OVERRIDE
michael@0 1554 {
michael@0 1555 RootedId id(cx, id_);
michael@0 1556 ScopeObject &scopeObj = proxy->as<DebugScopeObject>().scope();
michael@0 1557
michael@0 1558 if (isArguments(cx, id) && isFunctionScope(scopeObj)) {
michael@0 1559 *bp = true;
michael@0 1560 return true;
michael@0 1561 }
michael@0 1562
michael@0 1563 bool found;
michael@0 1564 RootedObject scope(cx, &scopeObj);
michael@0 1565 if (!JS_HasPropertyById(cx, scope, id, &found))
michael@0 1566 return false;
michael@0 1567
michael@0 1568 /*
michael@0 1569 * Function scopes are optimized to not contain unaliased variables so
michael@0 1570 * a manual search is necessary.
michael@0 1571 */
michael@0 1572 if (!found && scope->is<CallObject>() && !scope->as<CallObject>().isForEval()) {
michael@0 1573 RootedScript script(cx, scope->as<CallObject>().callee().nonLazyScript());
michael@0 1574 for (BindingIter bi(script); bi; bi++) {
michael@0 1575 if (!bi->aliased() && NameToId(bi->name()) == id) {
michael@0 1576 found = true;
michael@0 1577 break;
michael@0 1578 }
michael@0 1579 }
michael@0 1580 }
michael@0 1581
michael@0 1582 *bp = found;
michael@0 1583 return true;
michael@0 1584 }
michael@0 1585
michael@0 1586 bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE
michael@0 1587 {
michael@0 1588 RootedValue idval(cx, IdToValue(id));
michael@0 1589 return js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_CANT_DELETE,
michael@0 1590 JSDVG_IGNORE_STACK, idval, NullPtr(), nullptr, nullptr);
michael@0 1591 }
michael@0 1592 };
michael@0 1593
michael@0 1594 } /* anonymous namespace */
michael@0 1595
michael@0 1596 int DebugScopeProxy::family = 0;
michael@0 1597 DebugScopeProxy DebugScopeProxy::singleton;
michael@0 1598
michael@0 1599 /* static */ DebugScopeObject *
michael@0 1600 DebugScopeObject::create(JSContext *cx, ScopeObject &scope, HandleObject enclosing)
michael@0 1601 {
michael@0 1602 JS_ASSERT(scope.compartment() == cx->compartment());
michael@0 1603 RootedValue priv(cx, ObjectValue(scope));
michael@0 1604 JSObject *obj = NewProxyObject(cx, &DebugScopeProxy::singleton, priv,
michael@0 1605 nullptr /* proto */, &scope.global());
michael@0 1606 if (!obj)
michael@0 1607 return nullptr;
michael@0 1608
michael@0 1609 JS_ASSERT(!enclosing->is<ScopeObject>());
michael@0 1610
michael@0 1611 DebugScopeObject *debugScope = &obj->as<DebugScopeObject>();
michael@0 1612 debugScope->setExtra(ENCLOSING_EXTRA, ObjectValue(*enclosing));
michael@0 1613 debugScope->setExtra(SNAPSHOT_EXTRA, NullValue());
michael@0 1614
michael@0 1615 return debugScope;
michael@0 1616 }
michael@0 1617
michael@0 1618 ScopeObject &
michael@0 1619 DebugScopeObject::scope() const
michael@0 1620 {
michael@0 1621 return target()->as<ScopeObject>();
michael@0 1622 }
michael@0 1623
michael@0 1624 JSObject &
michael@0 1625 DebugScopeObject::enclosingScope() const
michael@0 1626 {
michael@0 1627 return extra(ENCLOSING_EXTRA).toObject();
michael@0 1628 }
michael@0 1629
michael@0 1630 JSObject *
michael@0 1631 DebugScopeObject::maybeSnapshot() const
michael@0 1632 {
michael@0 1633 JS_ASSERT(!scope().as<CallObject>().isForEval());
michael@0 1634 return extra(SNAPSHOT_EXTRA).toObjectOrNull();
michael@0 1635 }
michael@0 1636
michael@0 1637 void
michael@0 1638 DebugScopeObject::initSnapshot(JSObject &o)
michael@0 1639 {
michael@0 1640 JS_ASSERT(maybeSnapshot() == nullptr);
michael@0 1641 setExtra(SNAPSHOT_EXTRA, ObjectValue(o));
michael@0 1642 }
michael@0 1643
michael@0 1644 bool
michael@0 1645 DebugScopeObject::isForDeclarative() const
michael@0 1646 {
michael@0 1647 ScopeObject &s = scope();
michael@0 1648 return s.is<CallObject>() || s.is<BlockObject>() || s.is<DeclEnvObject>();
michael@0 1649 }
michael@0 1650
michael@0 1651 bool
michael@0 1652 DebugScopeObject::getMaybeSentinelValue(JSContext *cx, HandleId id, MutableHandleValue vp)
michael@0 1653 {
michael@0 1654 Rooted<DebugScopeObject *> self(cx, this);
michael@0 1655 return DebugScopeProxy::singleton.getMaybeSentinelValue(cx, self, id, vp);
michael@0 1656 }
michael@0 1657
michael@0 1658 bool
michael@0 1659 js_IsDebugScopeSlow(ProxyObject *proxy)
michael@0 1660 {
michael@0 1661 JS_ASSERT(proxy->hasClass(&ProxyObject::uncallableClass_));
michael@0 1662 return proxy->handler() == &DebugScopeProxy::singleton;
michael@0 1663 }
michael@0 1664
michael@0 1665 /*****************************************************************************/
michael@0 1666
michael@0 1667 /* static */ MOZ_ALWAYS_INLINE void
michael@0 1668 DebugScopes::proxiedScopesPostWriteBarrier(JSRuntime *rt, ObjectWeakMap *map,
michael@0 1669 const EncapsulatedPtr<JSObject> &key)
michael@0 1670 {
michael@0 1671 #ifdef JSGC_GENERATIONAL
michael@0 1672 /*
michael@0 1673 * Strip the barriers from the type before inserting into the store buffer.
michael@0 1674 * This will automatically ensure that barriers do not fire during GC.
michael@0 1675 *
michael@0 1676 * Some compilers complain about instantiating the WeakMap class for
michael@0 1677 * unbarriered type arguments, so we cast to a HashMap instead. Because of
michael@0 1678 * WeakMap's multiple inheritace, We need to do this in two stages, first to
michael@0 1679 * the HashMap base class and then to the unbarriered version.
michael@0 1680 */
michael@0 1681 ObjectWeakMap::Base *baseHashMap = static_cast<ObjectWeakMap::Base *>(map);
michael@0 1682
michael@0 1683 typedef HashMap<JSObject *, JSObject *> UnbarrieredMap;
michael@0 1684 UnbarrieredMap *unbarrieredMap = reinterpret_cast<UnbarrieredMap *>(baseHashMap);
michael@0 1685
michael@0 1686 typedef gc::HashKeyRef<UnbarrieredMap, JSObject *> Ref;
michael@0 1687 if (key && IsInsideNursery(rt, key))
michael@0 1688 rt->gcStoreBuffer.putGeneric(Ref(unbarrieredMap, key.get()));
michael@0 1689 #endif
michael@0 1690 }
michael@0 1691
michael@0 1692 #ifdef JSGC_GENERATIONAL
michael@0 1693 class DebugScopes::MissingScopesRef : public gc::BufferableRef
michael@0 1694 {
michael@0 1695 MissingScopeMap *map;
michael@0 1696 ScopeIterKey key;
michael@0 1697
michael@0 1698 public:
michael@0 1699 MissingScopesRef(MissingScopeMap *m, const ScopeIterKey &k) : map(m), key(k) {}
michael@0 1700
michael@0 1701 void mark(JSTracer *trc) {
michael@0 1702 ScopeIterKey prior = key;
michael@0 1703 MissingScopeMap::Ptr p = map->lookup(key);
michael@0 1704 if (!p)
michael@0 1705 return;
michael@0 1706 trc->setTracingLocation(&const_cast<ScopeIterKey &>(p->key()).enclosingScope());
michael@0 1707 Mark(trc, &key.enclosingScope(), "MissingScopesRef");
michael@0 1708 map->rekeyIfMoved(prior, key);
michael@0 1709 }
michael@0 1710 };
michael@0 1711 #endif
michael@0 1712
michael@0 1713 /* static */ MOZ_ALWAYS_INLINE void
michael@0 1714 DebugScopes::missingScopesPostWriteBarrier(JSRuntime *rt, MissingScopeMap *map,
michael@0 1715 const ScopeIterKey &key)
michael@0 1716 {
michael@0 1717 #ifdef JSGC_GENERATIONAL
michael@0 1718 if (key.enclosingScope() && IsInsideNursery(rt, key.enclosingScope()))
michael@0 1719 rt->gcStoreBuffer.putGeneric(MissingScopesRef(map, key));
michael@0 1720 #endif
michael@0 1721 }
michael@0 1722
michael@0 1723 /* static */ MOZ_ALWAYS_INLINE void
michael@0 1724 DebugScopes::liveScopesPostWriteBarrier(JSRuntime *rt, LiveScopeMap *map, ScopeObject *key)
michael@0 1725 {
michael@0 1726 #ifdef JSGC_GENERATIONAL
michael@0 1727 // As above. Otherwise, barriers could fire during GC when moving the
michael@0 1728 // value.
michael@0 1729 typedef HashMap<ScopeObject *,
michael@0 1730 ScopeIterKey,
michael@0 1731 DefaultHasher<ScopeObject *>,
michael@0 1732 RuntimeAllocPolicy> UnbarrieredLiveScopeMap;
michael@0 1733 typedef gc::HashKeyRef<UnbarrieredLiveScopeMap, ScopeObject *> Ref;
michael@0 1734 if (key && IsInsideNursery(rt, key))
michael@0 1735 rt->gcStoreBuffer.putGeneric(Ref(reinterpret_cast<UnbarrieredLiveScopeMap *>(map), key));
michael@0 1736 #endif
michael@0 1737 }
michael@0 1738
michael@0 1739 DebugScopes::DebugScopes(JSContext *cx)
michael@0 1740 : proxiedScopes(cx),
michael@0 1741 missingScopes(cx->runtime()),
michael@0 1742 liveScopes(cx->runtime())
michael@0 1743 {}
michael@0 1744
michael@0 1745 DebugScopes::~DebugScopes()
michael@0 1746 {
michael@0 1747 JS_ASSERT(missingScopes.empty());
michael@0 1748 WeakMapBase::removeWeakMapFromList(&proxiedScopes);
michael@0 1749 }
michael@0 1750
michael@0 1751 bool
michael@0 1752 DebugScopes::init()
michael@0 1753 {
michael@0 1754 if (!liveScopes.init() ||
michael@0 1755 !proxiedScopes.init() ||
michael@0 1756 !missingScopes.init())
michael@0 1757 {
michael@0 1758 return false;
michael@0 1759 }
michael@0 1760 return true;
michael@0 1761 }
michael@0 1762
michael@0 1763 void
michael@0 1764 DebugScopes::mark(JSTracer *trc)
michael@0 1765 {
michael@0 1766 proxiedScopes.trace(trc);
michael@0 1767 }
michael@0 1768
michael@0 1769 void
michael@0 1770 DebugScopes::sweep(JSRuntime *rt)
michael@0 1771 {
michael@0 1772 /*
michael@0 1773 * missingScopes points to debug scopes weakly so that debug scopes can be
michael@0 1774 * released more eagerly.
michael@0 1775 */
michael@0 1776 for (MissingScopeMap::Enum e(missingScopes); !e.empty(); e.popFront()) {
michael@0 1777 DebugScopeObject **debugScope = e.front().value().unsafeGet();
michael@0 1778 if (IsObjectAboutToBeFinalized(debugScope)) {
michael@0 1779 /*
michael@0 1780 * Note that onPopCall and onPopBlock rely on missingScopes to find
michael@0 1781 * scope objects that we synthesized for the debugger's sake, and
michael@0 1782 * clean up the synthetic scope objects' entries in liveScopes. So
michael@0 1783 * if we remove an entry frcom missingScopes here, we must also
michael@0 1784 * remove the corresponding liveScopes entry.
michael@0 1785 *
michael@0 1786 * Since the DebugScopeObject is the only thing using its scope
michael@0 1787 * object, and the DSO is about to be finalized, you might assume
michael@0 1788 * that the synthetic SO is also about to be finalized too, and thus
michael@0 1789 * the loop below will take care of things. But complex GC behavior
michael@0 1790 * means that marks are only conservative approximations of
michael@0 1791 * liveness; we should assume that anything could be marked.
michael@0 1792 *
michael@0 1793 * Thus, we must explicitly remove the entries from both liveScopes
michael@0 1794 * and missingScopes here.
michael@0 1795 */
michael@0 1796 liveScopes.remove(&(*debugScope)->scope());
michael@0 1797 e.removeFront();
michael@0 1798 }
michael@0 1799 }
michael@0 1800
michael@0 1801 for (LiveScopeMap::Enum e(liveScopes); !e.empty(); e.popFront()) {
michael@0 1802 ScopeObject *scope = e.front().key();
michael@0 1803
michael@0 1804 /*
michael@0 1805 * Scopes can be finalized when a debugger-synthesized ScopeObject is
michael@0 1806 * no longer reachable via its DebugScopeObject.
michael@0 1807 */
michael@0 1808 if (IsObjectAboutToBeFinalized(&scope)) {
michael@0 1809 e.removeFront();
michael@0 1810 continue;
michael@0 1811 }
michael@0 1812 }
michael@0 1813 }
michael@0 1814
michael@0 1815 #if defined(JSGC_GENERATIONAL) && defined(JS_GC_ZEAL)
michael@0 1816 void
michael@0 1817 DebugScopes::checkHashTablesAfterMovingGC(JSRuntime *runtime)
michael@0 1818 {
michael@0 1819 /*
michael@0 1820 * This is called at the end of StoreBuffer::mark() to check that our
michael@0 1821 * postbarriers have worked and that no hashtable keys (or values) are left
michael@0 1822 * pointing into the nursery.
michael@0 1823 */
michael@0 1824 JS::shadow::Runtime *rt = JS::shadow::Runtime::asShadowRuntime(runtime);
michael@0 1825 for (ObjectWeakMap::Range r = proxiedScopes.all(); !r.empty(); r.popFront()) {
michael@0 1826 JS_ASSERT(!IsInsideNursery(rt, r.front().key().get()));
michael@0 1827 JS_ASSERT(!IsInsideNursery(rt, r.front().value().get()));
michael@0 1828 }
michael@0 1829 for (MissingScopeMap::Range r = missingScopes.all(); !r.empty(); r.popFront()) {
michael@0 1830 JS_ASSERT(!IsInsideNursery(rt, r.front().key().cur()));
michael@0 1831 JS_ASSERT(!IsInsideNursery(rt, r.front().key().staticScope()));
michael@0 1832 JS_ASSERT(!IsInsideNursery(rt, r.front().value().get()));
michael@0 1833 }
michael@0 1834 for (LiveScopeMap::Range r = liveScopes.all(); !r.empty(); r.popFront()) {
michael@0 1835 JS_ASSERT(!IsInsideNursery(rt, r.front().key()));
michael@0 1836 JS_ASSERT(!IsInsideNursery(rt, r.front().value().cur_.get()));
michael@0 1837 JS_ASSERT(!IsInsideNursery(rt, r.front().value().staticScope_.get()));
michael@0 1838 }
michael@0 1839 }
michael@0 1840 #endif
michael@0 1841
michael@0 1842 /*
michael@0 1843 * Unfortunately, GetDebugScopeForFrame needs to work even outside debug mode
michael@0 1844 * (in particular, JS_GetFrameScopeChain does not require debug mode). Since
michael@0 1845 * DebugScopes::onPop* are only called in debug mode, this means we cannot
michael@0 1846 * use any of the maps in DebugScopes. This will produce debug scope chains
michael@0 1847 * that do not obey the debugger invariants but that is just fine.
michael@0 1848 */
michael@0 1849 static bool
michael@0 1850 CanUseDebugScopeMaps(JSContext *cx)
michael@0 1851 {
michael@0 1852 return cx->compartment()->debugMode();
michael@0 1853 }
michael@0 1854
michael@0 1855 DebugScopes *
michael@0 1856 DebugScopes::ensureCompartmentData(JSContext *cx)
michael@0 1857 {
michael@0 1858 JSCompartment *c = cx->compartment();
michael@0 1859 if (c->debugScopes)
michael@0 1860 return c->debugScopes;
michael@0 1861
michael@0 1862 c->debugScopes = cx->runtime()->new_<DebugScopes>(cx);
michael@0 1863 if (c->debugScopes && c->debugScopes->init())
michael@0 1864 return c->debugScopes;
michael@0 1865
michael@0 1866 js_ReportOutOfMemory(cx);
michael@0 1867 return nullptr;
michael@0 1868 }
michael@0 1869
michael@0 1870 DebugScopeObject *
michael@0 1871 DebugScopes::hasDebugScope(JSContext *cx, ScopeObject &scope)
michael@0 1872 {
michael@0 1873 DebugScopes *scopes = scope.compartment()->debugScopes;
michael@0 1874 if (!scopes)
michael@0 1875 return nullptr;
michael@0 1876
michael@0 1877 if (ObjectWeakMap::Ptr p = scopes->proxiedScopes.lookup(&scope)) {
michael@0 1878 JS_ASSERT(CanUseDebugScopeMaps(cx));
michael@0 1879 return &p->value()->as<DebugScopeObject>();
michael@0 1880 }
michael@0 1881
michael@0 1882 return nullptr;
michael@0 1883 }
michael@0 1884
michael@0 1885 bool
michael@0 1886 DebugScopes::addDebugScope(JSContext *cx, ScopeObject &scope, DebugScopeObject &debugScope)
michael@0 1887 {
michael@0 1888 JS_ASSERT(cx->compartment() == scope.compartment());
michael@0 1889 JS_ASSERT(cx->compartment() == debugScope.compartment());
michael@0 1890
michael@0 1891 if (!CanUseDebugScopeMaps(cx))
michael@0 1892 return true;
michael@0 1893
michael@0 1894 DebugScopes *scopes = ensureCompartmentData(cx);
michael@0 1895 if (!scopes)
michael@0 1896 return false;
michael@0 1897
michael@0 1898 JS_ASSERT(!scopes->proxiedScopes.has(&scope));
michael@0 1899 if (!scopes->proxiedScopes.put(&scope, &debugScope)) {
michael@0 1900 js_ReportOutOfMemory(cx);
michael@0 1901 return false;
michael@0 1902 }
michael@0 1903
michael@0 1904 proxiedScopesPostWriteBarrier(cx->runtime(), &scopes->proxiedScopes, &scope);
michael@0 1905 return true;
michael@0 1906 }
michael@0 1907
michael@0 1908 DebugScopeObject *
michael@0 1909 DebugScopes::hasDebugScope(JSContext *cx, const ScopeIter &si)
michael@0 1910 {
michael@0 1911 JS_ASSERT(!si.hasScopeObject());
michael@0 1912
michael@0 1913 DebugScopes *scopes = cx->compartment()->debugScopes;
michael@0 1914 if (!scopes)
michael@0 1915 return nullptr;
michael@0 1916
michael@0 1917 if (MissingScopeMap::Ptr p = scopes->missingScopes.lookup(si)) {
michael@0 1918 JS_ASSERT(CanUseDebugScopeMaps(cx));
michael@0 1919 return p->value();
michael@0 1920 }
michael@0 1921 return nullptr;
michael@0 1922 }
michael@0 1923
michael@0 1924 bool
michael@0 1925 DebugScopes::addDebugScope(JSContext *cx, const ScopeIter &si, DebugScopeObject &debugScope)
michael@0 1926 {
michael@0 1927 JS_ASSERT(!si.hasScopeObject());
michael@0 1928 JS_ASSERT(cx->compartment() == debugScope.compartment());
michael@0 1929 JS_ASSERT_IF(si.frame().isFunctionFrame(), !si.frame().callee()->isGenerator());
michael@0 1930
michael@0 1931 if (!CanUseDebugScopeMaps(cx))
michael@0 1932 return true;
michael@0 1933
michael@0 1934 DebugScopes *scopes = ensureCompartmentData(cx);
michael@0 1935 if (!scopes)
michael@0 1936 return false;
michael@0 1937
michael@0 1938 JS_ASSERT(!scopes->missingScopes.has(si));
michael@0 1939 if (!scopes->missingScopes.put(si, &debugScope)) {
michael@0 1940 js_ReportOutOfMemory(cx);
michael@0 1941 return false;
michael@0 1942 }
michael@0 1943 missingScopesPostWriteBarrier(cx->runtime(), &scopes->missingScopes, si);
michael@0 1944
michael@0 1945 JS_ASSERT(!scopes->liveScopes.has(&debugScope.scope()));
michael@0 1946 if (!scopes->liveScopes.put(&debugScope.scope(), si)) {
michael@0 1947 js_ReportOutOfMemory(cx);
michael@0 1948 return false;
michael@0 1949 }
michael@0 1950 liveScopesPostWriteBarrier(cx->runtime(), &scopes->liveScopes, &debugScope.scope());
michael@0 1951
michael@0 1952 return true;
michael@0 1953 }
michael@0 1954
michael@0 1955 void
michael@0 1956 DebugScopes::onPopCall(AbstractFramePtr frame, JSContext *cx)
michael@0 1957 {
michael@0 1958 JS_ASSERT(!frame.isYielding());
michael@0 1959 assertSameCompartment(cx, frame);
michael@0 1960
michael@0 1961 DebugScopes *scopes = cx->compartment()->debugScopes;
michael@0 1962 if (!scopes)
michael@0 1963 return;
michael@0 1964
michael@0 1965 Rooted<DebugScopeObject*> debugScope(cx, nullptr);
michael@0 1966
michael@0 1967 if (frame.fun()->isHeavyweight()) {
michael@0 1968 /*
michael@0 1969 * The frame may be observed before the prologue has created the
michael@0 1970 * CallObject. See ScopeIter::settle.
michael@0 1971 */
michael@0 1972 if (!frame.hasCallObj())
michael@0 1973 return;
michael@0 1974
michael@0 1975 CallObject &callobj = frame.scopeChain()->as<CallObject>();
michael@0 1976 scopes->liveScopes.remove(&callobj);
michael@0 1977 if (ObjectWeakMap::Ptr p = scopes->proxiedScopes.lookup(&callobj))
michael@0 1978 debugScope = &p->value()->as<DebugScopeObject>();
michael@0 1979 } else {
michael@0 1980 ScopeIter si(frame, frame.script()->main(), cx);
michael@0 1981 if (MissingScopeMap::Ptr p = scopes->missingScopes.lookup(si)) {
michael@0 1982 debugScope = p->value();
michael@0 1983 scopes->liveScopes.remove(&debugScope->scope().as<CallObject>());
michael@0 1984 scopes->missingScopes.remove(p);
michael@0 1985 }
michael@0 1986 }
michael@0 1987
michael@0 1988 /*
michael@0 1989 * When the JS stack frame is popped, the values of unaliased variables
michael@0 1990 * are lost. If there is any debug scope referring to this scope, save a
michael@0 1991 * copy of the unaliased variables' values in an array for later debugger
michael@0 1992 * access via DebugScopeProxy::handleUnaliasedAccess.
michael@0 1993 *
michael@0 1994 * Note: since it is simplest for this function to be infallible, failure
michael@0 1995 * in this code will be silently ignored. This does not break any
michael@0 1996 * invariants since DebugScopeObject::maybeSnapshot can already be nullptr.
michael@0 1997 */
michael@0 1998 if (debugScope) {
michael@0 1999 /*
michael@0 2000 * Copy all frame values into the snapshot, regardless of
michael@0 2001 * aliasing. This unnecessarily includes aliased variables
michael@0 2002 * but it simplifies later indexing logic.
michael@0 2003 */
michael@0 2004 AutoValueVector vec(cx);
michael@0 2005 if (!frame.copyRawFrameSlots(&vec) || vec.length() == 0)
michael@0 2006 return;
michael@0 2007
michael@0 2008 /*
michael@0 2009 * Copy in formals that are not aliased via the scope chain
michael@0 2010 * but are aliased via the arguments object.
michael@0 2011 */
michael@0 2012 RootedScript script(cx, frame.script());
michael@0 2013 if (script->analyzedArgsUsage() && script->needsArgsObj() && frame.hasArgsObj()) {
michael@0 2014 for (unsigned i = 0; i < frame.numFormalArgs(); ++i) {
michael@0 2015 if (script->formalLivesInArgumentsObject(i))
michael@0 2016 vec[i] = frame.argsObj().arg(i);
michael@0 2017 }
michael@0 2018 }
michael@0 2019
michael@0 2020 /*
michael@0 2021 * Use a dense array as storage (since proxies do not have trace
michael@0 2022 * hooks). This array must not escape into the wild.
michael@0 2023 */
michael@0 2024 RootedObject snapshot(cx, NewDenseCopiedArray(cx, vec.length(), vec.begin()));
michael@0 2025 if (!snapshot) {
michael@0 2026 cx->clearPendingException();
michael@0 2027 return;
michael@0 2028 }
michael@0 2029
michael@0 2030 debugScope->initSnapshot(*snapshot);
michael@0 2031 }
michael@0 2032 }
michael@0 2033
michael@0 2034 void
michael@0 2035 DebugScopes::onPopBlock(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc)
michael@0 2036 {
michael@0 2037 assertSameCompartment(cx, frame);
michael@0 2038
michael@0 2039 DebugScopes *scopes = cx->compartment()->debugScopes;
michael@0 2040 if (!scopes)
michael@0 2041 return;
michael@0 2042
michael@0 2043 ScopeIter si(frame, pc, cx);
michael@0 2044 onPopBlock(cx, si);
michael@0 2045 }
michael@0 2046
michael@0 2047 void
michael@0 2048 DebugScopes::onPopBlock(JSContext *cx, const ScopeIter &si)
michael@0 2049 {
michael@0 2050 DebugScopes *scopes = cx->compartment()->debugScopes;
michael@0 2051 if (!scopes)
michael@0 2052 return;
michael@0 2053
michael@0 2054 JS_ASSERT(si.type() == ScopeIter::Block);
michael@0 2055
michael@0 2056 if (si.staticBlock().needsClone()) {
michael@0 2057 ClonedBlockObject &clone = si.scope().as<ClonedBlockObject>();
michael@0 2058 clone.copyUnaliasedValues(si.frame());
michael@0 2059 scopes->liveScopes.remove(&clone);
michael@0 2060 } else {
michael@0 2061 if (MissingScopeMap::Ptr p = scopes->missingScopes.lookup(si)) {
michael@0 2062 ClonedBlockObject &clone = p->value()->scope().as<ClonedBlockObject>();
michael@0 2063 clone.copyUnaliasedValues(si.frame());
michael@0 2064 scopes->liveScopes.remove(&clone);
michael@0 2065 scopes->missingScopes.remove(p);
michael@0 2066 }
michael@0 2067 }
michael@0 2068 }
michael@0 2069
michael@0 2070 void
michael@0 2071 DebugScopes::onPopWith(AbstractFramePtr frame)
michael@0 2072 {
michael@0 2073 DebugScopes *scopes = frame.compartment()->debugScopes;
michael@0 2074 if (scopes)
michael@0 2075 scopes->liveScopes.remove(&frame.scopeChain()->as<DynamicWithObject>());
michael@0 2076 }
michael@0 2077
michael@0 2078 void
michael@0 2079 DebugScopes::onPopStrictEvalScope(AbstractFramePtr frame)
michael@0 2080 {
michael@0 2081 DebugScopes *scopes = frame.compartment()->debugScopes;
michael@0 2082 if (!scopes)
michael@0 2083 return;
michael@0 2084
michael@0 2085 /*
michael@0 2086 * The stack frame may be observed before the prologue has created the
michael@0 2087 * CallObject. See ScopeIter::settle.
michael@0 2088 */
michael@0 2089 if (frame.hasCallObj())
michael@0 2090 scopes->liveScopes.remove(&frame.scopeChain()->as<CallObject>());
michael@0 2091 }
michael@0 2092
michael@0 2093 void
michael@0 2094 DebugScopes::onCompartmentLeaveDebugMode(JSCompartment *c)
michael@0 2095 {
michael@0 2096 DebugScopes *scopes = c->debugScopes;
michael@0 2097 if (scopes) {
michael@0 2098 scopes->proxiedScopes.clear();
michael@0 2099 scopes->missingScopes.clear();
michael@0 2100 scopes->liveScopes.clear();
michael@0 2101 }
michael@0 2102 }
michael@0 2103
michael@0 2104 bool
michael@0 2105 DebugScopes::updateLiveScopes(JSContext *cx)
michael@0 2106 {
michael@0 2107 JS_CHECK_RECURSION(cx, return false);
michael@0 2108
michael@0 2109 /*
michael@0 2110 * Note that we must always update the top frame's scope objects' entries
michael@0 2111 * in liveScopes because we can't be sure code hasn't run in that frame to
michael@0 2112 * change the scope chain since we were last called. The fp->prevUpToDate()
michael@0 2113 * flag indicates whether the scopes of frames older than fp are already
michael@0 2114 * included in liveScopes. It might seem simpler to have fp instead carry a
michael@0 2115 * flag indicating whether fp itself is accurately described, but then we
michael@0 2116 * would need to clear that flag whenever fp ran code. By storing the 'up
michael@0 2117 * to date' bit for fp->prev() in fp, simply popping fp effectively clears
michael@0 2118 * the flag for us, at exactly the time when execution resumes fp->prev().
michael@0 2119 */
michael@0 2120 for (AllFramesIter i(cx); !i.done(); ++i) {
michael@0 2121 if (!i.hasUsableAbstractFramePtr())
michael@0 2122 continue;
michael@0 2123
michael@0 2124 AbstractFramePtr frame = i.abstractFramePtr();
michael@0 2125 if (frame.scopeChain()->compartment() != cx->compartment())
michael@0 2126 continue;
michael@0 2127
michael@0 2128 if (frame.isFunctionFrame() && frame.callee()->isGenerator())
michael@0 2129 continue;
michael@0 2130
michael@0 2131 for (ScopeIter si(frame, i.pc(), cx); !si.done(); ++si) {
michael@0 2132 if (si.hasScopeObject()) {
michael@0 2133 JS_ASSERT(si.scope().compartment() == cx->compartment());
michael@0 2134 DebugScopes *scopes = ensureCompartmentData(cx);
michael@0 2135 if (!scopes)
michael@0 2136 return false;
michael@0 2137 if (!scopes->liveScopes.put(&si.scope(), si))
michael@0 2138 return false;
michael@0 2139 liveScopesPostWriteBarrier(cx->runtime(), &scopes->liveScopes, &si.scope());
michael@0 2140 }
michael@0 2141 }
michael@0 2142
michael@0 2143 if (frame.prevUpToDate())
michael@0 2144 return true;
michael@0 2145 JS_ASSERT(frame.scopeChain()->compartment()->debugMode());
michael@0 2146 frame.setPrevUpToDate();
michael@0 2147 }
michael@0 2148
michael@0 2149 return true;
michael@0 2150 }
michael@0 2151
michael@0 2152 ScopeIterVal*
michael@0 2153 DebugScopes::hasLiveScope(ScopeObject &scope)
michael@0 2154 {
michael@0 2155 DebugScopes *scopes = scope.compartment()->debugScopes;
michael@0 2156 if (!scopes)
michael@0 2157 return nullptr;
michael@0 2158
michael@0 2159 if (LiveScopeMap::Ptr p = scopes->liveScopes.lookup(&scope))
michael@0 2160 return &p->value();
michael@0 2161
michael@0 2162 return nullptr;
michael@0 2163 }
michael@0 2164
michael@0 2165 /*****************************************************************************/
michael@0 2166
michael@0 2167 static JSObject *
michael@0 2168 GetDebugScope(JSContext *cx, const ScopeIter &si);
michael@0 2169
michael@0 2170 static DebugScopeObject *
michael@0 2171 GetDebugScopeForScope(JSContext *cx, Handle<ScopeObject*> scope, const ScopeIter &enclosing)
michael@0 2172 {
michael@0 2173 if (DebugScopeObject *debugScope = DebugScopes::hasDebugScope(cx, *scope))
michael@0 2174 return debugScope;
michael@0 2175
michael@0 2176 RootedObject enclosingDebug(cx, GetDebugScope(cx, enclosing));
michael@0 2177 if (!enclosingDebug)
michael@0 2178 return nullptr;
michael@0 2179
michael@0 2180 JSObject &maybeDecl = scope->enclosingScope();
michael@0 2181 if (maybeDecl.is<DeclEnvObject>()) {
michael@0 2182 JS_ASSERT(CallObjectLambdaName(scope->as<CallObject>().callee()));
michael@0 2183 enclosingDebug = DebugScopeObject::create(cx, maybeDecl.as<DeclEnvObject>(), enclosingDebug);
michael@0 2184 if (!enclosingDebug)
michael@0 2185 return nullptr;
michael@0 2186 }
michael@0 2187
michael@0 2188 DebugScopeObject *debugScope = DebugScopeObject::create(cx, *scope, enclosingDebug);
michael@0 2189 if (!debugScope)
michael@0 2190 return nullptr;
michael@0 2191
michael@0 2192 if (!DebugScopes::addDebugScope(cx, *scope, *debugScope))
michael@0 2193 return nullptr;
michael@0 2194
michael@0 2195 return debugScope;
michael@0 2196 }
michael@0 2197
michael@0 2198 static DebugScopeObject *
michael@0 2199 GetDebugScopeForMissing(JSContext *cx, const ScopeIter &si)
michael@0 2200 {
michael@0 2201 if (DebugScopeObject *debugScope = DebugScopes::hasDebugScope(cx, si))
michael@0 2202 return debugScope;
michael@0 2203
michael@0 2204 ScopeIter copy(si, cx);
michael@0 2205 RootedObject enclosingDebug(cx, GetDebugScope(cx, ++copy));
michael@0 2206 if (!enclosingDebug)
michael@0 2207 return nullptr;
michael@0 2208
michael@0 2209 /*
michael@0 2210 * Create the missing scope object. For block objects, this takes care of
michael@0 2211 * storing variable values after the stack frame has been popped. For call
michael@0 2212 * objects, we only use the pretend call object to access callee, bindings
michael@0 2213 * and to receive dynamically added properties. Together, this provides the
michael@0 2214 * nice invariant that every DebugScopeObject has a ScopeObject.
michael@0 2215 *
michael@0 2216 * Note: to preserve scopeChain depth invariants, these lazily-reified
michael@0 2217 * scopes must not be put on the frame's scope chain; instead, they are
michael@0 2218 * maintained via DebugScopes hooks.
michael@0 2219 */
michael@0 2220 DebugScopeObject *debugScope = nullptr;
michael@0 2221 switch (si.type()) {
michael@0 2222 case ScopeIter::Call: {
michael@0 2223 // Generators should always reify their scopes.
michael@0 2224 JS_ASSERT(!si.frame().callee()->isGenerator());
michael@0 2225 Rooted<CallObject*> callobj(cx, CallObject::createForFunction(cx, si.frame()));
michael@0 2226 if (!callobj)
michael@0 2227 return nullptr;
michael@0 2228
michael@0 2229 if (callobj->enclosingScope().is<DeclEnvObject>()) {
michael@0 2230 JS_ASSERT(CallObjectLambdaName(callobj->callee()));
michael@0 2231 DeclEnvObject &declenv = callobj->enclosingScope().as<DeclEnvObject>();
michael@0 2232 enclosingDebug = DebugScopeObject::create(cx, declenv, enclosingDebug);
michael@0 2233 if (!enclosingDebug)
michael@0 2234 return nullptr;
michael@0 2235 }
michael@0 2236
michael@0 2237 debugScope = DebugScopeObject::create(cx, *callobj, enclosingDebug);
michael@0 2238 break;
michael@0 2239 }
michael@0 2240 case ScopeIter::Block: {
michael@0 2241 // Generators should always reify their scopes.
michael@0 2242 JS_ASSERT_IF(si.frame().isFunctionFrame(), !si.frame().callee()->isGenerator());
michael@0 2243 Rooted<StaticBlockObject *> staticBlock(cx, &si.staticBlock());
michael@0 2244 ClonedBlockObject *block = ClonedBlockObject::create(cx, staticBlock, si.frame());
michael@0 2245 if (!block)
michael@0 2246 return nullptr;
michael@0 2247
michael@0 2248 debugScope = DebugScopeObject::create(cx, *block, enclosingDebug);
michael@0 2249 break;
michael@0 2250 }
michael@0 2251 case ScopeIter::With:
michael@0 2252 case ScopeIter::StrictEvalScope:
michael@0 2253 MOZ_ASSUME_UNREACHABLE("should already have a scope");
michael@0 2254 }
michael@0 2255 if (!debugScope)
michael@0 2256 return nullptr;
michael@0 2257
michael@0 2258 if (!DebugScopes::addDebugScope(cx, si, *debugScope))
michael@0 2259 return nullptr;
michael@0 2260
michael@0 2261 return debugScope;
michael@0 2262 }
michael@0 2263
michael@0 2264 static JSObject *
michael@0 2265 GetDebugScope(JSContext *cx, JSObject &obj)
michael@0 2266 {
michael@0 2267 /*
michael@0 2268 * As an engine invariant (maintained internally and asserted by Execute),
michael@0 2269 * ScopeObjects and non-ScopeObjects cannot be interleaved on the scope
michael@0 2270 * chain; every scope chain must start with zero or more ScopeObjects and
michael@0 2271 * terminate with one or more non-ScopeObjects (viz., GlobalObject).
michael@0 2272 */
michael@0 2273 if (!obj.is<ScopeObject>()) {
michael@0 2274 #ifdef DEBUG
michael@0 2275 JSObject *o = &obj;
michael@0 2276 while ((o = o->enclosingScope()))
michael@0 2277 JS_ASSERT(!o->is<ScopeObject>());
michael@0 2278 #endif
michael@0 2279 return &obj;
michael@0 2280 }
michael@0 2281
michael@0 2282 Rooted<ScopeObject*> scope(cx, &obj.as<ScopeObject>());
michael@0 2283 if (ScopeIterVal *maybeLiveScope = DebugScopes::hasLiveScope(*scope)) {
michael@0 2284 ScopeIter si(*maybeLiveScope, cx);
michael@0 2285 return GetDebugScope(cx, si);
michael@0 2286 }
michael@0 2287 ScopeIter si(scope->enclosingScope(), cx);
michael@0 2288 return GetDebugScopeForScope(cx, scope, si);
michael@0 2289 }
michael@0 2290
michael@0 2291 static JSObject *
michael@0 2292 GetDebugScope(JSContext *cx, const ScopeIter &si)
michael@0 2293 {
michael@0 2294 JS_CHECK_RECURSION(cx, return nullptr);
michael@0 2295
michael@0 2296 if (si.done())
michael@0 2297 return GetDebugScope(cx, si.enclosingScope());
michael@0 2298
michael@0 2299 if (!si.hasScopeObject())
michael@0 2300 return GetDebugScopeForMissing(cx, si);
michael@0 2301
michael@0 2302 Rooted<ScopeObject*> scope(cx, &si.scope());
michael@0 2303
michael@0 2304 ScopeIter copy(si, cx);
michael@0 2305 return GetDebugScopeForScope(cx, scope, ++copy);
michael@0 2306 }
michael@0 2307
michael@0 2308 JSObject *
michael@0 2309 js::GetDebugScopeForFunction(JSContext *cx, HandleFunction fun)
michael@0 2310 {
michael@0 2311 assertSameCompartment(cx, fun);
michael@0 2312 JS_ASSERT(cx->compartment()->debugMode());
michael@0 2313 if (!DebugScopes::updateLiveScopes(cx))
michael@0 2314 return nullptr;
michael@0 2315 return GetDebugScope(cx, *fun->environment());
michael@0 2316 }
michael@0 2317
michael@0 2318 JSObject *
michael@0 2319 js::GetDebugScopeForFrame(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc)
michael@0 2320 {
michael@0 2321 assertSameCompartment(cx, frame);
michael@0 2322 if (CanUseDebugScopeMaps(cx) && !DebugScopes::updateLiveScopes(cx))
michael@0 2323 return nullptr;
michael@0 2324 ScopeIter si(frame, pc, cx);
michael@0 2325 return GetDebugScope(cx, si);
michael@0 2326 }
michael@0 2327
michael@0 2328 #ifdef DEBUG
michael@0 2329
michael@0 2330 typedef HashSet<PropertyName *> PropertyNameSet;
michael@0 2331
michael@0 2332 static bool
michael@0 2333 RemoveReferencedNames(JSContext *cx, HandleScript script, PropertyNameSet &remainingNames)
michael@0 2334 {
michael@0 2335 // Remove from remainingNames --- the closure variables in some outer
michael@0 2336 // script --- any free variables in this script. This analysis isn't perfect:
michael@0 2337 //
michael@0 2338 // - It will not account for free variables in an inner script which are
michael@0 2339 // actually accessing some name in an intermediate script between the
michael@0 2340 // inner and outer scripts. This can cause remainingNames to be an
michael@0 2341 // underapproximation.
michael@0 2342 //
michael@0 2343 // - It will not account for new names introduced via eval. This can cause
michael@0 2344 // remainingNames to be an overapproximation. This would be easy to fix
michael@0 2345 // but is nice to have as the eval will probably not access these
michael@0 2346 // these names and putting eval in an inner script is bad news if you
michael@0 2347 // care about entraining variables unnecessarily.
michael@0 2348
michael@0 2349 for (jsbytecode *pc = script->code(); pc != script->codeEnd(); pc += GetBytecodeLength(pc)) {
michael@0 2350 PropertyName *name;
michael@0 2351
michael@0 2352 switch (JSOp(*pc)) {
michael@0 2353 case JSOP_NAME:
michael@0 2354 case JSOP_SETNAME:
michael@0 2355 name = script->getName(pc);
michael@0 2356 break;
michael@0 2357
michael@0 2358 case JSOP_GETALIASEDVAR:
michael@0 2359 case JSOP_SETALIASEDVAR:
michael@0 2360 name = ScopeCoordinateName(cx->runtime()->scopeCoordinateNameCache, script, pc);
michael@0 2361 break;
michael@0 2362
michael@0 2363 default:
michael@0 2364 name = nullptr;
michael@0 2365 break;
michael@0 2366 }
michael@0 2367
michael@0 2368 if (name)
michael@0 2369 remainingNames.remove(name);
michael@0 2370 }
michael@0 2371
michael@0 2372 if (script->hasObjects()) {
michael@0 2373 ObjectArray *objects = script->objects();
michael@0 2374 for (size_t i = 0; i < objects->length; i++) {
michael@0 2375 JSObject *obj = objects->vector[i];
michael@0 2376 if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) {
michael@0 2377 JSFunction *fun = &obj->as<JSFunction>();
michael@0 2378 RootedScript innerScript(cx, fun->getOrCreateScript(cx));
michael@0 2379 if (!innerScript)
michael@0 2380 return false;
michael@0 2381
michael@0 2382 if (!RemoveReferencedNames(cx, innerScript, remainingNames))
michael@0 2383 return false;
michael@0 2384 }
michael@0 2385 }
michael@0 2386 }
michael@0 2387
michael@0 2388 return true;
michael@0 2389 }
michael@0 2390
michael@0 2391 static bool
michael@0 2392 AnalyzeEntrainedVariablesInScript(JSContext *cx, HandleScript script, HandleScript innerScript)
michael@0 2393 {
michael@0 2394 PropertyNameSet remainingNames(cx);
michael@0 2395 if (!remainingNames.init())
michael@0 2396 return false;
michael@0 2397
michael@0 2398 for (BindingIter bi(script); bi; bi++) {
michael@0 2399 if (bi->aliased()) {
michael@0 2400 PropertyNameSet::AddPtr p = remainingNames.lookupForAdd(bi->name());
michael@0 2401 if (!p && !remainingNames.add(p, bi->name()))
michael@0 2402 return false;
michael@0 2403 }
michael@0 2404 }
michael@0 2405
michael@0 2406 if (!RemoveReferencedNames(cx, innerScript, remainingNames))
michael@0 2407 return false;
michael@0 2408
michael@0 2409 if (!remainingNames.empty()) {
michael@0 2410 Sprinter buf(cx);
michael@0 2411 if (!buf.init())
michael@0 2412 return false;
michael@0 2413
michael@0 2414 buf.printf("Script ");
michael@0 2415
michael@0 2416 if (JSAtom *name = script->functionNonDelazifying()->displayAtom()) {
michael@0 2417 buf.putString(name);
michael@0 2418 buf.printf(" ");
michael@0 2419 }
michael@0 2420
michael@0 2421 buf.printf("(%s:%d) has variables entrained by ", script->filename(), script->lineno());
michael@0 2422
michael@0 2423 if (JSAtom *name = innerScript->functionNonDelazifying()->displayAtom()) {
michael@0 2424 buf.putString(name);
michael@0 2425 buf.printf(" ");
michael@0 2426 }
michael@0 2427
michael@0 2428 buf.printf("(%s:%d) ::", innerScript->filename(), innerScript->lineno());
michael@0 2429
michael@0 2430 for (PropertyNameSet::Range r = remainingNames.all(); !r.empty(); r.popFront()) {
michael@0 2431 buf.printf(" ");
michael@0 2432 buf.putString(r.front());
michael@0 2433 }
michael@0 2434
michael@0 2435 printf("%s\n", buf.string());
michael@0 2436 }
michael@0 2437
michael@0 2438 if (innerScript->hasObjects()) {
michael@0 2439 ObjectArray *objects = innerScript->objects();
michael@0 2440 for (size_t i = 0; i < objects->length; i++) {
michael@0 2441 JSObject *obj = objects->vector[i];
michael@0 2442 if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) {
michael@0 2443 JSFunction *fun = &obj->as<JSFunction>();
michael@0 2444 RootedScript innerInnerScript(cx, fun->getOrCreateScript(cx));
michael@0 2445 if (!innerInnerScript ||
michael@0 2446 !AnalyzeEntrainedVariablesInScript(cx, script, innerInnerScript))
michael@0 2447 {
michael@0 2448 return false;
michael@0 2449 }
michael@0 2450 }
michael@0 2451 }
michael@0 2452 }
michael@0 2453
michael@0 2454 return true;
michael@0 2455 }
michael@0 2456
michael@0 2457 // Look for local variables in script or any other script inner to it, which are
michael@0 2458 // part of the script's call object and are unnecessarily entrained by their own
michael@0 2459 // inner scripts which do not refer to those variables. An example is:
michael@0 2460 //
michael@0 2461 // function foo() {
michael@0 2462 // var a, b;
michael@0 2463 // function bar() { return a; }
michael@0 2464 // function baz() { return b; }
michael@0 2465 // }
michael@0 2466 //
michael@0 2467 // |bar| unnecessarily entrains |b|, and |baz| unnecessarily entrains |a|.
michael@0 2468 bool
michael@0 2469 js::AnalyzeEntrainedVariables(JSContext *cx, HandleScript script)
michael@0 2470 {
michael@0 2471 if (!script->hasObjects())
michael@0 2472 return true;
michael@0 2473
michael@0 2474 ObjectArray *objects = script->objects();
michael@0 2475 for (size_t i = 0; i < objects->length; i++) {
michael@0 2476 JSObject *obj = objects->vector[i];
michael@0 2477 if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) {
michael@0 2478 JSFunction *fun = &obj->as<JSFunction>();
michael@0 2479 RootedScript innerScript(cx, fun->getOrCreateScript(cx));
michael@0 2480 if (!innerScript)
michael@0 2481 return false;
michael@0 2482
michael@0 2483 if (script->functionDelazifying() && script->functionDelazifying()->isHeavyweight()) {
michael@0 2484 if (!AnalyzeEntrainedVariablesInScript(cx, script, innerScript))
michael@0 2485 return false;
michael@0 2486 }
michael@0 2487
michael@0 2488 if (!AnalyzeEntrainedVariables(cx, innerScript))
michael@0 2489 return false;
michael@0 2490 }
michael@0 2491 }
michael@0 2492
michael@0 2493 return true;
michael@0 2494 }
michael@0 2495
michael@0 2496 #endif

mercurial