js/src/vm/PIC.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/vm/PIC.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,330 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99:
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "vm/PIC.h"
    1.11 +#include "jscntxt.h"
    1.12 +#include "jsobj.h"
    1.13 +#include "gc/Marking.h"
    1.14 +
    1.15 +#include "vm/GlobalObject.h"
    1.16 +#include "vm/ObjectImpl.h"
    1.17 +#include "vm/SelfHosting.h"
    1.18 +#include "jsobjinlines.h"
    1.19 +#include "vm/ObjectImpl-inl.h"
    1.20 +
    1.21 +using namespace js;
    1.22 +using namespace js::gc;
    1.23 +
    1.24 +bool
    1.25 +js::ForOfPIC::Chain::initialize(JSContext *cx)
    1.26 +{
    1.27 +    JS_ASSERT(!initialized_);
    1.28 +
    1.29 +    // Get the canonical Array.prototype
    1.30 +    RootedObject arrayProto(cx, GlobalObject::getOrCreateArrayPrototype(cx, cx->global()));
    1.31 +    if (!arrayProto)
    1.32 +        return false;
    1.33 +
    1.34 +    // Get the canonical ArrayIterator.prototype
    1.35 +    RootedObject arrayIteratorProto(cx,
    1.36 +                    GlobalObject::getOrCreateArrayIteratorPrototype(cx, cx->global()));
    1.37 +    if (!arrayIteratorProto)
    1.38 +        return false;
    1.39 +
    1.40 +    // From this point on, we can't fail.  Set initialized and fill the fields
    1.41 +    // for the canonical Array.prototype and ArrayIterator.prototype objects.
    1.42 +    initialized_ = true;
    1.43 +    arrayProto_ = arrayProto;
    1.44 +    arrayIteratorProto_ = arrayIteratorProto;
    1.45 +
    1.46 +    // Shortcut returns below means Array for-of will never be optimizable,
    1.47 +    // do set disabled_ now, and clear it later when we succeed.
    1.48 +    disabled_ = true;
    1.49 +
    1.50 +    // Look up '@@iterator' on Array.prototype, ensure it's a slotful shape.
    1.51 +    Shape *iterShape = arrayProto->nativeLookup(cx, cx->names().std_iterator);
    1.52 +    if (!iterShape || !iterShape->hasSlot() || !iterShape->hasDefaultGetter())
    1.53 +        return true;
    1.54 +
    1.55 +    // Get the referred value, and ensure it holds the canonical ArrayValues function.
    1.56 +    Value iterator = arrayProto->getSlot(iterShape->slot());
    1.57 +    JSFunction *iterFun;
    1.58 +    if (!IsFunctionObject(iterator, &iterFun))
    1.59 +        return true;
    1.60 +    if (!IsSelfHostedFunctionWithName(iterFun, cx->names().ArrayValues))
    1.61 +        return true;
    1.62 +
    1.63 +    // Look up the 'next' value on ArrayIterator.prototype
    1.64 +    Shape *nextShape = arrayIteratorProto->nativeLookup(cx, cx->names().next);
    1.65 +    if (!nextShape || !nextShape->hasSlot())
    1.66 +        return true;
    1.67 +
    1.68 +    // Get the referred value, ensure it holds the canonical ArrayIteratorNext function.
    1.69 +    Value next = arrayIteratorProto->getSlot(nextShape->slot());
    1.70 +    JSFunction *nextFun;
    1.71 +    if (!IsFunctionObject(next, &nextFun))
    1.72 +        return true;
    1.73 +    if (!IsSelfHostedFunctionWithName(nextFun, cx->names().ArrayIteratorNext))
    1.74 +        return true;
    1.75 +
    1.76 +    disabled_ = false;
    1.77 +    arrayProtoShape_ = arrayProto->lastProperty();
    1.78 +    arrayProtoIteratorSlot_ = iterShape->slot();
    1.79 +    canonicalIteratorFunc_ = iterator;
    1.80 +    arrayIteratorProtoShape_ = arrayIteratorProto->lastProperty();
    1.81 +    arrayIteratorProtoNextSlot_ = nextShape->slot();
    1.82 +    canonicalNextFunc_ = next;
    1.83 +    return true;
    1.84 +}
    1.85 +
    1.86 +js::ForOfPIC::Stub *
    1.87 +js::ForOfPIC::Chain::isArrayOptimized(ArrayObject *obj)
    1.88 +{
    1.89 +    Stub *stub = getMatchingStub(obj);
    1.90 +    if (!stub)
    1.91 +        return nullptr;
    1.92 +
    1.93 +    // Ensure that this is an otherwise optimizable array.
    1.94 +    if (!isOptimizableArray(obj))
    1.95 +        return nullptr;
    1.96 +
    1.97 +    // Not yet enough!  Ensure that the world as we know it remains sane.
    1.98 +    if (!isArrayStateStillSane())
    1.99 +        return nullptr;
   1.100 +
   1.101 +    return stub;
   1.102 +}
   1.103 +
   1.104 +bool
   1.105 +js::ForOfPIC::Chain::tryOptimizeArray(JSContext *cx, HandleObject array, bool *optimized)
   1.106 +{
   1.107 +    JS_ASSERT(array->is<ArrayObject>());
   1.108 +    JS_ASSERT(optimized);
   1.109 +
   1.110 +    *optimized = false;
   1.111 +
   1.112 +    if (!initialized_) {
   1.113 +        // If PIC is not initialized, initialize it.
   1.114 +        if (!initialize(cx))
   1.115 +            return false;
   1.116 +
   1.117 +    } else if (!disabled_ && !isArrayStateStillSane()) {
   1.118 +        // Otherwise, if array state is no longer sane, reinitialize.
   1.119 +        reset(cx);
   1.120 +
   1.121 +        if (!initialize(cx))
   1.122 +            return false;
   1.123 +    }
   1.124 +    JS_ASSERT(initialized_);
   1.125 +
   1.126 +    // If PIC is disabled, don't bother trying to optimize.
   1.127 +    if (disabled_)
   1.128 +        return true;
   1.129 +
   1.130 +    // By the time we get here, we should have a sane array state to work with.
   1.131 +    JS_ASSERT(isArrayStateStillSane());
   1.132 +
   1.133 +    // Check if stub already exists.
   1.134 +    ForOfPIC::Stub *stub = isArrayOptimized(&array->as<ArrayObject>());
   1.135 +    if (stub) {
   1.136 +        *optimized = true;
   1.137 +        return true;
   1.138 +    }
   1.139 +
   1.140 +    // If the number of stubs is about to exceed the limit, throw away entire
   1.141 +    // existing cache before adding new stubs.  We shouldn't really have heavy
   1.142 +    // churn on these.
   1.143 +    if (numStubs() >= MAX_STUBS)
   1.144 +        eraseChain();
   1.145 +
   1.146 +    // Ensure array's prototype is the actual Array.prototype
   1.147 +    if (!isOptimizableArray(array))
   1.148 +        return true;
   1.149 +
   1.150 +    // Ensure array doesn't define '@@iterator' directly.
   1.151 +    if (array->nativeLookup(cx, cx->names().std_iterator))
   1.152 +        return true;
   1.153 +
   1.154 +    // Good to optimize now, create stub to add.
   1.155 +    RootedShape shape(cx, array->lastProperty());
   1.156 +    stub = cx->new_<Stub>(shape);
   1.157 +    if (!stub)
   1.158 +        return false;
   1.159 +
   1.160 +    // Add the stub.
   1.161 +    addStub(stub);
   1.162 +
   1.163 +    *optimized = true;
   1.164 +    return true;
   1.165 +}
   1.166 +
   1.167 +js::ForOfPIC::Stub *
   1.168 +js::ForOfPIC::Chain::getMatchingStub(JSObject *obj)
   1.169 +{
   1.170 +    // Ensure PIC is initialized and not disabled.
   1.171 +    if (!initialized_ || disabled_)
   1.172 +        return nullptr;
   1.173 +
   1.174 +    // Check if there is a matching stub.
   1.175 +    for (Stub *stub = stubs(); stub != nullptr; stub = stub->next()) {
   1.176 +        if (stub->shape() == obj->lastProperty())
   1.177 +            return stub;
   1.178 +    }
   1.179 +
   1.180 +    return nullptr;
   1.181 +}
   1.182 +
   1.183 +bool
   1.184 +js::ForOfPIC::Chain::isOptimizableArray(JSObject *obj)
   1.185 +{
   1.186 +    JS_ASSERT(obj->is<ArrayObject>());
   1.187 +
   1.188 +    // Ensure object's prototype is the actual Array.prototype
   1.189 +    if (!obj->getTaggedProto().isObject())
   1.190 +        return false;
   1.191 +    if (obj->getTaggedProto().toObject() != arrayProto_)
   1.192 +        return false;
   1.193 +
   1.194 +    return true;
   1.195 +}
   1.196 +
   1.197 +bool
   1.198 +js::ForOfPIC::Chain::isArrayStateStillSane()
   1.199 +{
   1.200 +    // Ensure that canonical Array.prototype has matching shape.
   1.201 +    if (arrayProto_->lastProperty() != arrayProtoShape_)
   1.202 +        return false;
   1.203 +
   1.204 +    // Ensure that Array.prototype['@@iterator'] contains the
   1.205 +    // canonical iterator function.
   1.206 +    if (arrayProto_->getSlot(arrayProtoIteratorSlot_) != canonicalIteratorFunc_)
   1.207 +        return false;
   1.208 +
   1.209 +    // Chain to isArrayNextStillSane.
   1.210 +    return isArrayNextStillSane();
   1.211 +}
   1.212 +
   1.213 +void
   1.214 +js::ForOfPIC::Chain::reset(JSContext *cx)
   1.215 +{
   1.216 +    // Should never reset a disabled_ stub.
   1.217 +    JS_ASSERT(!disabled_);
   1.218 +
   1.219 +    // Erase the chain.
   1.220 +    eraseChain();
   1.221 +
   1.222 +    arrayProto_ = nullptr;
   1.223 +    arrayIteratorProto_ = nullptr;
   1.224 +
   1.225 +    arrayProtoShape_ = nullptr;
   1.226 +    arrayProtoIteratorSlot_ = -1;
   1.227 +    canonicalIteratorFunc_ = UndefinedValue();
   1.228 +
   1.229 +    arrayIteratorProtoShape_ = nullptr;
   1.230 +    arrayIteratorProtoNextSlot_ = -1;
   1.231 +    canonicalNextFunc_ = UndefinedValue();
   1.232 +
   1.233 +    initialized_ = false;
   1.234 +}
   1.235 +
   1.236 +void
   1.237 +js::ForOfPIC::Chain::eraseChain()
   1.238 +{
   1.239 +    // Should never need to clear the chain of a disabled stub.
   1.240 +    JS_ASSERT(!disabled_);
   1.241 +
   1.242 +    // Free all stubs.
   1.243 +    Stub *stub = stubs_;
   1.244 +    while (stub) {
   1.245 +        Stub *next = stub->next();
   1.246 +        js_delete(stub);
   1.247 +        stub = next;
   1.248 +    }
   1.249 +    stubs_ = nullptr;
   1.250 +}
   1.251 +
   1.252 +
   1.253 +// Trace the pointers stored directly on the stub.
   1.254 +void
   1.255 +js::ForOfPIC::Chain::mark(JSTracer *trc)
   1.256 +{
   1.257 +    if (!initialized_ || disabled_)
   1.258 +        return;
   1.259 +
   1.260 +    gc::MarkObject(trc, &arrayProto_, "ForOfPIC Array.prototype.");
   1.261 +    gc::MarkObject(trc, &arrayIteratorProto_, "ForOfPIC ArrayIterator.prototype.");
   1.262 +
   1.263 +    gc::MarkShape(trc, &arrayProtoShape_, "ForOfPIC Array.prototype shape.");
   1.264 +    gc::MarkShape(trc, &arrayIteratorProtoShape_, "ForOfPIC ArrayIterator.prototype shape.");
   1.265 +
   1.266 +    gc::MarkValue(trc, &canonicalIteratorFunc_, "ForOfPIC ArrayValues builtin.");
   1.267 +    gc::MarkValue(trc, &canonicalNextFunc_, "ForOfPIC ArrayIterator.prototype.next builtin.");
   1.268 +
   1.269 +    // Free all the stubs in the chain.
   1.270 +    while (stubs_)
   1.271 +        removeStub(stubs_, nullptr);
   1.272 +}
   1.273 +
   1.274 +void
   1.275 +js::ForOfPIC::Chain::sweep(FreeOp *fop)
   1.276 +{
   1.277 +    // Free all the stubs in the chain.
   1.278 +    while (stubs_) {
   1.279 +        Stub *next = stubs_->next();
   1.280 +        fop->delete_(stubs_);
   1.281 +        stubs_ = next;
   1.282 +    }
   1.283 +    fop->delete_(this);
   1.284 +}
   1.285 +
   1.286 +static void
   1.287 +ForOfPIC_finalize(FreeOp *fop, JSObject *obj)
   1.288 +{
   1.289 +    if (ForOfPIC::Chain *chain = ForOfPIC::fromJSObject(obj))
   1.290 +        chain->sweep(fop);
   1.291 +}
   1.292 +
   1.293 +static void
   1.294 +ForOfPIC_traceObject(JSTracer *trc, JSObject *obj)
   1.295 +{
   1.296 +    if (ForOfPIC::Chain *chain = ForOfPIC::fromJSObject(obj))
   1.297 +        chain->mark(trc);
   1.298 +}
   1.299 +
   1.300 +const Class ForOfPIC::jsclass = {
   1.301 +    "ForOfPIC", JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
   1.302 +    JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
   1.303 +    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, ForOfPIC_finalize,
   1.304 +    nullptr,              /* call        */
   1.305 +    nullptr,              /* hasInstance */
   1.306 +    nullptr,              /* construct   */
   1.307 +    ForOfPIC_traceObject
   1.308 +};
   1.309 +
   1.310 +/* static */ JSObject *
   1.311 +js::ForOfPIC::createForOfPICObject(JSContext *cx, Handle<GlobalObject*> global)
   1.312 +{
   1.313 +    assertSameCompartment(cx, global);
   1.314 +    JSObject *obj = NewObjectWithGivenProto(cx, &ForOfPIC::jsclass, nullptr, global);
   1.315 +    if (!obj)
   1.316 +        return nullptr;
   1.317 +    ForOfPIC::Chain *chain = cx->new_<ForOfPIC::Chain>();
   1.318 +    if (!chain)
   1.319 +        return nullptr;
   1.320 +    obj->setPrivate(chain);
   1.321 +    return obj;
   1.322 +}
   1.323 +
   1.324 +/* static */ js::ForOfPIC::Chain *
   1.325 +js::ForOfPIC::create(JSContext *cx)
   1.326 +{
   1.327 +    JS_ASSERT(!cx->global()->getForOfPICObject());
   1.328 +    Rooted<GlobalObject *> global(cx, cx->global());
   1.329 +    JSObject *obj = GlobalObject::getOrCreateForOfPICObject(cx, global);
   1.330 +    if (!obj)
   1.331 +        return nullptr;
   1.332 +    return fromJSObject(obj);
   1.333 +}

mercurial