Wed, 31 Dec 2014 06:09:35 +0100
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 | } |