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 +}