1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/vm/ScopeObject.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,2496 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "vm/ScopeObject-inl.h" 1.11 + 1.12 +#include "mozilla/PodOperations.h" 1.13 + 1.14 +#include "jscompartment.h" 1.15 +#include "jsiter.h" 1.16 + 1.17 +#include "vm/ArgumentsObject.h" 1.18 +#include "vm/GlobalObject.h" 1.19 +#include "vm/ProxyObject.h" 1.20 +#include "vm/Shape.h" 1.21 +#include "vm/Xdr.h" 1.22 + 1.23 +#include "jsatominlines.h" 1.24 +#include "jsobjinlines.h" 1.25 +#include "jsscriptinlines.h" 1.26 + 1.27 +#include "vm/Stack-inl.h" 1.28 + 1.29 +using namespace js; 1.30 +using namespace js::types; 1.31 + 1.32 +using mozilla::PodZero; 1.33 + 1.34 +typedef Rooted<ArgumentsObject *> RootedArgumentsObject; 1.35 +typedef MutableHandle<ArgumentsObject *> MutableHandleArgumentsObject; 1.36 + 1.37 +/*****************************************************************************/ 1.38 + 1.39 +static JSObject * 1.40 +InnermostStaticScope(JSScript *script, jsbytecode *pc) 1.41 +{ 1.42 + JS_ASSERT(script->containsPC(pc)); 1.43 + JS_ASSERT(JOF_OPTYPE(*pc) == JOF_SCOPECOORD); 1.44 + 1.45 + NestedScopeObject *scope = script->getStaticScope(pc); 1.46 + if (scope) 1.47 + return scope; 1.48 + return script->functionNonDelazifying(); 1.49 +} 1.50 + 1.51 +Shape * 1.52 +js::ScopeCoordinateToStaticScopeShape(JSScript *script, jsbytecode *pc) 1.53 +{ 1.54 + StaticScopeIter<NoGC> ssi(InnermostStaticScope(script, pc)); 1.55 + uint32_t hops = ScopeCoordinate(pc).hops(); 1.56 + while (true) { 1.57 + JS_ASSERT(!ssi.done()); 1.58 + if (ssi.hasDynamicScopeObject()) { 1.59 + if (!hops) 1.60 + break; 1.61 + hops--; 1.62 + } 1.63 + ssi++; 1.64 + } 1.65 + return ssi.scopeShape(); 1.66 +} 1.67 + 1.68 +static const uint32_t SCOPE_COORDINATE_NAME_THRESHOLD = 20; 1.69 + 1.70 +void 1.71 +ScopeCoordinateNameCache::purge() 1.72 +{ 1.73 + shape = nullptr; 1.74 + if (map.initialized()) 1.75 + map.finish(); 1.76 +} 1.77 + 1.78 +PropertyName * 1.79 +js::ScopeCoordinateName(ScopeCoordinateNameCache &cache, JSScript *script, jsbytecode *pc) 1.80 +{ 1.81 + Shape *shape = ScopeCoordinateToStaticScopeShape(script, pc); 1.82 + if (shape != cache.shape && shape->slot() >= SCOPE_COORDINATE_NAME_THRESHOLD) { 1.83 + cache.purge(); 1.84 + if (cache.map.init(shape->slot())) { 1.85 + cache.shape = shape; 1.86 + Shape::Range<NoGC> r(shape); 1.87 + while (!r.empty()) { 1.88 + if (!cache.map.putNew(r.front().slot(), r.front().propid())) { 1.89 + cache.purge(); 1.90 + break; 1.91 + } 1.92 + r.popFront(); 1.93 + } 1.94 + } 1.95 + } 1.96 + 1.97 + jsid id; 1.98 + ScopeCoordinate sc(pc); 1.99 + if (shape == cache.shape) { 1.100 + ScopeCoordinateNameCache::Map::Ptr p = cache.map.lookup(sc.slot()); 1.101 + id = p->value(); 1.102 + } else { 1.103 + Shape::Range<NoGC> r(shape); 1.104 + while (r.front().slot() != sc.slot()) 1.105 + r.popFront(); 1.106 + id = r.front().propidRaw(); 1.107 + } 1.108 + 1.109 + /* Beware nameless destructuring formal. */ 1.110 + if (!JSID_IS_ATOM(id)) 1.111 + return script->runtimeFromAnyThread()->commonNames->empty; 1.112 + return JSID_TO_ATOM(id)->asPropertyName(); 1.113 +} 1.114 + 1.115 +JSScript * 1.116 +js::ScopeCoordinateFunctionScript(JSScript *script, jsbytecode *pc) 1.117 +{ 1.118 + StaticScopeIter<NoGC> ssi(InnermostStaticScope(script, pc)); 1.119 + uint32_t hops = ScopeCoordinate(pc).hops(); 1.120 + while (true) { 1.121 + if (ssi.hasDynamicScopeObject()) { 1.122 + if (!hops) 1.123 + break; 1.124 + hops--; 1.125 + } 1.126 + ssi++; 1.127 + } 1.128 + if (ssi.type() != StaticScopeIter<NoGC>::FUNCTION) 1.129 + return nullptr; 1.130 + return ssi.funScript(); 1.131 +} 1.132 + 1.133 +/*****************************************************************************/ 1.134 + 1.135 +void 1.136 +ScopeObject::setEnclosingScope(HandleObject obj) 1.137 +{ 1.138 + JS_ASSERT_IF(obj->is<CallObject>() || obj->is<DeclEnvObject>() || obj->is<BlockObject>(), 1.139 + obj->isDelegate()); 1.140 + setFixedSlot(SCOPE_CHAIN_SLOT, ObjectValue(*obj)); 1.141 +} 1.142 + 1.143 +CallObject * 1.144 +CallObject::create(JSContext *cx, HandleShape shape, HandleTypeObject type, HeapSlot *slots) 1.145 +{ 1.146 + MOZ_ASSERT(!type->singleton(), 1.147 + "passed a singleton type to create() (use createSingleton() " 1.148 + "instead)"); 1.149 + gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots()); 1.150 + MOZ_ASSERT(CanBeFinalizedInBackground(kind, &CallObject::class_)); 1.151 + kind = gc::GetBackgroundAllocKind(kind); 1.152 + 1.153 + JSObject *obj = JSObject::create(cx, kind, gc::DefaultHeap, shape, type, slots); 1.154 + if (!obj) 1.155 + return nullptr; 1.156 + 1.157 + return &obj->as<CallObject>(); 1.158 +} 1.159 + 1.160 +CallObject * 1.161 +CallObject::createSingleton(JSContext *cx, HandleShape shape, HeapSlot *slots) 1.162 +{ 1.163 + gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots()); 1.164 + MOZ_ASSERT(CanBeFinalizedInBackground(kind, &CallObject::class_)); 1.165 + kind = gc::GetBackgroundAllocKind(kind); 1.166 + 1.167 + RootedTypeObject type(cx, cx->getSingletonType(&class_, nullptr)); 1.168 + if (!type) 1.169 + return nullptr; 1.170 + RootedObject obj(cx, JSObject::create(cx, kind, gc::TenuredHeap, shape, type, slots)); 1.171 + if (!obj) 1.172 + return nullptr; 1.173 + 1.174 + MOZ_ASSERT(obj->hasSingletonType(), 1.175 + "type created inline above must be a singleton"); 1.176 + 1.177 + return &obj->as<CallObject>(); 1.178 +} 1.179 + 1.180 +/* 1.181 + * Create a CallObject for a JSScript that is not initialized to any particular 1.182 + * callsite. This object can either be initialized (with an enclosing scope and 1.183 + * callee) or used as a template for jit compilation. 1.184 + */ 1.185 +CallObject * 1.186 +CallObject::createTemplateObject(JSContext *cx, HandleScript script, gc::InitialHeap heap) 1.187 +{ 1.188 + RootedShape shape(cx, script->bindings.callObjShape()); 1.189 + JS_ASSERT(shape->getObjectClass() == &class_); 1.190 + 1.191 + RootedTypeObject type(cx, cx->getNewType(&class_, nullptr)); 1.192 + if (!type) 1.193 + return nullptr; 1.194 + 1.195 + gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots()); 1.196 + JS_ASSERT(CanBeFinalizedInBackground(kind, &class_)); 1.197 + kind = gc::GetBackgroundAllocKind(kind); 1.198 + 1.199 + JSObject *obj = JSObject::create(cx, kind, heap, shape, type); 1.200 + if (!obj) 1.201 + return nullptr; 1.202 + 1.203 + return &obj->as<CallObject>(); 1.204 +} 1.205 + 1.206 +/* 1.207 + * Construct a call object for the given bindings. If this is a call object 1.208 + * for a function invocation, callee should be the function being called. 1.209 + * Otherwise it must be a call object for eval of strict mode code, and callee 1.210 + * must be null. 1.211 + */ 1.212 +CallObject * 1.213 +CallObject::create(JSContext *cx, HandleScript script, HandleObject enclosing, HandleFunction callee) 1.214 +{ 1.215 + gc::InitialHeap heap = script->treatAsRunOnce() ? gc::TenuredHeap : gc::DefaultHeap; 1.216 + CallObject *callobj = CallObject::createTemplateObject(cx, script, heap); 1.217 + if (!callobj) 1.218 + return nullptr; 1.219 + 1.220 + callobj->as<ScopeObject>().setEnclosingScope(enclosing); 1.221 + callobj->initFixedSlot(CALLEE_SLOT, ObjectOrNullValue(callee)); 1.222 + 1.223 + if (script->treatAsRunOnce()) { 1.224 + Rooted<CallObject*> ncallobj(cx, callobj); 1.225 + if (!JSObject::setSingletonType(cx, ncallobj)) 1.226 + return nullptr; 1.227 + return ncallobj; 1.228 + } 1.229 + 1.230 + return callobj; 1.231 +} 1.232 + 1.233 +CallObject * 1.234 +CallObject::createForFunction(JSContext *cx, HandleObject enclosing, HandleFunction callee) 1.235 +{ 1.236 + RootedObject scopeChain(cx, enclosing); 1.237 + JS_ASSERT(scopeChain); 1.238 + 1.239 + /* 1.240 + * For a named function expression Call's parent points to an environment 1.241 + * object holding function's name. 1.242 + */ 1.243 + if (callee->isNamedLambda()) { 1.244 + scopeChain = DeclEnvObject::create(cx, scopeChain, callee); 1.245 + if (!scopeChain) 1.246 + return nullptr; 1.247 + } 1.248 + 1.249 + RootedScript script(cx, callee->nonLazyScript()); 1.250 + return create(cx, script, scopeChain, callee); 1.251 +} 1.252 + 1.253 +CallObject * 1.254 +CallObject::createForFunction(JSContext *cx, AbstractFramePtr frame) 1.255 +{ 1.256 + JS_ASSERT(frame.isNonEvalFunctionFrame()); 1.257 + assertSameCompartment(cx, frame); 1.258 + 1.259 + RootedObject scopeChain(cx, frame.scopeChain()); 1.260 + RootedFunction callee(cx, frame.callee()); 1.261 + 1.262 + CallObject *callobj = createForFunction(cx, scopeChain, callee); 1.263 + if (!callobj) 1.264 + return nullptr; 1.265 + 1.266 + /* Copy in the closed-over formal arguments. */ 1.267 + for (AliasedFormalIter i(frame.script()); i; i++) { 1.268 + callobj->setAliasedVar(cx, i, i->name(), 1.269 + frame.unaliasedFormal(i.frameIndex(), DONT_CHECK_ALIASING)); 1.270 + } 1.271 + 1.272 + return callobj; 1.273 +} 1.274 + 1.275 +CallObject * 1.276 +CallObject::createForStrictEval(JSContext *cx, AbstractFramePtr frame) 1.277 +{ 1.278 + JS_ASSERT(frame.isStrictEvalFrame()); 1.279 + JS_ASSERT_IF(frame.isInterpreterFrame(), cx->interpreterFrame() == frame.asInterpreterFrame()); 1.280 + JS_ASSERT_IF(frame.isInterpreterFrame(), cx->interpreterRegs().pc == frame.script()->code()); 1.281 + 1.282 + RootedFunction callee(cx); 1.283 + RootedScript script(cx, frame.script()); 1.284 + RootedObject scopeChain(cx, frame.scopeChain()); 1.285 + return create(cx, script, scopeChain, callee); 1.286 +} 1.287 + 1.288 +const Class CallObject::class_ = { 1.289 + "Call", 1.290 + JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_RESERVED_SLOTS(CallObject::RESERVED_SLOTS), 1.291 + JS_PropertyStub, /* addProperty */ 1.292 + JS_DeletePropertyStub, /* delProperty */ 1.293 + JS_PropertyStub, /* getProperty */ 1.294 + JS_StrictPropertyStub, /* setProperty */ 1.295 + JS_EnumerateStub, 1.296 + JS_ResolveStub, 1.297 + nullptr /* convert: Leave it nullptr so we notice if calls ever escape */ 1.298 +}; 1.299 + 1.300 +const Class DeclEnvObject::class_ = { 1.301 + js_Object_str, 1.302 + JSCLASS_HAS_RESERVED_SLOTS(DeclEnvObject::RESERVED_SLOTS) | 1.303 + JSCLASS_HAS_CACHED_PROTO(JSProto_Object), 1.304 + JS_PropertyStub, /* addProperty */ 1.305 + JS_DeletePropertyStub, /* delProperty */ 1.306 + JS_PropertyStub, /* getProperty */ 1.307 + JS_StrictPropertyStub, /* setProperty */ 1.308 + JS_EnumerateStub, 1.309 + JS_ResolveStub, 1.310 + JS_ConvertStub 1.311 +}; 1.312 + 1.313 +/* 1.314 + * Create a DeclEnvObject for a JSScript that is not initialized to any 1.315 + * particular callsite. This object can either be initialized (with an enclosing 1.316 + * scope and callee) or used as a template for jit compilation. 1.317 + */ 1.318 +DeclEnvObject * 1.319 +DeclEnvObject::createTemplateObject(JSContext *cx, HandleFunction fun, gc::InitialHeap heap) 1.320 +{ 1.321 + JS_ASSERT(IsNurseryAllocable(FINALIZE_KIND)); 1.322 + 1.323 + RootedTypeObject type(cx, cx->getNewType(&class_, nullptr)); 1.324 + if (!type) 1.325 + return nullptr; 1.326 + 1.327 + RootedShape emptyDeclEnvShape(cx); 1.328 + emptyDeclEnvShape = EmptyShape::getInitialShape(cx, &class_, nullptr, 1.329 + cx->global(), nullptr, FINALIZE_KIND, 1.330 + BaseShape::DELEGATE); 1.331 + if (!emptyDeclEnvShape) 1.332 + return nullptr; 1.333 + 1.334 + RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, heap, emptyDeclEnvShape, type)); 1.335 + if (!obj) 1.336 + return nullptr; 1.337 + 1.338 + // Assign a fixed slot to a property with the same name as the lambda. 1.339 + Rooted<jsid> id(cx, AtomToId(fun->atom())); 1.340 + const Class *clasp = obj->getClass(); 1.341 + unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY; 1.342 + if (!JSObject::putProperty<SequentialExecution>(cx, obj, id, clasp->getProperty, 1.343 + clasp->setProperty, lambdaSlot(), attrs, 0)) { 1.344 + return nullptr; 1.345 + } 1.346 + 1.347 + JS_ASSERT(!obj->hasDynamicSlots()); 1.348 + return &obj->as<DeclEnvObject>(); 1.349 +} 1.350 + 1.351 +DeclEnvObject * 1.352 +DeclEnvObject::create(JSContext *cx, HandleObject enclosing, HandleFunction callee) 1.353 +{ 1.354 + RootedObject obj(cx, createTemplateObject(cx, callee, gc::DefaultHeap)); 1.355 + if (!obj) 1.356 + return nullptr; 1.357 + 1.358 + obj->as<ScopeObject>().setEnclosingScope(enclosing); 1.359 + obj->setFixedSlot(lambdaSlot(), ObjectValue(*callee)); 1.360 + return &obj->as<DeclEnvObject>(); 1.361 +} 1.362 + 1.363 +template<XDRMode mode> 1.364 +bool 1.365 +js::XDRStaticWithObject(XDRState<mode> *xdr, HandleObject enclosingScope, StaticWithObject **objp) 1.366 +{ 1.367 + if (mode == XDR_DECODE) { 1.368 + JSContext *cx = xdr->cx(); 1.369 + Rooted<StaticWithObject*> obj(cx, StaticWithObject::create(cx)); 1.370 + if (!obj) 1.371 + return false; 1.372 + obj->initEnclosingNestedScope(enclosingScope); 1.373 + *objp = obj; 1.374 + } 1.375 + // For encoding, there is nothing to do. The only information that is 1.376 + // encoded by a StaticWithObject is its presence on the scope chain, and the 1.377 + // script XDR handler already takes care of that. 1.378 + 1.379 + return true; 1.380 +} 1.381 + 1.382 +template bool 1.383 +js::XDRStaticWithObject(XDRState<XDR_ENCODE> *, HandleObject, StaticWithObject **); 1.384 + 1.385 +template bool 1.386 +js::XDRStaticWithObject(XDRState<XDR_DECODE> *, HandleObject, StaticWithObject **); 1.387 + 1.388 +StaticWithObject * 1.389 +StaticWithObject::create(ExclusiveContext *cx) 1.390 +{ 1.391 + RootedTypeObject type(cx, cx->getNewType(&class_, nullptr)); 1.392 + if (!type) 1.393 + return nullptr; 1.394 + 1.395 + RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(nullptr), 1.396 + nullptr, nullptr, FINALIZE_KIND)); 1.397 + if (!shape) 1.398 + return nullptr; 1.399 + 1.400 + RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, gc::TenuredHeap, shape, type)); 1.401 + if (!obj) 1.402 + return nullptr; 1.403 + 1.404 + return &obj->as<StaticWithObject>(); 1.405 +} 1.406 + 1.407 +static JSObject * 1.408 +CloneStaticWithObject(JSContext *cx, HandleObject enclosingScope, Handle<StaticWithObject*> srcWith) 1.409 +{ 1.410 + Rooted<StaticWithObject*> clone(cx, StaticWithObject::create(cx)); 1.411 + if (!clone) 1.412 + return nullptr; 1.413 + 1.414 + clone->initEnclosingNestedScope(enclosingScope); 1.415 + 1.416 + return clone; 1.417 +} 1.418 + 1.419 +DynamicWithObject * 1.420 +DynamicWithObject::create(JSContext *cx, HandleObject object, HandleObject enclosing, 1.421 + HandleObject staticWith) 1.422 +{ 1.423 + JS_ASSERT(staticWith->is<StaticWithObject>()); 1.424 + RootedTypeObject type(cx, cx->getNewType(&class_, staticWith.get())); 1.425 + if (!type) 1.426 + return nullptr; 1.427 + 1.428 + RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(staticWith), 1.429 + &enclosing->global(), nullptr, 1.430 + FINALIZE_KIND)); 1.431 + if (!shape) 1.432 + return nullptr; 1.433 + 1.434 + RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, gc::DefaultHeap, shape, type)); 1.435 + if (!obj) 1.436 + return nullptr; 1.437 + 1.438 + JSObject *thisp = JSObject::thisObject(cx, object); 1.439 + if (!thisp) 1.440 + return nullptr; 1.441 + 1.442 + obj->as<ScopeObject>().setEnclosingScope(enclosing); 1.443 + obj->setFixedSlot(OBJECT_SLOT, ObjectValue(*object)); 1.444 + obj->setFixedSlot(THIS_SLOT, ObjectValue(*thisp)); 1.445 + 1.446 + return &obj->as<DynamicWithObject>(); 1.447 +} 1.448 + 1.449 +static bool 1.450 +with_LookupGeneric(JSContext *cx, HandleObject obj, HandleId id, 1.451 + MutableHandleObject objp, MutableHandleShape propp) 1.452 +{ 1.453 + RootedObject actual(cx, &obj->as<DynamicWithObject>().object()); 1.454 + return JSObject::lookupGeneric(cx, actual, id, objp, propp); 1.455 +} 1.456 + 1.457 +static bool 1.458 +with_LookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, 1.459 + MutableHandleObject objp, MutableHandleShape propp) 1.460 +{ 1.461 + Rooted<jsid> id(cx, NameToId(name)); 1.462 + return with_LookupGeneric(cx, obj, id, objp, propp); 1.463 +} 1.464 + 1.465 +static bool 1.466 +with_LookupElement(JSContext *cx, HandleObject obj, uint32_t index, 1.467 + MutableHandleObject objp, MutableHandleShape propp) 1.468 +{ 1.469 + RootedId id(cx); 1.470 + if (!IndexToId(cx, index, &id)) 1.471 + return false; 1.472 + return with_LookupGeneric(cx, obj, id, objp, propp); 1.473 +} 1.474 + 1.475 +static bool 1.476 +with_GetGeneric(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id, 1.477 + MutableHandleValue vp) 1.478 +{ 1.479 + RootedObject actual(cx, &obj->as<DynamicWithObject>().object()); 1.480 + return JSObject::getGeneric(cx, actual, actual, id, vp); 1.481 +} 1.482 + 1.483 +static bool 1.484 +with_GetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandlePropertyName name, 1.485 + MutableHandleValue vp) 1.486 +{ 1.487 + RootedId id(cx, NameToId(name)); 1.488 + return with_GetGeneric(cx, obj, receiver, id, vp); 1.489 +} 1.490 + 1.491 +static bool 1.492 +with_GetElement(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index, 1.493 + MutableHandleValue vp) 1.494 +{ 1.495 + RootedId id(cx); 1.496 + if (!IndexToId(cx, index, &id)) 1.497 + return false; 1.498 + return with_GetGeneric(cx, obj, receiver, id, vp); 1.499 +} 1.500 + 1.501 +static bool 1.502 +with_SetGeneric(JSContext *cx, HandleObject obj, HandleId id, 1.503 + MutableHandleValue vp, bool strict) 1.504 +{ 1.505 + RootedObject actual(cx, &obj->as<DynamicWithObject>().object()); 1.506 + return JSObject::setGeneric(cx, actual, actual, id, vp, strict); 1.507 +} 1.508 + 1.509 +static bool 1.510 +with_SetProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, 1.511 + MutableHandleValue vp, bool strict) 1.512 +{ 1.513 + RootedObject actual(cx, &obj->as<DynamicWithObject>().object()); 1.514 + return JSObject::setProperty(cx, actual, actual, name, vp, strict); 1.515 +} 1.516 + 1.517 +static bool 1.518 +with_SetElement(JSContext *cx, HandleObject obj, uint32_t index, 1.519 + MutableHandleValue vp, bool strict) 1.520 +{ 1.521 + RootedObject actual(cx, &obj->as<DynamicWithObject>().object()); 1.522 + return JSObject::setElement(cx, actual, actual, index, vp, strict); 1.523 +} 1.524 + 1.525 +static bool 1.526 +with_GetGenericAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp) 1.527 +{ 1.528 + RootedObject actual(cx, &obj->as<DynamicWithObject>().object()); 1.529 + return JSObject::getGenericAttributes(cx, actual, id, attrsp); 1.530 +} 1.531 + 1.532 +static bool 1.533 +with_SetGenericAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp) 1.534 +{ 1.535 + RootedObject actual(cx, &obj->as<DynamicWithObject>().object()); 1.536 + return JSObject::setGenericAttributes(cx, actual, id, attrsp); 1.537 +} 1.538 + 1.539 +static bool 1.540 +with_DeleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, 1.541 + bool *succeeded) 1.542 +{ 1.543 + RootedObject actual(cx, &obj->as<DynamicWithObject>().object()); 1.544 + return JSObject::deleteProperty(cx, actual, name, succeeded); 1.545 +} 1.546 + 1.547 +static bool 1.548 +with_DeleteElement(JSContext *cx, HandleObject obj, uint32_t index, 1.549 + bool *succeeded) 1.550 +{ 1.551 + RootedObject actual(cx, &obj->as<DynamicWithObject>().object()); 1.552 + return JSObject::deleteElement(cx, actual, index, succeeded); 1.553 +} 1.554 + 1.555 +static JSObject * 1.556 +with_ThisObject(JSContext *cx, HandleObject obj) 1.557 +{ 1.558 + return &obj->as<DynamicWithObject>().withThis(); 1.559 +} 1.560 + 1.561 +const Class StaticWithObject::class_ = { 1.562 + "WithTemplate", 1.563 + JSCLASS_IMPLEMENTS_BARRIERS | 1.564 + JSCLASS_HAS_RESERVED_SLOTS(StaticWithObject::RESERVED_SLOTS) | 1.565 + JSCLASS_IS_ANONYMOUS, 1.566 + JS_PropertyStub, /* addProperty */ 1.567 + JS_DeletePropertyStub, /* delProperty */ 1.568 + JS_PropertyStub, /* getProperty */ 1.569 + JS_StrictPropertyStub, /* setProperty */ 1.570 + JS_EnumerateStub, 1.571 + JS_ResolveStub, 1.572 + JS_ConvertStub 1.573 +}; 1.574 + 1.575 +const Class DynamicWithObject::class_ = { 1.576 + "With", 1.577 + JSCLASS_HAS_RESERVED_SLOTS(DynamicWithObject::RESERVED_SLOTS) | 1.578 + JSCLASS_IS_ANONYMOUS, 1.579 + JS_PropertyStub, /* addProperty */ 1.580 + JS_DeletePropertyStub, /* delProperty */ 1.581 + JS_PropertyStub, /* getProperty */ 1.582 + JS_StrictPropertyStub, /* setProperty */ 1.583 + JS_EnumerateStub, 1.584 + JS_ResolveStub, 1.585 + JS_ConvertStub, 1.586 + nullptr, /* finalize */ 1.587 + nullptr, /* call */ 1.588 + nullptr, /* hasInstance */ 1.589 + nullptr, /* construct */ 1.590 + nullptr, /* trace */ 1.591 + JS_NULL_CLASS_SPEC, 1.592 + JS_NULL_CLASS_EXT, 1.593 + { 1.594 + with_LookupGeneric, 1.595 + with_LookupProperty, 1.596 + with_LookupElement, 1.597 + nullptr, /* defineGeneric */ 1.598 + nullptr, /* defineProperty */ 1.599 + nullptr, /* defineElement */ 1.600 + with_GetGeneric, 1.601 + with_GetProperty, 1.602 + with_GetElement, 1.603 + with_SetGeneric, 1.604 + with_SetProperty, 1.605 + with_SetElement, 1.606 + with_GetGenericAttributes, 1.607 + with_SetGenericAttributes, 1.608 + with_DeleteProperty, 1.609 + with_DeleteElement, 1.610 + nullptr, nullptr, /* watch/unwatch */ 1.611 + nullptr, /* slice */ 1.612 + nullptr, /* enumerate (native enumeration of target doesn't work) */ 1.613 + with_ThisObject, 1.614 + } 1.615 +}; 1.616 + 1.617 +/*****************************************************************************/ 1.618 + 1.619 +ClonedBlockObject * 1.620 +ClonedBlockObject::create(JSContext *cx, Handle<StaticBlockObject *> block, AbstractFramePtr frame) 1.621 +{ 1.622 + assertSameCompartment(cx, frame); 1.623 + JS_ASSERT(block->getClass() == &BlockObject::class_); 1.624 + 1.625 + RootedTypeObject type(cx, cx->getNewType(&BlockObject::class_, block.get())); 1.626 + if (!type) 1.627 + return nullptr; 1.628 + 1.629 + RootedShape shape(cx, block->lastProperty()); 1.630 + 1.631 + RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, gc::TenuredHeap, shape, type)); 1.632 + if (!obj) 1.633 + return nullptr; 1.634 + 1.635 + /* Set the parent if necessary, as for call objects. */ 1.636 + if (&frame.scopeChain()->global() != obj->getParent()) { 1.637 + JS_ASSERT(obj->getParent() == nullptr); 1.638 + Rooted<GlobalObject*> global(cx, &frame.scopeChain()->global()); 1.639 + if (!JSObject::setParent(cx, obj, global)) 1.640 + return nullptr; 1.641 + } 1.642 + 1.643 + JS_ASSERT(!obj->inDictionaryMode()); 1.644 + JS_ASSERT(obj->slotSpan() >= block->numVariables() + RESERVED_SLOTS); 1.645 + 1.646 + obj->setReservedSlot(SCOPE_CHAIN_SLOT, ObjectValue(*frame.scopeChain())); 1.647 + 1.648 + /* 1.649 + * Copy in the closed-over locals. Closed-over locals don't need 1.650 + * any fixup since the initial value is 'undefined'. 1.651 + */ 1.652 + unsigned nvars = block->numVariables(); 1.653 + for (unsigned i = 0; i < nvars; ++i) { 1.654 + if (block->isAliased(i)) { 1.655 + Value &val = frame.unaliasedLocal(block->blockIndexToLocalIndex(i)); 1.656 + obj->as<ClonedBlockObject>().setVar(i, val); 1.657 + } 1.658 + } 1.659 + 1.660 + JS_ASSERT(obj->isDelegate()); 1.661 + 1.662 + return &obj->as<ClonedBlockObject>(); 1.663 +} 1.664 + 1.665 +void 1.666 +ClonedBlockObject::copyUnaliasedValues(AbstractFramePtr frame) 1.667 +{ 1.668 + StaticBlockObject &block = staticBlock(); 1.669 + for (unsigned i = 0; i < numVariables(); ++i) { 1.670 + if (!block.isAliased(i)) { 1.671 + Value &val = frame.unaliasedLocal(block.blockIndexToLocalIndex(i)); 1.672 + setVar(i, val, DONT_CHECK_ALIASING); 1.673 + } 1.674 + } 1.675 +} 1.676 + 1.677 +StaticBlockObject * 1.678 +StaticBlockObject::create(ExclusiveContext *cx) 1.679 +{ 1.680 + RootedTypeObject type(cx, cx->getNewType(&BlockObject::class_, nullptr)); 1.681 + if (!type) 1.682 + return nullptr; 1.683 + 1.684 + RootedShape emptyBlockShape(cx); 1.685 + emptyBlockShape = EmptyShape::getInitialShape(cx, &BlockObject::class_, nullptr, nullptr, 1.686 + nullptr, FINALIZE_KIND, BaseShape::DELEGATE); 1.687 + if (!emptyBlockShape) 1.688 + return nullptr; 1.689 + 1.690 + JSObject *obj = JSObject::create(cx, FINALIZE_KIND, gc::TenuredHeap, emptyBlockShape, type); 1.691 + if (!obj) 1.692 + return nullptr; 1.693 + 1.694 + return &obj->as<StaticBlockObject>(); 1.695 +} 1.696 + 1.697 +/* static */ Shape * 1.698 +StaticBlockObject::addVar(ExclusiveContext *cx, Handle<StaticBlockObject*> block, HandleId id, 1.699 + unsigned index, bool *redeclared) 1.700 +{ 1.701 + JS_ASSERT(JSID_IS_ATOM(id)); 1.702 + JS_ASSERT(index < LOCAL_INDEX_LIMIT); 1.703 + 1.704 + *redeclared = false; 1.705 + 1.706 + /* Inline JSObject::addProperty in order to trap the redefinition case. */ 1.707 + Shape **spp; 1.708 + if (Shape::search(cx, block->lastProperty(), id, &spp, true)) { 1.709 + *redeclared = true; 1.710 + return nullptr; 1.711 + } 1.712 + 1.713 + /* 1.714 + * Don't convert this object to dictionary mode so that we can clone the 1.715 + * block's shape later. 1.716 + */ 1.717 + uint32_t slot = JSSLOT_FREE(&BlockObject::class_) + index; 1.718 + return JSObject::addPropertyInternal<SequentialExecution>(cx, block, id, 1.719 + /* getter = */ nullptr, 1.720 + /* setter = */ nullptr, 1.721 + slot, 1.722 + JSPROP_ENUMERATE | JSPROP_PERMANENT, 1.723 + /* attrs = */ 0, 1.724 + spp, 1.725 + /* allowDictionary = */ false); 1.726 +} 1.727 + 1.728 +const Class BlockObject::class_ = { 1.729 + "Block", 1.730 + JSCLASS_IMPLEMENTS_BARRIERS | 1.731 + JSCLASS_HAS_RESERVED_SLOTS(BlockObject::RESERVED_SLOTS) | 1.732 + JSCLASS_IS_ANONYMOUS, 1.733 + JS_PropertyStub, /* addProperty */ 1.734 + JS_DeletePropertyStub, /* delProperty */ 1.735 + JS_PropertyStub, /* getProperty */ 1.736 + JS_StrictPropertyStub, /* setProperty */ 1.737 + JS_EnumerateStub, 1.738 + JS_ResolveStub, 1.739 + JS_ConvertStub 1.740 +}; 1.741 + 1.742 +template<XDRMode mode> 1.743 +bool 1.744 +js::XDRStaticBlockObject(XDRState<mode> *xdr, HandleObject enclosingScope, 1.745 + StaticBlockObject **objp) 1.746 +{ 1.747 + /* NB: Keep this in sync with CloneStaticBlockObject. */ 1.748 + 1.749 + JSContext *cx = xdr->cx(); 1.750 + 1.751 + Rooted<StaticBlockObject*> obj(cx); 1.752 + uint32_t count = 0, offset = 0; 1.753 + 1.754 + if (mode == XDR_ENCODE) { 1.755 + obj = *objp; 1.756 + count = obj->numVariables(); 1.757 + offset = obj->localOffset(); 1.758 + } 1.759 + 1.760 + if (mode == XDR_DECODE) { 1.761 + obj = StaticBlockObject::create(cx); 1.762 + if (!obj) 1.763 + return false; 1.764 + obj->initEnclosingNestedScope(enclosingScope); 1.765 + *objp = obj; 1.766 + } 1.767 + 1.768 + if (!xdr->codeUint32(&count)) 1.769 + return false; 1.770 + if (!xdr->codeUint32(&offset)) 1.771 + return false; 1.772 + 1.773 + /* 1.774 + * XDR the block object's properties. We know that there are 'count' 1.775 + * properties to XDR, stored as id/aliased pairs. (The empty string as 1.776 + * id indicates an int id.) 1.777 + */ 1.778 + if (mode == XDR_DECODE) { 1.779 + obj->setLocalOffset(offset); 1.780 + 1.781 + for (unsigned i = 0; i < count; i++) { 1.782 + RootedAtom atom(cx); 1.783 + if (!XDRAtom(xdr, &atom)) 1.784 + return false; 1.785 + 1.786 + RootedId id(cx, atom != cx->runtime()->emptyString 1.787 + ? AtomToId(atom) 1.788 + : INT_TO_JSID(i)); 1.789 + 1.790 + bool redeclared; 1.791 + if (!StaticBlockObject::addVar(cx, obj, id, i, &redeclared)) { 1.792 + JS_ASSERT(!redeclared); 1.793 + return false; 1.794 + } 1.795 + 1.796 + uint32_t aliased; 1.797 + if (!xdr->codeUint32(&aliased)) 1.798 + return false; 1.799 + 1.800 + JS_ASSERT(aliased == 0 || aliased == 1); 1.801 + obj->setAliased(i, !!aliased); 1.802 + } 1.803 + } else { 1.804 + AutoShapeVector shapes(cx); 1.805 + if (!shapes.growBy(count)) 1.806 + return false; 1.807 + 1.808 + for (Shape::Range<NoGC> r(obj->lastProperty()); !r.empty(); r.popFront()) 1.809 + shapes[obj->shapeToIndex(r.front())] = &r.front(); 1.810 + 1.811 + RootedShape shape(cx); 1.812 + RootedId propid(cx); 1.813 + RootedAtom atom(cx); 1.814 + for (unsigned i = 0; i < count; i++) { 1.815 + shape = shapes[i]; 1.816 + JS_ASSERT(shape->hasDefaultGetter()); 1.817 + JS_ASSERT(obj->shapeToIndex(*shape) == i); 1.818 + 1.819 + propid = shape->propid(); 1.820 + JS_ASSERT(JSID_IS_ATOM(propid) || JSID_IS_INT(propid)); 1.821 + 1.822 + atom = JSID_IS_ATOM(propid) 1.823 + ? JSID_TO_ATOM(propid) 1.824 + : cx->runtime()->emptyString; 1.825 + if (!XDRAtom(xdr, &atom)) 1.826 + return false; 1.827 + 1.828 + uint32_t aliased = obj->isAliased(i); 1.829 + if (!xdr->codeUint32(&aliased)) 1.830 + return false; 1.831 + } 1.832 + } 1.833 + return true; 1.834 +} 1.835 + 1.836 +template bool 1.837 +js::XDRStaticBlockObject(XDRState<XDR_ENCODE> *, HandleObject, StaticBlockObject **); 1.838 + 1.839 +template bool 1.840 +js::XDRStaticBlockObject(XDRState<XDR_DECODE> *, HandleObject, StaticBlockObject **); 1.841 + 1.842 +static JSObject * 1.843 +CloneStaticBlockObject(JSContext *cx, HandleObject enclosingScope, Handle<StaticBlockObject*> srcBlock) 1.844 +{ 1.845 + /* NB: Keep this in sync with XDRStaticBlockObject. */ 1.846 + 1.847 + Rooted<StaticBlockObject*> clone(cx, StaticBlockObject::create(cx)); 1.848 + if (!clone) 1.849 + return nullptr; 1.850 + 1.851 + clone->initEnclosingNestedScope(enclosingScope); 1.852 + clone->setLocalOffset(srcBlock->localOffset()); 1.853 + 1.854 + /* Shape::Range is reverse order, so build a list in forward order. */ 1.855 + AutoShapeVector shapes(cx); 1.856 + if (!shapes.growBy(srcBlock->numVariables())) 1.857 + return nullptr; 1.858 + 1.859 + for (Shape::Range<NoGC> r(srcBlock->lastProperty()); !r.empty(); r.popFront()) 1.860 + shapes[srcBlock->shapeToIndex(r.front())] = &r.front(); 1.861 + 1.862 + for (Shape **p = shapes.begin(); p != shapes.end(); ++p) { 1.863 + RootedId id(cx, (*p)->propid()); 1.864 + unsigned i = srcBlock->shapeToIndex(**p); 1.865 + 1.866 + bool redeclared; 1.867 + if (!StaticBlockObject::addVar(cx, clone, id, i, &redeclared)) { 1.868 + JS_ASSERT(!redeclared); 1.869 + return nullptr; 1.870 + } 1.871 + 1.872 + clone->setAliased(i, srcBlock->isAliased(i)); 1.873 + } 1.874 + 1.875 + return clone; 1.876 +} 1.877 + 1.878 +JSObject * 1.879 +js::CloneNestedScopeObject(JSContext *cx, HandleObject enclosingScope, Handle<NestedScopeObject*> srcBlock) 1.880 +{ 1.881 + if (srcBlock->is<StaticBlockObject>()) { 1.882 + Rooted<StaticBlockObject *> blockObj(cx, &srcBlock->as<StaticBlockObject>()); 1.883 + return CloneStaticBlockObject(cx, enclosingScope, blockObj); 1.884 + } else { 1.885 + Rooted<StaticWithObject *> withObj(cx, &srcBlock->as<StaticWithObject>()); 1.886 + return CloneStaticWithObject(cx, enclosingScope, withObj); 1.887 + } 1.888 +} 1.889 + 1.890 +/*****************************************************************************/ 1.891 + 1.892 +// Any name atom for a function which will be added as a DeclEnv object to the 1.893 +// scope chain above call objects for fun. 1.894 +static inline JSAtom * 1.895 +CallObjectLambdaName(JSFunction &fun) 1.896 +{ 1.897 + return fun.isNamedLambda() ? fun.atom() : nullptr; 1.898 +} 1.899 + 1.900 +ScopeIter::ScopeIter(const ScopeIter &si, JSContext *cx 1.901 + MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) 1.902 + : cx(cx), 1.903 + frame_(si.frame_), 1.904 + cur_(cx, si.cur_), 1.905 + staticScope_(cx, si.staticScope_), 1.906 + type_(si.type_), 1.907 + hasScopeObject_(si.hasScopeObject_) 1.908 +{ 1.909 + MOZ_GUARD_OBJECT_NOTIFIER_INIT; 1.910 +} 1.911 + 1.912 +ScopeIter::ScopeIter(JSObject &enclosingScope, JSContext *cx 1.913 + MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) 1.914 + : cx(cx), 1.915 + frame_(NullFramePtr()), 1.916 + cur_(cx, &enclosingScope), 1.917 + staticScope_(cx, nullptr), 1.918 + type_(Type(-1)) 1.919 +{ 1.920 + MOZ_GUARD_OBJECT_NOTIFIER_INIT; 1.921 +} 1.922 + 1.923 +ScopeIter::ScopeIter(AbstractFramePtr frame, jsbytecode *pc, JSContext *cx 1.924 + MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) 1.925 + : cx(cx), 1.926 + frame_(frame), 1.927 + cur_(cx, frame.scopeChain()), 1.928 + staticScope_(cx, frame.script()->getStaticScope(pc)) 1.929 +{ 1.930 + assertSameCompartment(cx, frame); 1.931 + settle(); 1.932 + MOZ_GUARD_OBJECT_NOTIFIER_INIT; 1.933 +} 1.934 + 1.935 +ScopeIter::ScopeIter(const ScopeIterVal &val, JSContext *cx 1.936 + MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) 1.937 + : cx(cx), 1.938 + frame_(val.frame_), 1.939 + cur_(cx, val.cur_), 1.940 + staticScope_(cx, val.staticScope_), 1.941 + type_(val.type_), 1.942 + hasScopeObject_(val.hasScopeObject_) 1.943 +{ 1.944 + assertSameCompartment(cx, val.frame_); 1.945 + MOZ_GUARD_OBJECT_NOTIFIER_INIT; 1.946 +} 1.947 + 1.948 +ScopeObject & 1.949 +ScopeIter::scope() const 1.950 +{ 1.951 + JS_ASSERT(hasScopeObject()); 1.952 + return cur_->as<ScopeObject>(); 1.953 +} 1.954 + 1.955 +ScopeIter & 1.956 +ScopeIter::operator++() 1.957 +{ 1.958 + JS_ASSERT(!done()); 1.959 + switch (type_) { 1.960 + case Call: 1.961 + if (hasScopeObject_) { 1.962 + cur_ = &cur_->as<CallObject>().enclosingScope(); 1.963 + if (CallObjectLambdaName(*frame_.fun())) 1.964 + cur_ = &cur_->as<DeclEnvObject>().enclosingScope(); 1.965 + } 1.966 + frame_ = NullFramePtr(); 1.967 + break; 1.968 + case Block: 1.969 + JS_ASSERT(staticScope_ && staticScope_->is<StaticBlockObject>()); 1.970 + staticScope_ = staticScope_->as<StaticBlockObject>().enclosingNestedScope(); 1.971 + if (hasScopeObject_) 1.972 + cur_ = &cur_->as<ClonedBlockObject>().enclosingScope(); 1.973 + settle(); 1.974 + break; 1.975 + case With: 1.976 + JS_ASSERT(staticScope_ && staticScope_->is<StaticWithObject>()); 1.977 + JS_ASSERT(hasScopeObject_); 1.978 + staticScope_ = staticScope_->as<StaticWithObject>().enclosingNestedScope(); 1.979 + cur_ = &cur_->as<DynamicWithObject>().enclosingScope(); 1.980 + settle(); 1.981 + break; 1.982 + case StrictEvalScope: 1.983 + if (hasScopeObject_) 1.984 + cur_ = &cur_->as<CallObject>().enclosingScope(); 1.985 + frame_ = NullFramePtr(); 1.986 + break; 1.987 + } 1.988 + return *this; 1.989 +} 1.990 + 1.991 +void 1.992 +ScopeIter::settle() 1.993 +{ 1.994 + /* 1.995 + * Given an iterator state (cur_, staticScope_), figure out which (potentially 1.996 + * optimized) scope the iterator should report. Thus, the result is a pair 1.997 + * (type_, hasScopeObject_) where hasScopeObject_ indicates whether the 1.998 + * scope object has been optimized away and does not exist on the scope 1.999 + * chain. Beware: while ScopeIter iterates over the scopes of a single 1.1000 + * frame, the scope chain (pointed to by cur_) continues into the scopes of 1.1001 + * enclosing frames. Thus, it is important not to look at cur_ until it is 1.1002 + * certain that cur_ points to a scope object in the current frame. In 1.1003 + * particular, there are three tricky corner cases: 1.1004 + * - non-heavyweight functions; 1.1005 + * - non-strict direct eval. 1.1006 + * - heavyweight functions observed before the prologue has finished; 1.1007 + * In all cases, cur_ can already be pointing into an enclosing frame's 1.1008 + * scope chain. Furthermore, in the first two cases: even if cur_ points 1.1009 + * into an enclosing frame's scope chain, the current frame may still have 1.1010 + * uncloned blocks. In the last case, since we haven't entered the 1.1011 + * function, we simply return a ScopeIter where done() == true. 1.1012 + * 1.1013 + * Note: DebugScopeObject falls nicely into this plan: since they are only 1.1014 + * ever introduced as the *enclosing* scope of a frame, they should never 1.1015 + * show up in scope iteration and fall into the final non-scope case. 1.1016 + */ 1.1017 + if (frame_.isNonEvalFunctionFrame() && !frame_.fun()->isHeavyweight()) { 1.1018 + if (staticScope_) { 1.1019 + // If staticScope_ were a StaticWithObject, the function would be 1.1020 + // heavyweight. 1.1021 + JS_ASSERT(staticScope_->is<StaticBlockObject>()); 1.1022 + type_ = Block; 1.1023 + hasScopeObject_ = staticScope_->as<StaticBlockObject>().needsClone(); 1.1024 + } else { 1.1025 + type_ = Call; 1.1026 + hasScopeObject_ = false; 1.1027 + } 1.1028 + } else if (frame_.isNonStrictDirectEvalFrame() && cur_ == frame_.evalPrevScopeChain(cx)) { 1.1029 + if (staticScope_) { 1.1030 + JS_ASSERT(staticScope_->is<StaticBlockObject>()); 1.1031 + JS_ASSERT(!staticScope_->as<StaticBlockObject>().needsClone()); 1.1032 + type_ = Block; 1.1033 + hasScopeObject_ = false; 1.1034 + } else { 1.1035 + frame_ = NullFramePtr(); 1.1036 + } 1.1037 + } else if (frame_.isNonEvalFunctionFrame() && !frame_.hasCallObj()) { 1.1038 + JS_ASSERT(cur_ == frame_.fun()->environment()); 1.1039 + frame_ = NullFramePtr(); 1.1040 + } else if (frame_.isStrictEvalFrame() && !frame_.hasCallObj()) { 1.1041 + JS_ASSERT(cur_ == frame_.evalPrevScopeChain(cx)); 1.1042 + frame_ = NullFramePtr(); 1.1043 + } else if (staticScope_) { 1.1044 + if (staticScope_->is<StaticWithObject>()) { 1.1045 + JS_ASSERT(cur_); 1.1046 + JS_ASSERT(cur_->as<DynamicWithObject>().staticScope() == staticScope_); 1.1047 + type_ = With; 1.1048 + hasScopeObject_ = true; 1.1049 + } else { 1.1050 + type_ = Block; 1.1051 + hasScopeObject_ = staticScope_->as<StaticBlockObject>().needsClone(); 1.1052 + JS_ASSERT_IF(hasScopeObject_, 1.1053 + cur_->as<ClonedBlockObject>().staticBlock() == *staticScope_); 1.1054 + } 1.1055 + } else if (cur_->is<CallObject>()) { 1.1056 + CallObject &callobj = cur_->as<CallObject>(); 1.1057 + type_ = callobj.isForEval() ? StrictEvalScope : Call; 1.1058 + hasScopeObject_ = true; 1.1059 + JS_ASSERT_IF(type_ == Call, callobj.callee().nonLazyScript() == frame_.script()); 1.1060 + } else { 1.1061 + JS_ASSERT(!cur_->is<ScopeObject>()); 1.1062 + JS_ASSERT(frame_.isGlobalFrame() || frame_.isDebuggerFrame()); 1.1063 + frame_ = NullFramePtr(); 1.1064 + } 1.1065 +} 1.1066 + 1.1067 +/* static */ HashNumber 1.1068 +ScopeIterKey::hash(ScopeIterKey si) 1.1069 +{ 1.1070 + /* hasScopeObject_ is determined by the other fields. */ 1.1071 + return size_t(si.frame_.raw()) ^ size_t(si.cur_) ^ size_t(si.staticScope_) ^ si.type_; 1.1072 +} 1.1073 + 1.1074 +/* static */ bool 1.1075 +ScopeIterKey::match(ScopeIterKey si1, ScopeIterKey si2) 1.1076 +{ 1.1077 + /* hasScopeObject_ is determined by the other fields. */ 1.1078 + return si1.frame_ == si2.frame_ && 1.1079 + (!si1.frame_ || 1.1080 + (si1.cur_ == si2.cur_ && 1.1081 + si1.staticScope_ == si2.staticScope_ && 1.1082 + si1.type_ == si2.type_)); 1.1083 +} 1.1084 + 1.1085 +// Live ScopeIter values may be added to DebugScopes::liveScopes, as 1.1086 +// ScopeIterVal instances. They need to have write barriers when they are added 1.1087 +// to the hash table, but no barriers when rehashing inside GC. It's a nasty 1.1088 +// hack, but the important thing is that ScopeIterKey and ScopeIterVal need to 1.1089 +// alias each other. 1.1090 +void ScopeIterVal::staticAsserts() { 1.1091 + static_assert(sizeof(ScopeIterVal) == sizeof(ScopeIterKey), 1.1092 + "ScopeIterVal must be same size of ScopeIterKey"); 1.1093 + static_assert(offsetof(ScopeIterVal, cur_) == offsetof(ScopeIterKey, cur_), 1.1094 + "ScopeIterVal.cur_ must alias ScopeIterKey.cur_"); 1.1095 + static_assert(offsetof(ScopeIterVal, staticScope_) == offsetof(ScopeIterKey, staticScope_), 1.1096 + "ScopeIterVal.staticScope_ must alias ScopeIterKey.staticScope_"); 1.1097 +} 1.1098 + 1.1099 +/*****************************************************************************/ 1.1100 + 1.1101 +namespace { 1.1102 + 1.1103 +/* 1.1104 + * DebugScopeProxy is the handler for DebugScopeObject proxy objects. Having a 1.1105 + * custom handler (rather than trying to reuse js::Wrapper) gives us several 1.1106 + * important abilities: 1.1107 + * - We want to pass the ScopeObject as the receiver to forwarded scope 1.1108 + * property ops on aliased variables so that Call/Block/With ops do not all 1.1109 + * require a 'normalization' step. 1.1110 + * - The debug scope proxy can directly manipulate the stack frame to allow 1.1111 + * the debugger to read/write args/locals that were otherwise unaliased. 1.1112 + * - The debug scope proxy can store unaliased variables after the stack frame 1.1113 + * is popped so that they may still be read/written by the debugger. 1.1114 + * - The engine has made certain assumptions about the possible reads/writes 1.1115 + * in a scope. DebugScopeProxy allows us to prevent the debugger from 1.1116 + * breaking those assumptions. 1.1117 + * - The engine makes optimizations that are observable to the debugger. The 1.1118 + * proxy can either hide these optimizations or make the situation more 1.1119 + * clear to the debugger. An example is 'arguments'. 1.1120 + */ 1.1121 +class DebugScopeProxy : public BaseProxyHandler 1.1122 +{ 1.1123 + enum Action { SET, GET }; 1.1124 + 1.1125 + enum AccessResult { 1.1126 + ACCESS_UNALIASED, 1.1127 + ACCESS_GENERIC, 1.1128 + ACCESS_LOST 1.1129 + }; 1.1130 + 1.1131 + /* 1.1132 + * This function handles access to unaliased locals/formals. Since they are 1.1133 + * unaliased, the values of these variables are not stored in the slots of 1.1134 + * the normal Call/BlockObject scope objects and thus must be recovered 1.1135 + * from somewhere else: 1.1136 + * + if the invocation for which the scope was created is still executing, 1.1137 + * there is a JS frame live on the stack holding the values; 1.1138 + * + if the invocation for which the scope was created finished executing: 1.1139 + * - and there was a DebugScopeObject associated with scope, then the 1.1140 + * DebugScopes::onPop(Call|Block) handler copied out the unaliased 1.1141 + * variables: 1.1142 + * . for block scopes, the unaliased values were copied directly 1.1143 + * into the block object, since there is a slot allocated for every 1.1144 + * block binding, regardless of whether it is aliased; 1.1145 + * . for function scopes, a dense array is created in onPopCall to hold 1.1146 + * the unaliased values and attached to the DebugScopeObject; 1.1147 + * - and there was not a DebugScopeObject yet associated with the 1.1148 + * scope, then the unaliased values are lost and not recoverable. 1.1149 + * 1.1150 + * Callers should check accessResult for non-failure results: 1.1151 + * - ACCESS_UNALIASED if the access was unaliased and completed 1.1152 + * - ACCESS_GENERIC if the access was aliased or the property not found 1.1153 + * - ACCESS_LOST if the value has been lost to the debugger 1.1154 + */ 1.1155 + bool handleUnaliasedAccess(JSContext *cx, Handle<DebugScopeObject*> debugScope, 1.1156 + Handle<ScopeObject*> scope, jsid id, Action action, 1.1157 + MutableHandleValue vp, AccessResult *accessResult) 1.1158 + { 1.1159 + JS_ASSERT(&debugScope->scope() == scope); 1.1160 + *accessResult = ACCESS_GENERIC; 1.1161 + ScopeIterVal *maybeLiveScope = DebugScopes::hasLiveScope(*scope); 1.1162 + 1.1163 + /* Handle unaliased formals, vars, and consts at function scope. */ 1.1164 + if (scope->is<CallObject>() && !scope->as<CallObject>().isForEval()) { 1.1165 + CallObject &callobj = scope->as<CallObject>(); 1.1166 + RootedScript script(cx, callobj.callee().nonLazyScript()); 1.1167 + if (!script->ensureHasTypes(cx) || !script->ensureHasAnalyzedArgsUsage(cx)) 1.1168 + return false; 1.1169 + 1.1170 + Bindings &bindings = script->bindings; 1.1171 + BindingIter bi(script); 1.1172 + while (bi && NameToId(bi->name()) != id) 1.1173 + bi++; 1.1174 + if (!bi) 1.1175 + return true; 1.1176 + 1.1177 + if (bi->kind() == Binding::VARIABLE || bi->kind() == Binding::CONSTANT) { 1.1178 + uint32_t i = bi.frameIndex(); 1.1179 + if (script->varIsAliased(i)) 1.1180 + return true; 1.1181 + 1.1182 + if (maybeLiveScope) { 1.1183 + AbstractFramePtr frame = maybeLiveScope->frame(); 1.1184 + if (action == GET) 1.1185 + vp.set(frame.unaliasedVar(i)); 1.1186 + else 1.1187 + frame.unaliasedVar(i) = vp; 1.1188 + } else if (JSObject *snapshot = debugScope->maybeSnapshot()) { 1.1189 + if (action == GET) 1.1190 + vp.set(snapshot->getDenseElement(bindings.numArgs() + i)); 1.1191 + else 1.1192 + snapshot->setDenseElement(bindings.numArgs() + i, vp); 1.1193 + } else { 1.1194 + /* The unaliased value has been lost to the debugger. */ 1.1195 + if (action == GET) { 1.1196 + *accessResult = ACCESS_LOST; 1.1197 + return true; 1.1198 + } 1.1199 + } 1.1200 + } else { 1.1201 + JS_ASSERT(bi->kind() == Binding::ARGUMENT); 1.1202 + unsigned i = bi.frameIndex(); 1.1203 + if (script->formalIsAliased(i)) 1.1204 + return true; 1.1205 + 1.1206 + if (maybeLiveScope) { 1.1207 + AbstractFramePtr frame = maybeLiveScope->frame(); 1.1208 + if (script->argsObjAliasesFormals() && frame.hasArgsObj()) { 1.1209 + if (action == GET) 1.1210 + vp.set(frame.argsObj().arg(i)); 1.1211 + else 1.1212 + frame.argsObj().setArg(i, vp); 1.1213 + } else { 1.1214 + if (action == GET) 1.1215 + vp.set(frame.unaliasedFormal(i, DONT_CHECK_ALIASING)); 1.1216 + else 1.1217 + frame.unaliasedFormal(i, DONT_CHECK_ALIASING) = vp; 1.1218 + } 1.1219 + } else if (JSObject *snapshot = debugScope->maybeSnapshot()) { 1.1220 + if (action == GET) 1.1221 + vp.set(snapshot->getDenseElement(i)); 1.1222 + else 1.1223 + snapshot->setDenseElement(i, vp); 1.1224 + } else { 1.1225 + /* The unaliased value has been lost to the debugger. */ 1.1226 + if (action == GET) { 1.1227 + *accessResult = ACCESS_LOST; 1.1228 + return true; 1.1229 + } 1.1230 + } 1.1231 + 1.1232 + if (action == SET) 1.1233 + TypeScript::SetArgument(cx, script, i, vp); 1.1234 + } 1.1235 + 1.1236 + *accessResult = ACCESS_UNALIASED; 1.1237 + return true; 1.1238 + } 1.1239 + 1.1240 + /* Handle unaliased let and catch bindings at block scope. */ 1.1241 + if (scope->is<ClonedBlockObject>()) { 1.1242 + Rooted<ClonedBlockObject *> block(cx, &scope->as<ClonedBlockObject>()); 1.1243 + Shape *shape = block->lastProperty()->search(cx, id); 1.1244 + if (!shape) 1.1245 + return true; 1.1246 + 1.1247 + unsigned i = block->staticBlock().shapeToIndex(*shape); 1.1248 + if (block->staticBlock().isAliased(i)) 1.1249 + return true; 1.1250 + 1.1251 + if (maybeLiveScope) { 1.1252 + AbstractFramePtr frame = maybeLiveScope->frame(); 1.1253 + uint32_t local = block->staticBlock().blockIndexToLocalIndex(i); 1.1254 + JS_ASSERT(local < frame.script()->nfixed()); 1.1255 + if (action == GET) 1.1256 + vp.set(frame.unaliasedLocal(local)); 1.1257 + else 1.1258 + frame.unaliasedLocal(local) = vp; 1.1259 + } else { 1.1260 + if (action == GET) 1.1261 + vp.set(block->var(i, DONT_CHECK_ALIASING)); 1.1262 + else 1.1263 + block->setVar(i, vp, DONT_CHECK_ALIASING); 1.1264 + } 1.1265 + 1.1266 + *accessResult = ACCESS_UNALIASED; 1.1267 + return true; 1.1268 + } 1.1269 + 1.1270 + /* The rest of the internal scopes do not have unaliased vars. */ 1.1271 + JS_ASSERT(scope->is<DeclEnvObject>() || scope->is<DynamicWithObject>() || 1.1272 + scope->as<CallObject>().isForEval()); 1.1273 + return true; 1.1274 + } 1.1275 + 1.1276 + static bool isArguments(JSContext *cx, jsid id) 1.1277 + { 1.1278 + return id == NameToId(cx->names().arguments); 1.1279 + } 1.1280 + 1.1281 + static bool isFunctionScope(ScopeObject &scope) 1.1282 + { 1.1283 + return scope.is<CallObject>() && !scope.as<CallObject>().isForEval(); 1.1284 + } 1.1285 + 1.1286 + /* 1.1287 + * In theory, every function scope contains an 'arguments' bindings. 1.1288 + * However, the engine only adds a binding if 'arguments' is used in the 1.1289 + * function body. Thus, from the debugger's perspective, 'arguments' may be 1.1290 + * missing from the list of bindings. 1.1291 + */ 1.1292 + static bool isMissingArgumentsBinding(ScopeObject &scope) 1.1293 + { 1.1294 + return isFunctionScope(scope) && 1.1295 + !scope.as<CallObject>().callee().nonLazyScript()->argumentsHasVarBinding(); 1.1296 + } 1.1297 + 1.1298 + /* 1.1299 + * This function checks if an arguments object needs to be created when 1.1300 + * the debugger requests 'arguments' for a function scope where the 1.1301 + * arguments object has been optimized away (either because the binding is 1.1302 + * missing altogether or because !ScriptAnalysis::needsArgsObj). 1.1303 + */ 1.1304 + static bool isMissingArguments(JSContext *cx, jsid id, ScopeObject &scope) 1.1305 + { 1.1306 + return isArguments(cx, id) && isFunctionScope(scope) && 1.1307 + !scope.as<CallObject>().callee().nonLazyScript()->needsArgsObj(); 1.1308 + } 1.1309 + 1.1310 + /* 1.1311 + * Create a missing arguments object. If the function returns true but 1.1312 + * argsObj is null, it means the scope is dead. 1.1313 + */ 1.1314 + static bool createMissingArguments(JSContext *cx, jsid id, ScopeObject &scope, 1.1315 + MutableHandleArgumentsObject argsObj) 1.1316 + { 1.1317 + MOZ_ASSERT(isMissingArguments(cx, id, scope)); 1.1318 + argsObj.set(nullptr); 1.1319 + 1.1320 + ScopeIterVal *maybeScope = DebugScopes::hasLiveScope(scope); 1.1321 + if (!maybeScope) 1.1322 + return true; 1.1323 + 1.1324 + argsObj.set(ArgumentsObject::createUnexpected(cx, maybeScope->frame())); 1.1325 + return !!argsObj; 1.1326 + } 1.1327 + 1.1328 + public: 1.1329 + static int family; 1.1330 + static DebugScopeProxy singleton; 1.1331 + 1.1332 + DebugScopeProxy() : BaseProxyHandler(&family) {} 1.1333 + 1.1334 + bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) MOZ_OVERRIDE 1.1335 + { 1.1336 + // always [[Extensible]], can't be made non-[[Extensible]], like most 1.1337 + // proxies 1.1338 + *extensible = true; 1.1339 + return true; 1.1340 + } 1.1341 + 1.1342 + bool preventExtensions(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE 1.1343 + { 1.1344 + // See above. 1.1345 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CHANGE_EXTENSIBILITY); 1.1346 + return false; 1.1347 + } 1.1348 + 1.1349 + bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, 1.1350 + MutableHandle<PropertyDescriptor> desc) MOZ_OVERRIDE 1.1351 + { 1.1352 + return getOwnPropertyDescriptor(cx, proxy, id, desc); 1.1353 + } 1.1354 + 1.1355 + bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, 1.1356 + MutableHandle<PropertyDescriptor> desc) MOZ_OVERRIDE 1.1357 + { 1.1358 + Rooted<DebugScopeObject*> debugScope(cx, &proxy->as<DebugScopeObject>()); 1.1359 + Rooted<ScopeObject*> scope(cx, &debugScope->scope()); 1.1360 + 1.1361 + if (isMissingArguments(cx, id, *scope)) { 1.1362 + RootedArgumentsObject argsObj(cx); 1.1363 + if (!createMissingArguments(cx, id, *scope, &argsObj)) 1.1364 + return false; 1.1365 + 1.1366 + if (!argsObj) { 1.1367 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_LIVE, 1.1368 + "Debugger scope"); 1.1369 + return false; 1.1370 + } 1.1371 + 1.1372 + desc.object().set(debugScope); 1.1373 + desc.setAttributes(JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT); 1.1374 + desc.value().setObject(*argsObj); 1.1375 + desc.setGetter(nullptr); 1.1376 + desc.setSetter(nullptr); 1.1377 + return true; 1.1378 + } 1.1379 + 1.1380 + RootedValue v(cx); 1.1381 + AccessResult access; 1.1382 + if (!handleUnaliasedAccess(cx, debugScope, scope, id, GET, &v, &access)) 1.1383 + return false; 1.1384 + 1.1385 + switch (access) { 1.1386 + case ACCESS_UNALIASED: 1.1387 + desc.object().set(debugScope); 1.1388 + desc.setAttributes(JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT); 1.1389 + desc.value().set(v); 1.1390 + desc.setGetter(nullptr); 1.1391 + desc.setSetter(nullptr); 1.1392 + return true; 1.1393 + case ACCESS_GENERIC: 1.1394 + return JS_GetOwnPropertyDescriptorById(cx, scope, id, desc); 1.1395 + case ACCESS_LOST: 1.1396 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_OPTIMIZED_OUT); 1.1397 + return false; 1.1398 + default: 1.1399 + MOZ_ASSUME_UNREACHABLE("bad AccessResult"); 1.1400 + } 1.1401 + } 1.1402 + 1.1403 + bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, 1.1404 + MutableHandleValue vp) MOZ_OVERRIDE 1.1405 + { 1.1406 + Rooted<DebugScopeObject*> debugScope(cx, &proxy->as<DebugScopeObject>()); 1.1407 + Rooted<ScopeObject*> scope(cx, &proxy->as<DebugScopeObject>().scope()); 1.1408 + 1.1409 + if (isMissingArguments(cx, id, *scope)) { 1.1410 + RootedArgumentsObject argsObj(cx); 1.1411 + if (!createMissingArguments(cx, id, *scope, &argsObj)) 1.1412 + return false; 1.1413 + 1.1414 + if (!argsObj) { 1.1415 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_LIVE, 1.1416 + "Debugger scope"); 1.1417 + return false; 1.1418 + } 1.1419 + 1.1420 + vp.setObject(*argsObj); 1.1421 + return true; 1.1422 + } 1.1423 + 1.1424 + AccessResult access; 1.1425 + if (!handleUnaliasedAccess(cx, debugScope, scope, id, GET, vp, &access)) 1.1426 + return false; 1.1427 + 1.1428 + switch (access) { 1.1429 + case ACCESS_UNALIASED: 1.1430 + return true; 1.1431 + case ACCESS_GENERIC: 1.1432 + return JSObject::getGeneric(cx, scope, scope, id, vp); 1.1433 + case ACCESS_LOST: 1.1434 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_OPTIMIZED_OUT); 1.1435 + return false; 1.1436 + default: 1.1437 + MOZ_ASSUME_UNREACHABLE("bad AccessResult"); 1.1438 + } 1.1439 + } 1.1440 + 1.1441 + /* 1.1442 + * Like 'get', but returns sentinel values instead of throwing on 1.1443 + * exceptional cases. 1.1444 + */ 1.1445 + bool getMaybeSentinelValue(JSContext *cx, Handle<DebugScopeObject *> debugScope, HandleId id, 1.1446 + MutableHandleValue vp) 1.1447 + { 1.1448 + Rooted<ScopeObject*> scope(cx, &debugScope->scope()); 1.1449 + 1.1450 + if (isMissingArguments(cx, id, *scope)) { 1.1451 + RootedArgumentsObject argsObj(cx); 1.1452 + if (!createMissingArguments(cx, id, *scope, &argsObj)) 1.1453 + return false; 1.1454 + vp.set(argsObj ? ObjectValue(*argsObj) : MagicValue(JS_OPTIMIZED_ARGUMENTS)); 1.1455 + return true; 1.1456 + } 1.1457 + 1.1458 + AccessResult access; 1.1459 + if (!handleUnaliasedAccess(cx, debugScope, scope, id, GET, vp, &access)) 1.1460 + return false; 1.1461 + 1.1462 + switch (access) { 1.1463 + case ACCESS_UNALIASED: 1.1464 + return true; 1.1465 + case ACCESS_GENERIC: 1.1466 + return JSObject::getGeneric(cx, scope, scope, id, vp); 1.1467 + case ACCESS_LOST: 1.1468 + vp.setMagic(JS_OPTIMIZED_OUT); 1.1469 + return true; 1.1470 + default: 1.1471 + MOZ_ASSUME_UNREACHABLE("bad AccessResult"); 1.1472 + } 1.1473 + } 1.1474 + 1.1475 + bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, bool strict, 1.1476 + MutableHandleValue vp) MOZ_OVERRIDE 1.1477 + { 1.1478 + Rooted<DebugScopeObject*> debugScope(cx, &proxy->as<DebugScopeObject>()); 1.1479 + Rooted<ScopeObject*> scope(cx, &proxy->as<DebugScopeObject>().scope()); 1.1480 + 1.1481 + AccessResult access; 1.1482 + if (!handleUnaliasedAccess(cx, debugScope, scope, id, SET, vp, &access)) 1.1483 + return false; 1.1484 + 1.1485 + switch (access) { 1.1486 + case ACCESS_UNALIASED: 1.1487 + return true; 1.1488 + case ACCESS_GENERIC: 1.1489 + return JSObject::setGeneric(cx, scope, scope, id, vp, strict); 1.1490 + default: 1.1491 + MOZ_ASSUME_UNREACHABLE("bad AccessResult"); 1.1492 + } 1.1493 + } 1.1494 + 1.1495 + bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id, 1.1496 + MutableHandle<PropertyDescriptor> desc) MOZ_OVERRIDE 1.1497 + { 1.1498 + Rooted<ScopeObject*> scope(cx, &proxy->as<DebugScopeObject>().scope()); 1.1499 + 1.1500 + bool found; 1.1501 + if (!has(cx, proxy, id, &found)) 1.1502 + return false; 1.1503 + if (found) 1.1504 + return Throw(cx, id, JSMSG_CANT_REDEFINE_PROP); 1.1505 + 1.1506 + return JS_DefinePropertyById(cx, scope, id, desc.value(), desc.getter(), desc.setter(), 1.1507 + desc.attributes()); 1.1508 + } 1.1509 + 1.1510 + bool getScopePropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props, 1.1511 + unsigned flags) 1.1512 + { 1.1513 + Rooted<ScopeObject*> scope(cx, &proxy->as<DebugScopeObject>().scope()); 1.1514 + 1.1515 + if (isMissingArgumentsBinding(*scope)) { 1.1516 + if (!props.append(NameToId(cx->names().arguments))) 1.1517 + return false; 1.1518 + } 1.1519 + 1.1520 + // DynamicWithObject isn't a very good proxy. It doesn't have a 1.1521 + // JSNewEnumerateOp implementation, because if it just delegated to the 1.1522 + // target object, the object would indicate that native enumeration is 1.1523 + // the thing to do, but native enumeration over the DynamicWithObject 1.1524 + // wrapper yields no properties. So instead here we hack around the 1.1525 + // issue, and punch a hole through to the with object target. 1.1526 + Rooted<JSObject*> target(cx, (scope->is<DynamicWithObject>() 1.1527 + ? &scope->as<DynamicWithObject>().object() : scope)); 1.1528 + if (!GetPropertyNames(cx, target, flags, &props)) 1.1529 + return false; 1.1530 + 1.1531 + /* 1.1532 + * Function scopes are optimized to not contain unaliased variables so 1.1533 + * they must be manually appended here. 1.1534 + */ 1.1535 + if (scope->is<CallObject>() && !scope->as<CallObject>().isForEval()) { 1.1536 + RootedScript script(cx, scope->as<CallObject>().callee().nonLazyScript()); 1.1537 + for (BindingIter bi(script); bi; bi++) { 1.1538 + if (!bi->aliased() && !props.append(NameToId(bi->name()))) 1.1539 + return false; 1.1540 + } 1.1541 + } 1.1542 + 1.1543 + return true; 1.1544 + } 1.1545 + 1.1546 + bool getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props) MOZ_OVERRIDE 1.1547 + { 1.1548 + return getScopePropertyNames(cx, proxy, props, JSITER_OWNONLY); 1.1549 + } 1.1550 + 1.1551 + bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) MOZ_OVERRIDE 1.1552 + { 1.1553 + return getScopePropertyNames(cx, proxy, props, 0); 1.1554 + } 1.1555 + 1.1556 + bool has(JSContext *cx, HandleObject proxy, HandleId id_, bool *bp) MOZ_OVERRIDE 1.1557 + { 1.1558 + RootedId id(cx, id_); 1.1559 + ScopeObject &scopeObj = proxy->as<DebugScopeObject>().scope(); 1.1560 + 1.1561 + if (isArguments(cx, id) && isFunctionScope(scopeObj)) { 1.1562 + *bp = true; 1.1563 + return true; 1.1564 + } 1.1565 + 1.1566 + bool found; 1.1567 + RootedObject scope(cx, &scopeObj); 1.1568 + if (!JS_HasPropertyById(cx, scope, id, &found)) 1.1569 + return false; 1.1570 + 1.1571 + /* 1.1572 + * Function scopes are optimized to not contain unaliased variables so 1.1573 + * a manual search is necessary. 1.1574 + */ 1.1575 + if (!found && scope->is<CallObject>() && !scope->as<CallObject>().isForEval()) { 1.1576 + RootedScript script(cx, scope->as<CallObject>().callee().nonLazyScript()); 1.1577 + for (BindingIter bi(script); bi; bi++) { 1.1578 + if (!bi->aliased() && NameToId(bi->name()) == id) { 1.1579 + found = true; 1.1580 + break; 1.1581 + } 1.1582 + } 1.1583 + } 1.1584 + 1.1585 + *bp = found; 1.1586 + return true; 1.1587 + } 1.1588 + 1.1589 + bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE 1.1590 + { 1.1591 + RootedValue idval(cx, IdToValue(id)); 1.1592 + return js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_CANT_DELETE, 1.1593 + JSDVG_IGNORE_STACK, idval, NullPtr(), nullptr, nullptr); 1.1594 + } 1.1595 +}; 1.1596 + 1.1597 +} /* anonymous namespace */ 1.1598 + 1.1599 +int DebugScopeProxy::family = 0; 1.1600 +DebugScopeProxy DebugScopeProxy::singleton; 1.1601 + 1.1602 +/* static */ DebugScopeObject * 1.1603 +DebugScopeObject::create(JSContext *cx, ScopeObject &scope, HandleObject enclosing) 1.1604 +{ 1.1605 + JS_ASSERT(scope.compartment() == cx->compartment()); 1.1606 + RootedValue priv(cx, ObjectValue(scope)); 1.1607 + JSObject *obj = NewProxyObject(cx, &DebugScopeProxy::singleton, priv, 1.1608 + nullptr /* proto */, &scope.global()); 1.1609 + if (!obj) 1.1610 + return nullptr; 1.1611 + 1.1612 + JS_ASSERT(!enclosing->is<ScopeObject>()); 1.1613 + 1.1614 + DebugScopeObject *debugScope = &obj->as<DebugScopeObject>(); 1.1615 + debugScope->setExtra(ENCLOSING_EXTRA, ObjectValue(*enclosing)); 1.1616 + debugScope->setExtra(SNAPSHOT_EXTRA, NullValue()); 1.1617 + 1.1618 + return debugScope; 1.1619 +} 1.1620 + 1.1621 +ScopeObject & 1.1622 +DebugScopeObject::scope() const 1.1623 +{ 1.1624 + return target()->as<ScopeObject>(); 1.1625 +} 1.1626 + 1.1627 +JSObject & 1.1628 +DebugScopeObject::enclosingScope() const 1.1629 +{ 1.1630 + return extra(ENCLOSING_EXTRA).toObject(); 1.1631 +} 1.1632 + 1.1633 +JSObject * 1.1634 +DebugScopeObject::maybeSnapshot() const 1.1635 +{ 1.1636 + JS_ASSERT(!scope().as<CallObject>().isForEval()); 1.1637 + return extra(SNAPSHOT_EXTRA).toObjectOrNull(); 1.1638 +} 1.1639 + 1.1640 +void 1.1641 +DebugScopeObject::initSnapshot(JSObject &o) 1.1642 +{ 1.1643 + JS_ASSERT(maybeSnapshot() == nullptr); 1.1644 + setExtra(SNAPSHOT_EXTRA, ObjectValue(o)); 1.1645 +} 1.1646 + 1.1647 +bool 1.1648 +DebugScopeObject::isForDeclarative() const 1.1649 +{ 1.1650 + ScopeObject &s = scope(); 1.1651 + return s.is<CallObject>() || s.is<BlockObject>() || s.is<DeclEnvObject>(); 1.1652 +} 1.1653 + 1.1654 +bool 1.1655 +DebugScopeObject::getMaybeSentinelValue(JSContext *cx, HandleId id, MutableHandleValue vp) 1.1656 +{ 1.1657 + Rooted<DebugScopeObject *> self(cx, this); 1.1658 + return DebugScopeProxy::singleton.getMaybeSentinelValue(cx, self, id, vp); 1.1659 +} 1.1660 + 1.1661 +bool 1.1662 +js_IsDebugScopeSlow(ProxyObject *proxy) 1.1663 +{ 1.1664 + JS_ASSERT(proxy->hasClass(&ProxyObject::uncallableClass_)); 1.1665 + return proxy->handler() == &DebugScopeProxy::singleton; 1.1666 +} 1.1667 + 1.1668 +/*****************************************************************************/ 1.1669 + 1.1670 +/* static */ MOZ_ALWAYS_INLINE void 1.1671 +DebugScopes::proxiedScopesPostWriteBarrier(JSRuntime *rt, ObjectWeakMap *map, 1.1672 + const EncapsulatedPtr<JSObject> &key) 1.1673 +{ 1.1674 +#ifdef JSGC_GENERATIONAL 1.1675 + /* 1.1676 + * Strip the barriers from the type before inserting into the store buffer. 1.1677 + * This will automatically ensure that barriers do not fire during GC. 1.1678 + * 1.1679 + * Some compilers complain about instantiating the WeakMap class for 1.1680 + * unbarriered type arguments, so we cast to a HashMap instead. Because of 1.1681 + * WeakMap's multiple inheritace, We need to do this in two stages, first to 1.1682 + * the HashMap base class and then to the unbarriered version. 1.1683 + */ 1.1684 + ObjectWeakMap::Base *baseHashMap = static_cast<ObjectWeakMap::Base *>(map); 1.1685 + 1.1686 + typedef HashMap<JSObject *, JSObject *> UnbarrieredMap; 1.1687 + UnbarrieredMap *unbarrieredMap = reinterpret_cast<UnbarrieredMap *>(baseHashMap); 1.1688 + 1.1689 + typedef gc::HashKeyRef<UnbarrieredMap, JSObject *> Ref; 1.1690 + if (key && IsInsideNursery(rt, key)) 1.1691 + rt->gcStoreBuffer.putGeneric(Ref(unbarrieredMap, key.get())); 1.1692 +#endif 1.1693 +} 1.1694 + 1.1695 +#ifdef JSGC_GENERATIONAL 1.1696 +class DebugScopes::MissingScopesRef : public gc::BufferableRef 1.1697 +{ 1.1698 + MissingScopeMap *map; 1.1699 + ScopeIterKey key; 1.1700 + 1.1701 + public: 1.1702 + MissingScopesRef(MissingScopeMap *m, const ScopeIterKey &k) : map(m), key(k) {} 1.1703 + 1.1704 + void mark(JSTracer *trc) { 1.1705 + ScopeIterKey prior = key; 1.1706 + MissingScopeMap::Ptr p = map->lookup(key); 1.1707 + if (!p) 1.1708 + return; 1.1709 + trc->setTracingLocation(&const_cast<ScopeIterKey &>(p->key()).enclosingScope()); 1.1710 + Mark(trc, &key.enclosingScope(), "MissingScopesRef"); 1.1711 + map->rekeyIfMoved(prior, key); 1.1712 + } 1.1713 +}; 1.1714 +#endif 1.1715 + 1.1716 +/* static */ MOZ_ALWAYS_INLINE void 1.1717 +DebugScopes::missingScopesPostWriteBarrier(JSRuntime *rt, MissingScopeMap *map, 1.1718 + const ScopeIterKey &key) 1.1719 +{ 1.1720 +#ifdef JSGC_GENERATIONAL 1.1721 + if (key.enclosingScope() && IsInsideNursery(rt, key.enclosingScope())) 1.1722 + rt->gcStoreBuffer.putGeneric(MissingScopesRef(map, key)); 1.1723 +#endif 1.1724 +} 1.1725 + 1.1726 +/* static */ MOZ_ALWAYS_INLINE void 1.1727 +DebugScopes::liveScopesPostWriteBarrier(JSRuntime *rt, LiveScopeMap *map, ScopeObject *key) 1.1728 +{ 1.1729 +#ifdef JSGC_GENERATIONAL 1.1730 + // As above. Otherwise, barriers could fire during GC when moving the 1.1731 + // value. 1.1732 + typedef HashMap<ScopeObject *, 1.1733 + ScopeIterKey, 1.1734 + DefaultHasher<ScopeObject *>, 1.1735 + RuntimeAllocPolicy> UnbarrieredLiveScopeMap; 1.1736 + typedef gc::HashKeyRef<UnbarrieredLiveScopeMap, ScopeObject *> Ref; 1.1737 + if (key && IsInsideNursery(rt, key)) 1.1738 + rt->gcStoreBuffer.putGeneric(Ref(reinterpret_cast<UnbarrieredLiveScopeMap *>(map), key)); 1.1739 +#endif 1.1740 +} 1.1741 + 1.1742 +DebugScopes::DebugScopes(JSContext *cx) 1.1743 + : proxiedScopes(cx), 1.1744 + missingScopes(cx->runtime()), 1.1745 + liveScopes(cx->runtime()) 1.1746 +{} 1.1747 + 1.1748 +DebugScopes::~DebugScopes() 1.1749 +{ 1.1750 + JS_ASSERT(missingScopes.empty()); 1.1751 + WeakMapBase::removeWeakMapFromList(&proxiedScopes); 1.1752 +} 1.1753 + 1.1754 +bool 1.1755 +DebugScopes::init() 1.1756 +{ 1.1757 + if (!liveScopes.init() || 1.1758 + !proxiedScopes.init() || 1.1759 + !missingScopes.init()) 1.1760 + { 1.1761 + return false; 1.1762 + } 1.1763 + return true; 1.1764 +} 1.1765 + 1.1766 +void 1.1767 +DebugScopes::mark(JSTracer *trc) 1.1768 +{ 1.1769 + proxiedScopes.trace(trc); 1.1770 +} 1.1771 + 1.1772 +void 1.1773 +DebugScopes::sweep(JSRuntime *rt) 1.1774 +{ 1.1775 + /* 1.1776 + * missingScopes points to debug scopes weakly so that debug scopes can be 1.1777 + * released more eagerly. 1.1778 + */ 1.1779 + for (MissingScopeMap::Enum e(missingScopes); !e.empty(); e.popFront()) { 1.1780 + DebugScopeObject **debugScope = e.front().value().unsafeGet(); 1.1781 + if (IsObjectAboutToBeFinalized(debugScope)) { 1.1782 + /* 1.1783 + * Note that onPopCall and onPopBlock rely on missingScopes to find 1.1784 + * scope objects that we synthesized for the debugger's sake, and 1.1785 + * clean up the synthetic scope objects' entries in liveScopes. So 1.1786 + * if we remove an entry frcom missingScopes here, we must also 1.1787 + * remove the corresponding liveScopes entry. 1.1788 + * 1.1789 + * Since the DebugScopeObject is the only thing using its scope 1.1790 + * object, and the DSO is about to be finalized, you might assume 1.1791 + * that the synthetic SO is also about to be finalized too, and thus 1.1792 + * the loop below will take care of things. But complex GC behavior 1.1793 + * means that marks are only conservative approximations of 1.1794 + * liveness; we should assume that anything could be marked. 1.1795 + * 1.1796 + * Thus, we must explicitly remove the entries from both liveScopes 1.1797 + * and missingScopes here. 1.1798 + */ 1.1799 + liveScopes.remove(&(*debugScope)->scope()); 1.1800 + e.removeFront(); 1.1801 + } 1.1802 + } 1.1803 + 1.1804 + for (LiveScopeMap::Enum e(liveScopes); !e.empty(); e.popFront()) { 1.1805 + ScopeObject *scope = e.front().key(); 1.1806 + 1.1807 + /* 1.1808 + * Scopes can be finalized when a debugger-synthesized ScopeObject is 1.1809 + * no longer reachable via its DebugScopeObject. 1.1810 + */ 1.1811 + if (IsObjectAboutToBeFinalized(&scope)) { 1.1812 + e.removeFront(); 1.1813 + continue; 1.1814 + } 1.1815 + } 1.1816 +} 1.1817 + 1.1818 +#if defined(JSGC_GENERATIONAL) && defined(JS_GC_ZEAL) 1.1819 +void 1.1820 +DebugScopes::checkHashTablesAfterMovingGC(JSRuntime *runtime) 1.1821 +{ 1.1822 + /* 1.1823 + * This is called at the end of StoreBuffer::mark() to check that our 1.1824 + * postbarriers have worked and that no hashtable keys (or values) are left 1.1825 + * pointing into the nursery. 1.1826 + */ 1.1827 + JS::shadow::Runtime *rt = JS::shadow::Runtime::asShadowRuntime(runtime); 1.1828 + for (ObjectWeakMap::Range r = proxiedScopes.all(); !r.empty(); r.popFront()) { 1.1829 + JS_ASSERT(!IsInsideNursery(rt, r.front().key().get())); 1.1830 + JS_ASSERT(!IsInsideNursery(rt, r.front().value().get())); 1.1831 + } 1.1832 + for (MissingScopeMap::Range r = missingScopes.all(); !r.empty(); r.popFront()) { 1.1833 + JS_ASSERT(!IsInsideNursery(rt, r.front().key().cur())); 1.1834 + JS_ASSERT(!IsInsideNursery(rt, r.front().key().staticScope())); 1.1835 + JS_ASSERT(!IsInsideNursery(rt, r.front().value().get())); 1.1836 + } 1.1837 + for (LiveScopeMap::Range r = liveScopes.all(); !r.empty(); r.popFront()) { 1.1838 + JS_ASSERT(!IsInsideNursery(rt, r.front().key())); 1.1839 + JS_ASSERT(!IsInsideNursery(rt, r.front().value().cur_.get())); 1.1840 + JS_ASSERT(!IsInsideNursery(rt, r.front().value().staticScope_.get())); 1.1841 + } 1.1842 +} 1.1843 +#endif 1.1844 + 1.1845 +/* 1.1846 + * Unfortunately, GetDebugScopeForFrame needs to work even outside debug mode 1.1847 + * (in particular, JS_GetFrameScopeChain does not require debug mode). Since 1.1848 + * DebugScopes::onPop* are only called in debug mode, this means we cannot 1.1849 + * use any of the maps in DebugScopes. This will produce debug scope chains 1.1850 + * that do not obey the debugger invariants but that is just fine. 1.1851 + */ 1.1852 +static bool 1.1853 +CanUseDebugScopeMaps(JSContext *cx) 1.1854 +{ 1.1855 + return cx->compartment()->debugMode(); 1.1856 +} 1.1857 + 1.1858 +DebugScopes * 1.1859 +DebugScopes::ensureCompartmentData(JSContext *cx) 1.1860 +{ 1.1861 + JSCompartment *c = cx->compartment(); 1.1862 + if (c->debugScopes) 1.1863 + return c->debugScopes; 1.1864 + 1.1865 + c->debugScopes = cx->runtime()->new_<DebugScopes>(cx); 1.1866 + if (c->debugScopes && c->debugScopes->init()) 1.1867 + return c->debugScopes; 1.1868 + 1.1869 + js_ReportOutOfMemory(cx); 1.1870 + return nullptr; 1.1871 +} 1.1872 + 1.1873 +DebugScopeObject * 1.1874 +DebugScopes::hasDebugScope(JSContext *cx, ScopeObject &scope) 1.1875 +{ 1.1876 + DebugScopes *scopes = scope.compartment()->debugScopes; 1.1877 + if (!scopes) 1.1878 + return nullptr; 1.1879 + 1.1880 + if (ObjectWeakMap::Ptr p = scopes->proxiedScopes.lookup(&scope)) { 1.1881 + JS_ASSERT(CanUseDebugScopeMaps(cx)); 1.1882 + return &p->value()->as<DebugScopeObject>(); 1.1883 + } 1.1884 + 1.1885 + return nullptr; 1.1886 +} 1.1887 + 1.1888 +bool 1.1889 +DebugScopes::addDebugScope(JSContext *cx, ScopeObject &scope, DebugScopeObject &debugScope) 1.1890 +{ 1.1891 + JS_ASSERT(cx->compartment() == scope.compartment()); 1.1892 + JS_ASSERT(cx->compartment() == debugScope.compartment()); 1.1893 + 1.1894 + if (!CanUseDebugScopeMaps(cx)) 1.1895 + return true; 1.1896 + 1.1897 + DebugScopes *scopes = ensureCompartmentData(cx); 1.1898 + if (!scopes) 1.1899 + return false; 1.1900 + 1.1901 + JS_ASSERT(!scopes->proxiedScopes.has(&scope)); 1.1902 + if (!scopes->proxiedScopes.put(&scope, &debugScope)) { 1.1903 + js_ReportOutOfMemory(cx); 1.1904 + return false; 1.1905 + } 1.1906 + 1.1907 + proxiedScopesPostWriteBarrier(cx->runtime(), &scopes->proxiedScopes, &scope); 1.1908 + return true; 1.1909 +} 1.1910 + 1.1911 +DebugScopeObject * 1.1912 +DebugScopes::hasDebugScope(JSContext *cx, const ScopeIter &si) 1.1913 +{ 1.1914 + JS_ASSERT(!si.hasScopeObject()); 1.1915 + 1.1916 + DebugScopes *scopes = cx->compartment()->debugScopes; 1.1917 + if (!scopes) 1.1918 + return nullptr; 1.1919 + 1.1920 + if (MissingScopeMap::Ptr p = scopes->missingScopes.lookup(si)) { 1.1921 + JS_ASSERT(CanUseDebugScopeMaps(cx)); 1.1922 + return p->value(); 1.1923 + } 1.1924 + return nullptr; 1.1925 +} 1.1926 + 1.1927 +bool 1.1928 +DebugScopes::addDebugScope(JSContext *cx, const ScopeIter &si, DebugScopeObject &debugScope) 1.1929 +{ 1.1930 + JS_ASSERT(!si.hasScopeObject()); 1.1931 + JS_ASSERT(cx->compartment() == debugScope.compartment()); 1.1932 + JS_ASSERT_IF(si.frame().isFunctionFrame(), !si.frame().callee()->isGenerator()); 1.1933 + 1.1934 + if (!CanUseDebugScopeMaps(cx)) 1.1935 + return true; 1.1936 + 1.1937 + DebugScopes *scopes = ensureCompartmentData(cx); 1.1938 + if (!scopes) 1.1939 + return false; 1.1940 + 1.1941 + JS_ASSERT(!scopes->missingScopes.has(si)); 1.1942 + if (!scopes->missingScopes.put(si, &debugScope)) { 1.1943 + js_ReportOutOfMemory(cx); 1.1944 + return false; 1.1945 + } 1.1946 + missingScopesPostWriteBarrier(cx->runtime(), &scopes->missingScopes, si); 1.1947 + 1.1948 + JS_ASSERT(!scopes->liveScopes.has(&debugScope.scope())); 1.1949 + if (!scopes->liveScopes.put(&debugScope.scope(), si)) { 1.1950 + js_ReportOutOfMemory(cx); 1.1951 + return false; 1.1952 + } 1.1953 + liveScopesPostWriteBarrier(cx->runtime(), &scopes->liveScopes, &debugScope.scope()); 1.1954 + 1.1955 + return true; 1.1956 +} 1.1957 + 1.1958 +void 1.1959 +DebugScopes::onPopCall(AbstractFramePtr frame, JSContext *cx) 1.1960 +{ 1.1961 + JS_ASSERT(!frame.isYielding()); 1.1962 + assertSameCompartment(cx, frame); 1.1963 + 1.1964 + DebugScopes *scopes = cx->compartment()->debugScopes; 1.1965 + if (!scopes) 1.1966 + return; 1.1967 + 1.1968 + Rooted<DebugScopeObject*> debugScope(cx, nullptr); 1.1969 + 1.1970 + if (frame.fun()->isHeavyweight()) { 1.1971 + /* 1.1972 + * The frame may be observed before the prologue has created the 1.1973 + * CallObject. See ScopeIter::settle. 1.1974 + */ 1.1975 + if (!frame.hasCallObj()) 1.1976 + return; 1.1977 + 1.1978 + CallObject &callobj = frame.scopeChain()->as<CallObject>(); 1.1979 + scopes->liveScopes.remove(&callobj); 1.1980 + if (ObjectWeakMap::Ptr p = scopes->proxiedScopes.lookup(&callobj)) 1.1981 + debugScope = &p->value()->as<DebugScopeObject>(); 1.1982 + } else { 1.1983 + ScopeIter si(frame, frame.script()->main(), cx); 1.1984 + if (MissingScopeMap::Ptr p = scopes->missingScopes.lookup(si)) { 1.1985 + debugScope = p->value(); 1.1986 + scopes->liveScopes.remove(&debugScope->scope().as<CallObject>()); 1.1987 + scopes->missingScopes.remove(p); 1.1988 + } 1.1989 + } 1.1990 + 1.1991 + /* 1.1992 + * When the JS stack frame is popped, the values of unaliased variables 1.1993 + * are lost. If there is any debug scope referring to this scope, save a 1.1994 + * copy of the unaliased variables' values in an array for later debugger 1.1995 + * access via DebugScopeProxy::handleUnaliasedAccess. 1.1996 + * 1.1997 + * Note: since it is simplest for this function to be infallible, failure 1.1998 + * in this code will be silently ignored. This does not break any 1.1999 + * invariants since DebugScopeObject::maybeSnapshot can already be nullptr. 1.2000 + */ 1.2001 + if (debugScope) { 1.2002 + /* 1.2003 + * Copy all frame values into the snapshot, regardless of 1.2004 + * aliasing. This unnecessarily includes aliased variables 1.2005 + * but it simplifies later indexing logic. 1.2006 + */ 1.2007 + AutoValueVector vec(cx); 1.2008 + if (!frame.copyRawFrameSlots(&vec) || vec.length() == 0) 1.2009 + return; 1.2010 + 1.2011 + /* 1.2012 + * Copy in formals that are not aliased via the scope chain 1.2013 + * but are aliased via the arguments object. 1.2014 + */ 1.2015 + RootedScript script(cx, frame.script()); 1.2016 + if (script->analyzedArgsUsage() && script->needsArgsObj() && frame.hasArgsObj()) { 1.2017 + for (unsigned i = 0; i < frame.numFormalArgs(); ++i) { 1.2018 + if (script->formalLivesInArgumentsObject(i)) 1.2019 + vec[i] = frame.argsObj().arg(i); 1.2020 + } 1.2021 + } 1.2022 + 1.2023 + /* 1.2024 + * Use a dense array as storage (since proxies do not have trace 1.2025 + * hooks). This array must not escape into the wild. 1.2026 + */ 1.2027 + RootedObject snapshot(cx, NewDenseCopiedArray(cx, vec.length(), vec.begin())); 1.2028 + if (!snapshot) { 1.2029 + cx->clearPendingException(); 1.2030 + return; 1.2031 + } 1.2032 + 1.2033 + debugScope->initSnapshot(*snapshot); 1.2034 + } 1.2035 +} 1.2036 + 1.2037 +void 1.2038 +DebugScopes::onPopBlock(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc) 1.2039 +{ 1.2040 + assertSameCompartment(cx, frame); 1.2041 + 1.2042 + DebugScopes *scopes = cx->compartment()->debugScopes; 1.2043 + if (!scopes) 1.2044 + return; 1.2045 + 1.2046 + ScopeIter si(frame, pc, cx); 1.2047 + onPopBlock(cx, si); 1.2048 +} 1.2049 + 1.2050 +void 1.2051 +DebugScopes::onPopBlock(JSContext *cx, const ScopeIter &si) 1.2052 +{ 1.2053 + DebugScopes *scopes = cx->compartment()->debugScopes; 1.2054 + if (!scopes) 1.2055 + return; 1.2056 + 1.2057 + JS_ASSERT(si.type() == ScopeIter::Block); 1.2058 + 1.2059 + if (si.staticBlock().needsClone()) { 1.2060 + ClonedBlockObject &clone = si.scope().as<ClonedBlockObject>(); 1.2061 + clone.copyUnaliasedValues(si.frame()); 1.2062 + scopes->liveScopes.remove(&clone); 1.2063 + } else { 1.2064 + if (MissingScopeMap::Ptr p = scopes->missingScopes.lookup(si)) { 1.2065 + ClonedBlockObject &clone = p->value()->scope().as<ClonedBlockObject>(); 1.2066 + clone.copyUnaliasedValues(si.frame()); 1.2067 + scopes->liveScopes.remove(&clone); 1.2068 + scopes->missingScopes.remove(p); 1.2069 + } 1.2070 + } 1.2071 +} 1.2072 + 1.2073 +void 1.2074 +DebugScopes::onPopWith(AbstractFramePtr frame) 1.2075 +{ 1.2076 + DebugScopes *scopes = frame.compartment()->debugScopes; 1.2077 + if (scopes) 1.2078 + scopes->liveScopes.remove(&frame.scopeChain()->as<DynamicWithObject>()); 1.2079 +} 1.2080 + 1.2081 +void 1.2082 +DebugScopes::onPopStrictEvalScope(AbstractFramePtr frame) 1.2083 +{ 1.2084 + DebugScopes *scopes = frame.compartment()->debugScopes; 1.2085 + if (!scopes) 1.2086 + return; 1.2087 + 1.2088 + /* 1.2089 + * The stack frame may be observed before the prologue has created the 1.2090 + * CallObject. See ScopeIter::settle. 1.2091 + */ 1.2092 + if (frame.hasCallObj()) 1.2093 + scopes->liveScopes.remove(&frame.scopeChain()->as<CallObject>()); 1.2094 +} 1.2095 + 1.2096 +void 1.2097 +DebugScopes::onCompartmentLeaveDebugMode(JSCompartment *c) 1.2098 +{ 1.2099 + DebugScopes *scopes = c->debugScopes; 1.2100 + if (scopes) { 1.2101 + scopes->proxiedScopes.clear(); 1.2102 + scopes->missingScopes.clear(); 1.2103 + scopes->liveScopes.clear(); 1.2104 + } 1.2105 +} 1.2106 + 1.2107 +bool 1.2108 +DebugScopes::updateLiveScopes(JSContext *cx) 1.2109 +{ 1.2110 + JS_CHECK_RECURSION(cx, return false); 1.2111 + 1.2112 + /* 1.2113 + * Note that we must always update the top frame's scope objects' entries 1.2114 + * in liveScopes because we can't be sure code hasn't run in that frame to 1.2115 + * change the scope chain since we were last called. The fp->prevUpToDate() 1.2116 + * flag indicates whether the scopes of frames older than fp are already 1.2117 + * included in liveScopes. It might seem simpler to have fp instead carry a 1.2118 + * flag indicating whether fp itself is accurately described, but then we 1.2119 + * would need to clear that flag whenever fp ran code. By storing the 'up 1.2120 + * to date' bit for fp->prev() in fp, simply popping fp effectively clears 1.2121 + * the flag for us, at exactly the time when execution resumes fp->prev(). 1.2122 + */ 1.2123 + for (AllFramesIter i(cx); !i.done(); ++i) { 1.2124 + if (!i.hasUsableAbstractFramePtr()) 1.2125 + continue; 1.2126 + 1.2127 + AbstractFramePtr frame = i.abstractFramePtr(); 1.2128 + if (frame.scopeChain()->compartment() != cx->compartment()) 1.2129 + continue; 1.2130 + 1.2131 + if (frame.isFunctionFrame() && frame.callee()->isGenerator()) 1.2132 + continue; 1.2133 + 1.2134 + for (ScopeIter si(frame, i.pc(), cx); !si.done(); ++si) { 1.2135 + if (si.hasScopeObject()) { 1.2136 + JS_ASSERT(si.scope().compartment() == cx->compartment()); 1.2137 + DebugScopes *scopes = ensureCompartmentData(cx); 1.2138 + if (!scopes) 1.2139 + return false; 1.2140 + if (!scopes->liveScopes.put(&si.scope(), si)) 1.2141 + return false; 1.2142 + liveScopesPostWriteBarrier(cx->runtime(), &scopes->liveScopes, &si.scope()); 1.2143 + } 1.2144 + } 1.2145 + 1.2146 + if (frame.prevUpToDate()) 1.2147 + return true; 1.2148 + JS_ASSERT(frame.scopeChain()->compartment()->debugMode()); 1.2149 + frame.setPrevUpToDate(); 1.2150 + } 1.2151 + 1.2152 + return true; 1.2153 +} 1.2154 + 1.2155 +ScopeIterVal* 1.2156 +DebugScopes::hasLiveScope(ScopeObject &scope) 1.2157 +{ 1.2158 + DebugScopes *scopes = scope.compartment()->debugScopes; 1.2159 + if (!scopes) 1.2160 + return nullptr; 1.2161 + 1.2162 + if (LiveScopeMap::Ptr p = scopes->liveScopes.lookup(&scope)) 1.2163 + return &p->value(); 1.2164 + 1.2165 + return nullptr; 1.2166 +} 1.2167 + 1.2168 +/*****************************************************************************/ 1.2169 + 1.2170 +static JSObject * 1.2171 +GetDebugScope(JSContext *cx, const ScopeIter &si); 1.2172 + 1.2173 +static DebugScopeObject * 1.2174 +GetDebugScopeForScope(JSContext *cx, Handle<ScopeObject*> scope, const ScopeIter &enclosing) 1.2175 +{ 1.2176 + if (DebugScopeObject *debugScope = DebugScopes::hasDebugScope(cx, *scope)) 1.2177 + return debugScope; 1.2178 + 1.2179 + RootedObject enclosingDebug(cx, GetDebugScope(cx, enclosing)); 1.2180 + if (!enclosingDebug) 1.2181 + return nullptr; 1.2182 + 1.2183 + JSObject &maybeDecl = scope->enclosingScope(); 1.2184 + if (maybeDecl.is<DeclEnvObject>()) { 1.2185 + JS_ASSERT(CallObjectLambdaName(scope->as<CallObject>().callee())); 1.2186 + enclosingDebug = DebugScopeObject::create(cx, maybeDecl.as<DeclEnvObject>(), enclosingDebug); 1.2187 + if (!enclosingDebug) 1.2188 + return nullptr; 1.2189 + } 1.2190 + 1.2191 + DebugScopeObject *debugScope = DebugScopeObject::create(cx, *scope, enclosingDebug); 1.2192 + if (!debugScope) 1.2193 + return nullptr; 1.2194 + 1.2195 + if (!DebugScopes::addDebugScope(cx, *scope, *debugScope)) 1.2196 + return nullptr; 1.2197 + 1.2198 + return debugScope; 1.2199 +} 1.2200 + 1.2201 +static DebugScopeObject * 1.2202 +GetDebugScopeForMissing(JSContext *cx, const ScopeIter &si) 1.2203 +{ 1.2204 + if (DebugScopeObject *debugScope = DebugScopes::hasDebugScope(cx, si)) 1.2205 + return debugScope; 1.2206 + 1.2207 + ScopeIter copy(si, cx); 1.2208 + RootedObject enclosingDebug(cx, GetDebugScope(cx, ++copy)); 1.2209 + if (!enclosingDebug) 1.2210 + return nullptr; 1.2211 + 1.2212 + /* 1.2213 + * Create the missing scope object. For block objects, this takes care of 1.2214 + * storing variable values after the stack frame has been popped. For call 1.2215 + * objects, we only use the pretend call object to access callee, bindings 1.2216 + * and to receive dynamically added properties. Together, this provides the 1.2217 + * nice invariant that every DebugScopeObject has a ScopeObject. 1.2218 + * 1.2219 + * Note: to preserve scopeChain depth invariants, these lazily-reified 1.2220 + * scopes must not be put on the frame's scope chain; instead, they are 1.2221 + * maintained via DebugScopes hooks. 1.2222 + */ 1.2223 + DebugScopeObject *debugScope = nullptr; 1.2224 + switch (si.type()) { 1.2225 + case ScopeIter::Call: { 1.2226 + // Generators should always reify their scopes. 1.2227 + JS_ASSERT(!si.frame().callee()->isGenerator()); 1.2228 + Rooted<CallObject*> callobj(cx, CallObject::createForFunction(cx, si.frame())); 1.2229 + if (!callobj) 1.2230 + return nullptr; 1.2231 + 1.2232 + if (callobj->enclosingScope().is<DeclEnvObject>()) { 1.2233 + JS_ASSERT(CallObjectLambdaName(callobj->callee())); 1.2234 + DeclEnvObject &declenv = callobj->enclosingScope().as<DeclEnvObject>(); 1.2235 + enclosingDebug = DebugScopeObject::create(cx, declenv, enclosingDebug); 1.2236 + if (!enclosingDebug) 1.2237 + return nullptr; 1.2238 + } 1.2239 + 1.2240 + debugScope = DebugScopeObject::create(cx, *callobj, enclosingDebug); 1.2241 + break; 1.2242 + } 1.2243 + case ScopeIter::Block: { 1.2244 + // Generators should always reify their scopes. 1.2245 + JS_ASSERT_IF(si.frame().isFunctionFrame(), !si.frame().callee()->isGenerator()); 1.2246 + Rooted<StaticBlockObject *> staticBlock(cx, &si.staticBlock()); 1.2247 + ClonedBlockObject *block = ClonedBlockObject::create(cx, staticBlock, si.frame()); 1.2248 + if (!block) 1.2249 + return nullptr; 1.2250 + 1.2251 + debugScope = DebugScopeObject::create(cx, *block, enclosingDebug); 1.2252 + break; 1.2253 + } 1.2254 + case ScopeIter::With: 1.2255 + case ScopeIter::StrictEvalScope: 1.2256 + MOZ_ASSUME_UNREACHABLE("should already have a scope"); 1.2257 + } 1.2258 + if (!debugScope) 1.2259 + return nullptr; 1.2260 + 1.2261 + if (!DebugScopes::addDebugScope(cx, si, *debugScope)) 1.2262 + return nullptr; 1.2263 + 1.2264 + return debugScope; 1.2265 +} 1.2266 + 1.2267 +static JSObject * 1.2268 +GetDebugScope(JSContext *cx, JSObject &obj) 1.2269 +{ 1.2270 + /* 1.2271 + * As an engine invariant (maintained internally and asserted by Execute), 1.2272 + * ScopeObjects and non-ScopeObjects cannot be interleaved on the scope 1.2273 + * chain; every scope chain must start with zero or more ScopeObjects and 1.2274 + * terminate with one or more non-ScopeObjects (viz., GlobalObject). 1.2275 + */ 1.2276 + if (!obj.is<ScopeObject>()) { 1.2277 +#ifdef DEBUG 1.2278 + JSObject *o = &obj; 1.2279 + while ((o = o->enclosingScope())) 1.2280 + JS_ASSERT(!o->is<ScopeObject>()); 1.2281 +#endif 1.2282 + return &obj; 1.2283 + } 1.2284 + 1.2285 + Rooted<ScopeObject*> scope(cx, &obj.as<ScopeObject>()); 1.2286 + if (ScopeIterVal *maybeLiveScope = DebugScopes::hasLiveScope(*scope)) { 1.2287 + ScopeIter si(*maybeLiveScope, cx); 1.2288 + return GetDebugScope(cx, si); 1.2289 + } 1.2290 + ScopeIter si(scope->enclosingScope(), cx); 1.2291 + return GetDebugScopeForScope(cx, scope, si); 1.2292 +} 1.2293 + 1.2294 +static JSObject * 1.2295 +GetDebugScope(JSContext *cx, const ScopeIter &si) 1.2296 +{ 1.2297 + JS_CHECK_RECURSION(cx, return nullptr); 1.2298 + 1.2299 + if (si.done()) 1.2300 + return GetDebugScope(cx, si.enclosingScope()); 1.2301 + 1.2302 + if (!si.hasScopeObject()) 1.2303 + return GetDebugScopeForMissing(cx, si); 1.2304 + 1.2305 + Rooted<ScopeObject*> scope(cx, &si.scope()); 1.2306 + 1.2307 + ScopeIter copy(si, cx); 1.2308 + return GetDebugScopeForScope(cx, scope, ++copy); 1.2309 +} 1.2310 + 1.2311 +JSObject * 1.2312 +js::GetDebugScopeForFunction(JSContext *cx, HandleFunction fun) 1.2313 +{ 1.2314 + assertSameCompartment(cx, fun); 1.2315 + JS_ASSERT(cx->compartment()->debugMode()); 1.2316 + if (!DebugScopes::updateLiveScopes(cx)) 1.2317 + return nullptr; 1.2318 + return GetDebugScope(cx, *fun->environment()); 1.2319 +} 1.2320 + 1.2321 +JSObject * 1.2322 +js::GetDebugScopeForFrame(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc) 1.2323 +{ 1.2324 + assertSameCompartment(cx, frame); 1.2325 + if (CanUseDebugScopeMaps(cx) && !DebugScopes::updateLiveScopes(cx)) 1.2326 + return nullptr; 1.2327 + ScopeIter si(frame, pc, cx); 1.2328 + return GetDebugScope(cx, si); 1.2329 +} 1.2330 + 1.2331 +#ifdef DEBUG 1.2332 + 1.2333 +typedef HashSet<PropertyName *> PropertyNameSet; 1.2334 + 1.2335 +static bool 1.2336 +RemoveReferencedNames(JSContext *cx, HandleScript script, PropertyNameSet &remainingNames) 1.2337 +{ 1.2338 + // Remove from remainingNames --- the closure variables in some outer 1.2339 + // script --- any free variables in this script. This analysis isn't perfect: 1.2340 + // 1.2341 + // - It will not account for free variables in an inner script which are 1.2342 + // actually accessing some name in an intermediate script between the 1.2343 + // inner and outer scripts. This can cause remainingNames to be an 1.2344 + // underapproximation. 1.2345 + // 1.2346 + // - It will not account for new names introduced via eval. This can cause 1.2347 + // remainingNames to be an overapproximation. This would be easy to fix 1.2348 + // but is nice to have as the eval will probably not access these 1.2349 + // these names and putting eval in an inner script is bad news if you 1.2350 + // care about entraining variables unnecessarily. 1.2351 + 1.2352 + for (jsbytecode *pc = script->code(); pc != script->codeEnd(); pc += GetBytecodeLength(pc)) { 1.2353 + PropertyName *name; 1.2354 + 1.2355 + switch (JSOp(*pc)) { 1.2356 + case JSOP_NAME: 1.2357 + case JSOP_SETNAME: 1.2358 + name = script->getName(pc); 1.2359 + break; 1.2360 + 1.2361 + case JSOP_GETALIASEDVAR: 1.2362 + case JSOP_SETALIASEDVAR: 1.2363 + name = ScopeCoordinateName(cx->runtime()->scopeCoordinateNameCache, script, pc); 1.2364 + break; 1.2365 + 1.2366 + default: 1.2367 + name = nullptr; 1.2368 + break; 1.2369 + } 1.2370 + 1.2371 + if (name) 1.2372 + remainingNames.remove(name); 1.2373 + } 1.2374 + 1.2375 + if (script->hasObjects()) { 1.2376 + ObjectArray *objects = script->objects(); 1.2377 + for (size_t i = 0; i < objects->length; i++) { 1.2378 + JSObject *obj = objects->vector[i]; 1.2379 + if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) { 1.2380 + JSFunction *fun = &obj->as<JSFunction>(); 1.2381 + RootedScript innerScript(cx, fun->getOrCreateScript(cx)); 1.2382 + if (!innerScript) 1.2383 + return false; 1.2384 + 1.2385 + if (!RemoveReferencedNames(cx, innerScript, remainingNames)) 1.2386 + return false; 1.2387 + } 1.2388 + } 1.2389 + } 1.2390 + 1.2391 + return true; 1.2392 +} 1.2393 + 1.2394 +static bool 1.2395 +AnalyzeEntrainedVariablesInScript(JSContext *cx, HandleScript script, HandleScript innerScript) 1.2396 +{ 1.2397 + PropertyNameSet remainingNames(cx); 1.2398 + if (!remainingNames.init()) 1.2399 + return false; 1.2400 + 1.2401 + for (BindingIter bi(script); bi; bi++) { 1.2402 + if (bi->aliased()) { 1.2403 + PropertyNameSet::AddPtr p = remainingNames.lookupForAdd(bi->name()); 1.2404 + if (!p && !remainingNames.add(p, bi->name())) 1.2405 + return false; 1.2406 + } 1.2407 + } 1.2408 + 1.2409 + if (!RemoveReferencedNames(cx, innerScript, remainingNames)) 1.2410 + return false; 1.2411 + 1.2412 + if (!remainingNames.empty()) { 1.2413 + Sprinter buf(cx); 1.2414 + if (!buf.init()) 1.2415 + return false; 1.2416 + 1.2417 + buf.printf("Script "); 1.2418 + 1.2419 + if (JSAtom *name = script->functionNonDelazifying()->displayAtom()) { 1.2420 + buf.putString(name); 1.2421 + buf.printf(" "); 1.2422 + } 1.2423 + 1.2424 + buf.printf("(%s:%d) has variables entrained by ", script->filename(), script->lineno()); 1.2425 + 1.2426 + if (JSAtom *name = innerScript->functionNonDelazifying()->displayAtom()) { 1.2427 + buf.putString(name); 1.2428 + buf.printf(" "); 1.2429 + } 1.2430 + 1.2431 + buf.printf("(%s:%d) ::", innerScript->filename(), innerScript->lineno()); 1.2432 + 1.2433 + for (PropertyNameSet::Range r = remainingNames.all(); !r.empty(); r.popFront()) { 1.2434 + buf.printf(" "); 1.2435 + buf.putString(r.front()); 1.2436 + } 1.2437 + 1.2438 + printf("%s\n", buf.string()); 1.2439 + } 1.2440 + 1.2441 + if (innerScript->hasObjects()) { 1.2442 + ObjectArray *objects = innerScript->objects(); 1.2443 + for (size_t i = 0; i < objects->length; i++) { 1.2444 + JSObject *obj = objects->vector[i]; 1.2445 + if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) { 1.2446 + JSFunction *fun = &obj->as<JSFunction>(); 1.2447 + RootedScript innerInnerScript(cx, fun->getOrCreateScript(cx)); 1.2448 + if (!innerInnerScript || 1.2449 + !AnalyzeEntrainedVariablesInScript(cx, script, innerInnerScript)) 1.2450 + { 1.2451 + return false; 1.2452 + } 1.2453 + } 1.2454 + } 1.2455 + } 1.2456 + 1.2457 + return true; 1.2458 +} 1.2459 + 1.2460 +// Look for local variables in script or any other script inner to it, which are 1.2461 +// part of the script's call object and are unnecessarily entrained by their own 1.2462 +// inner scripts which do not refer to those variables. An example is: 1.2463 +// 1.2464 +// function foo() { 1.2465 +// var a, b; 1.2466 +// function bar() { return a; } 1.2467 +// function baz() { return b; } 1.2468 +// } 1.2469 +// 1.2470 +// |bar| unnecessarily entrains |b|, and |baz| unnecessarily entrains |a|. 1.2471 +bool 1.2472 +js::AnalyzeEntrainedVariables(JSContext *cx, HandleScript script) 1.2473 +{ 1.2474 + if (!script->hasObjects()) 1.2475 + return true; 1.2476 + 1.2477 + ObjectArray *objects = script->objects(); 1.2478 + for (size_t i = 0; i < objects->length; i++) { 1.2479 + JSObject *obj = objects->vector[i]; 1.2480 + if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) { 1.2481 + JSFunction *fun = &obj->as<JSFunction>(); 1.2482 + RootedScript innerScript(cx, fun->getOrCreateScript(cx)); 1.2483 + if (!innerScript) 1.2484 + return false; 1.2485 + 1.2486 + if (script->functionDelazifying() && script->functionDelazifying()->isHeavyweight()) { 1.2487 + if (!AnalyzeEntrainedVariablesInScript(cx, script, innerScript)) 1.2488 + return false; 1.2489 + } 1.2490 + 1.2491 + if (!AnalyzeEntrainedVariables(cx, innerScript)) 1.2492 + return false; 1.2493 + } 1.2494 + } 1.2495 + 1.2496 + return true; 1.2497 +} 1.2498 + 1.2499 +#endif