LCOV - code coverage report
Current view: top level - js/src/vm - PIC.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 84 158 53.2 %
Date: 2017-07-14 16:53:18 Functions: 8 14 57.1 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : #include "vm/PIC.h"
       8             : #include "jscntxt.h"
       9             : #include "jscompartment.h"
      10             : #include "jsobj.h"
      11             : #include "gc/Marking.h"
      12             : 
      13             : #include "vm/GlobalObject.h"
      14             : #include "vm/SelfHosting.h"
      15             : 
      16             : #include "jsobjinlines.h"
      17             : #include "vm/NativeObject-inl.h"
      18             : 
      19             : using namespace js;
      20             : using namespace js::gc;
      21             : 
      22             : bool
      23          18 : js::ForOfPIC::Chain::initialize(JSContext* cx)
      24             : {
      25          18 :     MOZ_ASSERT(!initialized_);
      26             : 
      27             :     // Get the canonical Array.prototype
      28          36 :     RootedNativeObject arrayProto(cx, GlobalObject::getOrCreateArrayPrototype(cx, cx->global()));
      29          18 :     if (!arrayProto)
      30           0 :         return false;
      31             : 
      32             :     // Get the canonical ArrayIterator.prototype
      33             :     RootedNativeObject arrayIteratorProto(cx,
      34          36 :         GlobalObject::getOrCreateArrayIteratorPrototype(cx, cx->global()));
      35          18 :     if (!arrayIteratorProto)
      36           0 :         return false;
      37             : 
      38             :     // From this point on, we can't fail.  Set initialized and fill the fields
      39             :     // for the canonical Array.prototype and ArrayIterator.prototype objects.
      40          18 :     initialized_ = true;
      41          18 :     arrayProto_ = arrayProto;
      42          18 :     arrayIteratorProto_ = arrayIteratorProto;
      43             : 
      44             :     // Shortcut returns below means Array for-of will never be optimizable,
      45             :     // do set disabled_ now, and clear it later when we succeed.
      46          18 :     disabled_ = true;
      47             : 
      48             :     // Look up Array.prototype[@@iterator], ensure it's a slotful shape.
      49          18 :     Shape* iterShape = arrayProto->lookup(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator));
      50          18 :     if (!iterShape || !iterShape->hasSlot() || !iterShape->hasDefaultGetter())
      51           0 :         return true;
      52             : 
      53             :     // Get the referred value, and ensure it holds the canonical ArrayValues function.
      54          18 :     Value iterator = arrayProto->getSlot(iterShape->slot());
      55             :     JSFunction* iterFun;
      56          18 :     if (!IsFunctionObject(iterator, &iterFun))
      57           0 :         return true;
      58          18 :     if (!IsSelfHostedFunctionWithName(iterFun, cx->names().ArrayValues))
      59           0 :         return true;
      60             : 
      61             :     // Look up the 'next' value on ArrayIterator.prototype
      62          18 :     Shape* nextShape = arrayIteratorProto->lookup(cx, cx->names().next);
      63          18 :     if (!nextShape || !nextShape->hasSlot())
      64           0 :         return true;
      65             : 
      66             :     // Get the referred value, ensure it holds the canonical ArrayIteratorNext function.
      67          18 :     Value next = arrayIteratorProto->getSlot(nextShape->slot());
      68             :     JSFunction* nextFun;
      69          18 :     if (!IsFunctionObject(next, &nextFun))
      70           0 :         return true;
      71          18 :     if (!IsSelfHostedFunctionWithName(nextFun, cx->names().ArrayIteratorNext))
      72           0 :         return true;
      73             : 
      74          18 :     disabled_ = false;
      75          18 :     arrayProtoShape_ = arrayProto->lastProperty();
      76          18 :     arrayProtoIteratorSlot_ = iterShape->slot();
      77          18 :     canonicalIteratorFunc_ = iterator;
      78          18 :     arrayIteratorProtoShape_ = arrayIteratorProto->lastProperty();
      79          18 :     arrayIteratorProtoNextSlot_ = nextShape->slot();
      80          18 :     canonicalNextFunc_ = next;
      81          18 :     return true;
      82             : }
      83             : 
      84             : js::ForOfPIC::Stub*
      85          55 : js::ForOfPIC::Chain::isArrayOptimized(ArrayObject* obj)
      86             : {
      87          55 :     Stub* stub = getMatchingStub(obj);
      88          55 :     if (!stub)
      89          18 :         return nullptr;
      90             : 
      91             :     // Ensure that this is an otherwise optimizable array.
      92          37 :     if (!isOptimizableArray(obj))
      93           0 :         return nullptr;
      94             : 
      95             :     // Not yet enough!  Ensure that the world as we know it remains sane.
      96          37 :     if (!isArrayStateStillSane())
      97           0 :         return nullptr;
      98             : 
      99          37 :     return stub;
     100             : }
     101             : 
     102             : bool
     103          55 : js::ForOfPIC::Chain::tryOptimizeArray(JSContext* cx, HandleArrayObject array, bool* optimized)
     104             : {
     105          55 :     MOZ_ASSERT(optimized);
     106             : 
     107          55 :     *optimized = false;
     108             : 
     109          55 :     if (!initialized_) {
     110             :         // If PIC is not initialized, initialize it.
     111          18 :         if (!initialize(cx))
     112           0 :             return false;
     113             : 
     114          37 :     } else if (!disabled_ && !isArrayStateStillSane()) {
     115             :         // Otherwise, if array state is no longer sane, reinitialize.
     116           0 :         reset(cx);
     117             : 
     118           0 :         if (!initialize(cx))
     119           0 :             return false;
     120             :     }
     121          55 :     MOZ_ASSERT(initialized_);
     122             : 
     123             :     // If PIC is disabled, don't bother trying to optimize.
     124          55 :     if (disabled_)
     125           0 :         return true;
     126             : 
     127             :     // By the time we get here, we should have a sane array state to work with.
     128          55 :     MOZ_ASSERT(isArrayStateStillSane());
     129             : 
     130             :     // Check if stub already exists.
     131          55 :     ForOfPIC::Stub* stub = isArrayOptimized(&array->as<ArrayObject>());
     132          55 :     if (stub) {
     133          37 :         *optimized = true;
     134          37 :         return true;
     135             :     }
     136             : 
     137             :     // If the number of stubs is about to exceed the limit, throw away entire
     138             :     // existing cache before adding new stubs.  We shouldn't really have heavy
     139             :     // churn on these.
     140          18 :     if (numStubs() >= MAX_STUBS)
     141           0 :         eraseChain();
     142             : 
     143             :     // Ensure array's prototype is the actual Array.prototype
     144          18 :     if (!isOptimizableArray(array))
     145           0 :         return true;
     146             : 
     147             :     // Ensure array doesn't define @@iterator directly.
     148          18 :     if (array->lookup(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator)))
     149           0 :         return true;
     150             : 
     151             :     // Good to optimize now, create stub to add.
     152          36 :     RootedShape shape(cx, array->lastProperty());
     153          18 :     stub = cx->new_<Stub>(shape);
     154          18 :     if (!stub)
     155           0 :         return false;
     156             : 
     157             :     // Add the stub.
     158          18 :     addStub(stub);
     159             : 
     160          18 :     *optimized = true;
     161          18 :     return true;
     162             : }
     163             : 
     164             : js::ForOfPIC::Stub*
     165          55 : js::ForOfPIC::Chain::getMatchingStub(JSObject* obj)
     166             : {
     167             :     // Ensure PIC is initialized and not disabled.
     168          55 :     if (!initialized_ || disabled_)
     169           0 :         return nullptr;
     170             : 
     171             :     // Check if there is a matching stub.
     172          55 :     for (Stub* stub = stubs(); stub != nullptr; stub = stub->next()) {
     173          37 :         if (stub->shape() == obj->maybeShape())
     174          37 :             return stub;
     175             :     }
     176             : 
     177          18 :     return nullptr;
     178             : }
     179             : 
     180             : bool
     181          55 : js::ForOfPIC::Chain::isOptimizableArray(JSObject* obj)
     182             : {
     183          55 :     MOZ_ASSERT(obj->is<ArrayObject>());
     184          55 :     return obj->staticPrototype() == arrayProto_;
     185             : }
     186             : 
     187             : bool
     188         129 : js::ForOfPIC::Chain::isArrayStateStillSane()
     189             : {
     190             :     // Ensure that canonical Array.prototype has matching shape.
     191         129 :     if (arrayProto_->lastProperty() != arrayProtoShape_)
     192           0 :         return false;
     193             : 
     194             :     // Ensure that Array.prototype[@@iterator] contains the
     195             :     // canonical iterator function.
     196         129 :     if (arrayProto_->getSlot(arrayProtoIteratorSlot_) != canonicalIteratorFunc_)
     197           0 :         return false;
     198             : 
     199             :     // Chain to isArrayNextStillSane.
     200         129 :     return isArrayNextStillSane();
     201             : }
     202             : 
     203             : void
     204           0 : js::ForOfPIC::Chain::reset(JSContext* cx)
     205             : {
     206             :     // Should never reset a disabled_ stub.
     207           0 :     MOZ_ASSERT(!disabled_);
     208             : 
     209             :     // Erase the chain.
     210           0 :     eraseChain();
     211             : 
     212           0 :     arrayProto_ = nullptr;
     213           0 :     arrayIteratorProto_ = nullptr;
     214             : 
     215           0 :     arrayProtoShape_ = nullptr;
     216           0 :     arrayProtoIteratorSlot_ = -1;
     217           0 :     canonicalIteratorFunc_ = UndefinedValue();
     218             : 
     219           0 :     arrayIteratorProtoShape_ = nullptr;
     220           0 :     arrayIteratorProtoNextSlot_ = -1;
     221           0 :     canonicalNextFunc_ = UndefinedValue();
     222             : 
     223           0 :     initialized_ = false;
     224           0 : }
     225             : 
     226             : void
     227           0 : js::ForOfPIC::Chain::eraseChain()
     228             : {
     229             :     // Should never need to clear the chain of a disabled stub.
     230           0 :     MOZ_ASSERT(!disabled_);
     231             : 
     232             :     // Free all stubs.
     233           0 :     Stub* stub = stubs_;
     234           0 :     while (stub) {
     235           0 :         Stub* next = stub->next();
     236           0 :         js_delete(stub);
     237           0 :         stub = next;
     238             :     }
     239           0 :     stubs_ = nullptr;
     240           0 : }
     241             : 
     242             : 
     243             : // Trace the pointers stored directly on the stub.
     244             : void
     245           0 : js::ForOfPIC::Chain::trace(JSTracer* trc)
     246             : {
     247           0 :     if (!initialized_ || disabled_)
     248           0 :         return;
     249             : 
     250           0 :     TraceEdge(trc, &arrayProto_, "ForOfPIC Array.prototype.");
     251           0 :     TraceEdge(trc, &arrayIteratorProto_, "ForOfPIC ArrayIterator.prototype.");
     252             : 
     253           0 :     TraceEdge(trc, &arrayProtoShape_, "ForOfPIC Array.prototype shape.");
     254           0 :     TraceEdge(trc, &arrayIteratorProtoShape_, "ForOfPIC ArrayIterator.prototype shape.");
     255             : 
     256           0 :     TraceEdge(trc, &canonicalIteratorFunc_, "ForOfPIC ArrayValues builtin.");
     257           0 :     TraceEdge(trc, &canonicalNextFunc_, "ForOfPIC ArrayIterator.prototype.next builtin.");
     258             : 
     259             :     // Free all the stubs in the chain.
     260           0 :     while (stubs_)
     261           0 :         removeStub(stubs_, nullptr);
     262             : }
     263             : 
     264             : void
     265           0 : js::ForOfPIC::Chain::sweep(FreeOp* fop)
     266             : {
     267             :     // Free all the stubs in the chain.
     268           0 :     while (stubs_) {
     269           0 :         Stub* next = stubs_->next();
     270           0 :         fop->delete_(stubs_);
     271           0 :         stubs_ = next;
     272             :     }
     273           0 :     fop->delete_(this);
     274           0 : }
     275             : 
     276             : static void
     277           0 : ForOfPIC_finalize(FreeOp* fop, JSObject* obj)
     278             : {
     279           0 :     MOZ_ASSERT(fop->maybeOnHelperThread());
     280           0 :     if (ForOfPIC::Chain* chain = ForOfPIC::fromJSObject(&obj->as<NativeObject>()))
     281           0 :         chain->sweep(fop);
     282           0 : }
     283             : 
     284             : static void
     285           0 : ForOfPIC_traceObject(JSTracer* trc, JSObject* obj)
     286             : {
     287           0 :     if (ForOfPIC::Chain* chain = ForOfPIC::fromJSObject(&obj->as<NativeObject>()))
     288           0 :         chain->trace(trc);
     289           0 : }
     290             : 
     291             : static const ClassOps ForOfPICClassOps = {
     292             :     nullptr, nullptr, nullptr, nullptr, nullptr,
     293             :     nullptr, nullptr, nullptr, ForOfPIC_finalize,
     294             :     nullptr,              /* call        */
     295             :     nullptr,              /* hasInstance */
     296             :     nullptr,              /* construct   */
     297             :     ForOfPIC_traceObject
     298             : };
     299             : 
     300             : const Class ForOfPIC::class_ = {
     301             :     "ForOfPIC",
     302             :     JSCLASS_HAS_PRIVATE |
     303             :     JSCLASS_BACKGROUND_FINALIZE,
     304             :     &ForOfPICClassOps
     305             : };
     306             : 
     307             : /* static */ NativeObject*
     308          18 : js::ForOfPIC::createForOfPICObject(JSContext* cx, Handle<GlobalObject*> global)
     309             : {
     310          18 :     assertSameCompartment(cx, global);
     311          18 :     NativeObject* obj = NewNativeObjectWithGivenProto(cx, &ForOfPIC::class_, nullptr);
     312          18 :     if (!obj)
     313           0 :         return nullptr;
     314          18 :     ForOfPIC::Chain* chain = cx->new_<ForOfPIC::Chain>();
     315          18 :     if (!chain)
     316           0 :         return nullptr;
     317          18 :     obj->setPrivate(chain);
     318          18 :     return obj;
     319             : }
     320             : 
     321             : /* static */ js::ForOfPIC::Chain*
     322          18 : js::ForOfPIC::create(JSContext* cx)
     323             : {
     324          18 :     MOZ_ASSERT(!cx->global()->getForOfPICObject());
     325          36 :     Rooted<GlobalObject*> global(cx, cx->global());
     326          18 :     NativeObject* obj = GlobalObject::getOrCreateForOfPICObject(cx, global);
     327          18 :     if (!obj)
     328           0 :         return nullptr;
     329          18 :     return fromJSObject(obj);
     330             : }

Generated by: LCOV version 1.13