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.

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

mercurial