js/src/vm/PIC.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.

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

mercurial