1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/vm/Shape.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1852 @@ 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 +/* JS symbol tables. */ 1.11 + 1.12 +#include "vm/Shape-inl.h" 1.13 + 1.14 +#include "mozilla/DebugOnly.h" 1.15 +#include "mozilla/MathAlgorithms.h" 1.16 +#include "mozilla/PodOperations.h" 1.17 + 1.18 +#include "jsatom.h" 1.19 +#include "jscntxt.h" 1.20 +#include "jshashutil.h" 1.21 +#include "jsobj.h" 1.22 + 1.23 +#include "js/HashTable.h" 1.24 + 1.25 +#include "jscntxtinlines.h" 1.26 +#include "jsobjinlines.h" 1.27 + 1.28 +#include "vm/ObjectImpl-inl.h" 1.29 +#include "vm/Runtime-inl.h" 1.30 + 1.31 +using namespace js; 1.32 +using namespace js::gc; 1.33 + 1.34 +using mozilla::CeilingLog2Size; 1.35 +using mozilla::DebugOnly; 1.36 +using mozilla::PodZero; 1.37 +using mozilla::RotateLeft; 1.38 + 1.39 +bool 1.40 +ShapeTable::init(ThreadSafeContext *cx, Shape *lastProp) 1.41 +{ 1.42 + /* 1.43 + * Either we're creating a table for a large scope that was populated 1.44 + * via property cache hit logic under JSOP_INITPROP, JSOP_SETNAME, or 1.45 + * JSOP_SETPROP; or else calloc failed at least once already. In any 1.46 + * event, let's try to grow, overallocating to hold at least twice the 1.47 + * current population. 1.48 + */ 1.49 + uint32_t sizeLog2 = CeilingLog2Size(2 * entryCount); 1.50 + if (sizeLog2 < MIN_SIZE_LOG2) 1.51 + sizeLog2 = MIN_SIZE_LOG2; 1.52 + 1.53 + /* 1.54 + * Use rt->calloc_ for memory accounting and overpressure handling 1.55 + * without OOM reporting. See ShapeTable::change. 1.56 + */ 1.57 + entries = (Shape **) cx->calloc_(sizeOfEntries(JS_BIT(sizeLog2))); 1.58 + if (!entries) 1.59 + return false; 1.60 + 1.61 + hashShift = HASH_BITS - sizeLog2; 1.62 + for (Shape::Range<NoGC> r(lastProp); !r.empty(); r.popFront()) { 1.63 + Shape &shape = r.front(); 1.64 + JS_ASSERT(cx->isThreadLocal(&shape)); 1.65 + Shape **spp = search(shape.propid(), true); 1.66 + 1.67 + /* 1.68 + * Beware duplicate args and arg vs. var conflicts: the youngest shape 1.69 + * (nearest to lastProp) must win. See bug 600067. 1.70 + */ 1.71 + if (!SHAPE_FETCH(spp)) 1.72 + SHAPE_STORE_PRESERVING_COLLISION(spp, &shape); 1.73 + } 1.74 + return true; 1.75 +} 1.76 + 1.77 +void 1.78 +Shape::removeFromDictionary(ObjectImpl *obj) 1.79 +{ 1.80 + JS_ASSERT(inDictionary()); 1.81 + JS_ASSERT(obj->inDictionaryMode()); 1.82 + JS_ASSERT(listp); 1.83 + 1.84 + JS_ASSERT(obj->shape_->inDictionary()); 1.85 + JS_ASSERT(obj->shape_->listp == &obj->shape_); 1.86 + 1.87 + if (parent) 1.88 + parent->listp = listp; 1.89 + *listp = parent; 1.90 + listp = nullptr; 1.91 +} 1.92 + 1.93 +void 1.94 +Shape::insertIntoDictionary(HeapPtrShape *dictp) 1.95 +{ 1.96 + // Don't assert inDictionaryMode() here because we may be called from 1.97 + // JSObject::toDictionaryMode via JSObject::newDictionaryShape. 1.98 + JS_ASSERT(inDictionary()); 1.99 + JS_ASSERT(!listp); 1.100 + 1.101 + JS_ASSERT_IF(*dictp, (*dictp)->inDictionary()); 1.102 + JS_ASSERT_IF(*dictp, (*dictp)->listp == dictp); 1.103 + JS_ASSERT_IF(*dictp, compartment() == (*dictp)->compartment()); 1.104 + 1.105 + setParent(dictp->get()); 1.106 + if (parent) 1.107 + parent->listp = &parent; 1.108 + listp = (HeapPtrShape *) dictp; 1.109 + *dictp = this; 1.110 +} 1.111 + 1.112 +bool 1.113 +Shape::makeOwnBaseShape(ThreadSafeContext *cx) 1.114 +{ 1.115 + JS_ASSERT(!base()->isOwned()); 1.116 + JS_ASSERT(cx->isThreadLocal(this)); 1.117 + assertSameCompartmentDebugOnly(cx, compartment()); 1.118 + 1.119 + BaseShape *nbase = js_NewGCBaseShape<NoGC>(cx); 1.120 + if (!nbase) 1.121 + return false; 1.122 + 1.123 + new (nbase) BaseShape(StackBaseShape(this)); 1.124 + nbase->setOwned(base()->toUnowned()); 1.125 + 1.126 + this->base_ = nbase; 1.127 + 1.128 + return true; 1.129 +} 1.130 + 1.131 +void 1.132 +Shape::handoffTableTo(Shape *shape) 1.133 +{ 1.134 + JS_ASSERT(inDictionary() && shape->inDictionary()); 1.135 + 1.136 + if (this == shape) 1.137 + return; 1.138 + 1.139 + JS_ASSERT(base()->isOwned() && !shape->base()->isOwned()); 1.140 + 1.141 + BaseShape *nbase = base(); 1.142 + 1.143 + JS_ASSERT_IF(shape->hasSlot(), nbase->slotSpan() > shape->slot()); 1.144 + 1.145 + this->base_ = nbase->baseUnowned(); 1.146 + nbase->adoptUnowned(shape->base()->toUnowned()); 1.147 + 1.148 + shape->base_ = nbase; 1.149 +} 1.150 + 1.151 +/* static */ bool 1.152 +Shape::hashify(ThreadSafeContext *cx, Shape *shape) 1.153 +{ 1.154 + JS_ASSERT(!shape->hasTable()); 1.155 + 1.156 + if (!shape->ensureOwnBaseShape(cx)) 1.157 + return false; 1.158 + 1.159 + ShapeTable *table = cx->new_<ShapeTable>(shape->entryCount()); 1.160 + if (!table) 1.161 + return false; 1.162 + 1.163 + if (!table->init(cx, shape)) { 1.164 + js_free(table); 1.165 + return false; 1.166 + } 1.167 + 1.168 + shape->base()->setTable(table); 1.169 + return true; 1.170 +} 1.171 + 1.172 +/* 1.173 + * Double hashing needs the second hash code to be relatively prime to table 1.174 + * size, so we simply make hash2 odd. 1.175 + */ 1.176 +#define HASH1(hash0,shift) ((hash0) >> (shift)) 1.177 +#define HASH2(hash0,log2,shift) ((((hash0) << (log2)) >> (shift)) | 1) 1.178 + 1.179 +Shape ** 1.180 +ShapeTable::search(jsid id, bool adding) 1.181 +{ 1.182 + js::HashNumber hash0, hash1, hash2; 1.183 + int sizeLog2; 1.184 + Shape *stored, *shape, **spp, **firstRemoved; 1.185 + uint32_t sizeMask; 1.186 + 1.187 + JS_ASSERT(entries); 1.188 + JS_ASSERT(!JSID_IS_EMPTY(id)); 1.189 + 1.190 + /* Compute the primary hash address. */ 1.191 + hash0 = HashId(id); 1.192 + hash1 = HASH1(hash0, hashShift); 1.193 + spp = entries + hash1; 1.194 + 1.195 + /* Miss: return space for a new entry. */ 1.196 + stored = *spp; 1.197 + if (SHAPE_IS_FREE(stored)) 1.198 + return spp; 1.199 + 1.200 + /* Hit: return entry. */ 1.201 + shape = SHAPE_CLEAR_COLLISION(stored); 1.202 + if (shape && shape->propidRaw() == id) 1.203 + return spp; 1.204 + 1.205 + /* Collision: double hash. */ 1.206 + sizeLog2 = HASH_BITS - hashShift; 1.207 + hash2 = HASH2(hash0, sizeLog2, hashShift); 1.208 + sizeMask = JS_BITMASK(sizeLog2); 1.209 + 1.210 +#ifdef DEBUG 1.211 + uintptr_t collision_flag = SHAPE_COLLISION; 1.212 +#endif 1.213 + 1.214 + /* Save the first removed entry pointer so we can recycle it if adding. */ 1.215 + if (SHAPE_IS_REMOVED(stored)) { 1.216 + firstRemoved = spp; 1.217 + } else { 1.218 + firstRemoved = nullptr; 1.219 + if (adding && !SHAPE_HAD_COLLISION(stored)) 1.220 + SHAPE_FLAG_COLLISION(spp, shape); 1.221 +#ifdef DEBUG 1.222 + collision_flag &= uintptr_t(*spp) & SHAPE_COLLISION; 1.223 +#endif 1.224 + } 1.225 + 1.226 + for (;;) { 1.227 + hash1 -= hash2; 1.228 + hash1 &= sizeMask; 1.229 + spp = entries + hash1; 1.230 + 1.231 + stored = *spp; 1.232 + if (SHAPE_IS_FREE(stored)) 1.233 + return (adding && firstRemoved) ? firstRemoved : spp; 1.234 + 1.235 + shape = SHAPE_CLEAR_COLLISION(stored); 1.236 + if (shape && shape->propidRaw() == id) { 1.237 + JS_ASSERT(collision_flag); 1.238 + return spp; 1.239 + } 1.240 + 1.241 + if (SHAPE_IS_REMOVED(stored)) { 1.242 + if (!firstRemoved) 1.243 + firstRemoved = spp; 1.244 + } else { 1.245 + if (adding && !SHAPE_HAD_COLLISION(stored)) 1.246 + SHAPE_FLAG_COLLISION(spp, shape); 1.247 +#ifdef DEBUG 1.248 + collision_flag &= uintptr_t(*spp) & SHAPE_COLLISION; 1.249 +#endif 1.250 + } 1.251 + } 1.252 + 1.253 + /* NOTREACHED */ 1.254 + return nullptr; 1.255 +} 1.256 + 1.257 +bool 1.258 +ShapeTable::change(int log2Delta, ThreadSafeContext *cx) 1.259 +{ 1.260 + JS_ASSERT(entries); 1.261 + 1.262 + /* 1.263 + * Grow, shrink, or compress by changing this->entries. 1.264 + */ 1.265 + int oldlog2 = HASH_BITS - hashShift; 1.266 + int newlog2 = oldlog2 + log2Delta; 1.267 + uint32_t oldsize = JS_BIT(oldlog2); 1.268 + uint32_t newsize = JS_BIT(newlog2); 1.269 + Shape **newTable = (Shape **) cx->calloc_(sizeOfEntries(newsize)); 1.270 + if (!newTable) 1.271 + return false; 1.272 + 1.273 + /* Now that we have newTable allocated, update members. */ 1.274 + hashShift = HASH_BITS - newlog2; 1.275 + removedCount = 0; 1.276 + Shape **oldTable = entries; 1.277 + entries = newTable; 1.278 + 1.279 + /* Copy only live entries, leaving removed and free ones behind. */ 1.280 + for (Shape **oldspp = oldTable; oldsize != 0; oldspp++) { 1.281 + Shape *shape = SHAPE_FETCH(oldspp); 1.282 + JS_ASSERT(cx->isThreadLocal(shape)); 1.283 + if (shape) { 1.284 + Shape **spp = search(shape->propid(), true); 1.285 + JS_ASSERT(SHAPE_IS_FREE(*spp)); 1.286 + *spp = shape; 1.287 + } 1.288 + oldsize--; 1.289 + } 1.290 + 1.291 + /* Finally, free the old entries storage. */ 1.292 + js_free(oldTable); 1.293 + return true; 1.294 +} 1.295 + 1.296 +bool 1.297 +ShapeTable::grow(ThreadSafeContext *cx) 1.298 +{ 1.299 + JS_ASSERT(needsToGrow()); 1.300 + 1.301 + uint32_t size = capacity(); 1.302 + int delta = removedCount < size >> 2; 1.303 + 1.304 + if (!change(delta, cx) && entryCount + removedCount == size - 1) { 1.305 + js_ReportOutOfMemory(cx); 1.306 + return false; 1.307 + } 1.308 + return true; 1.309 +} 1.310 + 1.311 +/* static */ Shape * 1.312 +Shape::replaceLastProperty(ExclusiveContext *cx, StackBaseShape &base, 1.313 + TaggedProto proto, HandleShape shape) 1.314 +{ 1.315 + JS_ASSERT(!shape->inDictionary()); 1.316 + 1.317 + if (!shape->parent) { 1.318 + /* Treat as resetting the initial property of the shape hierarchy. */ 1.319 + AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots()); 1.320 + return EmptyShape::getInitialShape(cx, base.clasp, proto, 1.321 + base.parent, base.metadata, kind, 1.322 + base.flags & BaseShape::OBJECT_FLAG_MASK); 1.323 + } 1.324 + 1.325 + UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base); 1.326 + if (!nbase) 1.327 + return nullptr; 1.328 + 1.329 + StackShape child(shape); 1.330 + child.base = nbase; 1.331 + 1.332 + return cx->compartment()->propertyTree.getChild(cx, shape->parent, child); 1.333 +} 1.334 + 1.335 +/* 1.336 + * Get or create a property-tree or dictionary child property of |parent|, 1.337 + * which must be lastProperty() if inDictionaryMode(), else parent must be 1.338 + * one of lastProperty() or lastProperty()->parent. 1.339 + */ 1.340 +/* static */ Shape * 1.341 +JSObject::getChildPropertyOnDictionary(ThreadSafeContext *cx, JS::HandleObject obj, 1.342 + HandleShape parent, js::StackShape &child) 1.343 +{ 1.344 + /* 1.345 + * Shared properties have no slot, but slot_ will reflect that of parent. 1.346 + * Unshared properties allocate a slot here but may lose it due to a 1.347 + * JS_ClearScope call. 1.348 + */ 1.349 + if (!child.hasSlot()) { 1.350 + child.setSlot(parent->maybeSlot()); 1.351 + } else { 1.352 + if (child.hasMissingSlot()) { 1.353 + uint32_t slot; 1.354 + if (!allocSlot(cx, obj, &slot)) 1.355 + return nullptr; 1.356 + child.setSlot(slot); 1.357 + } else { 1.358 + /* 1.359 + * Slots can only be allocated out of order on objects in 1.360 + * dictionary mode. Otherwise the child's slot must be after the 1.361 + * parent's slot (if it has one), because slot number determines 1.362 + * slot span for objects with that shape. Usually child slot 1.363 + * *immediately* follows parent slot, but there may be a slot gap 1.364 + * when the object uses some -- but not all -- of its reserved 1.365 + * slots to store properties. 1.366 + */ 1.367 + JS_ASSERT(obj->inDictionaryMode() || 1.368 + parent->hasMissingSlot() || 1.369 + child.slot() == parent->maybeSlot() + 1 || 1.370 + (parent->maybeSlot() + 1 < JSSLOT_FREE(obj->getClass()) && 1.371 + child.slot() == JSSLOT_FREE(obj->getClass()))); 1.372 + } 1.373 + } 1.374 + 1.375 + RootedShape shape(cx); 1.376 + 1.377 + if (obj->inDictionaryMode()) { 1.378 + JS_ASSERT(parent == obj->lastProperty()); 1.379 + RootedGeneric<StackShape*> childRoot(cx, &child); 1.380 + shape = js_NewGCShape(cx); 1.381 + if (!shape) 1.382 + return nullptr; 1.383 + if (childRoot->hasSlot() && childRoot->slot() >= obj->lastProperty()->base()->slotSpan()) { 1.384 + if (!JSObject::setSlotSpan(cx, obj, childRoot->slot() + 1)) 1.385 + return nullptr; 1.386 + } 1.387 + shape->initDictionaryShape(*childRoot, obj->numFixedSlots(), &obj->shape_); 1.388 + } 1.389 + 1.390 + return shape; 1.391 +} 1.392 + 1.393 +/* static */ Shape * 1.394 +JSObject::getChildProperty(ExclusiveContext *cx, 1.395 + HandleObject obj, HandleShape parent, StackShape &unrootedChild) 1.396 +{ 1.397 + RootedGeneric<StackShape*> child(cx, &unrootedChild); 1.398 + RootedShape shape(cx, getChildPropertyOnDictionary(cx, obj, parent, *child)); 1.399 + 1.400 + if (!obj->inDictionaryMode()) { 1.401 + shape = cx->compartment()->propertyTree.getChild(cx, parent, *child); 1.402 + if (!shape) 1.403 + return nullptr; 1.404 + //JS_ASSERT(shape->parent == parent); 1.405 + //JS_ASSERT_IF(parent != lastProperty(), parent == lastProperty()->parent); 1.406 + if (!JSObject::setLastProperty(cx, obj, shape)) 1.407 + return nullptr; 1.408 + } 1.409 + 1.410 + return shape; 1.411 +} 1.412 + 1.413 +/* static */ Shape * 1.414 +JSObject::lookupChildProperty(ThreadSafeContext *cx, 1.415 + HandleObject obj, HandleShape parent, StackShape &unrootedChild) 1.416 +{ 1.417 + RootedGeneric<StackShape*> child(cx, &unrootedChild); 1.418 + JS_ASSERT(cx->isThreadLocal(obj)); 1.419 + 1.420 + RootedShape shape(cx, getChildPropertyOnDictionary(cx, obj, parent, *child)); 1.421 + 1.422 + if (!obj->inDictionaryMode()) { 1.423 + shape = cx->compartment_->propertyTree.lookupChild(cx, parent, *child); 1.424 + if (!shape) 1.425 + return nullptr; 1.426 + if (!JSObject::setLastProperty(cx, obj, shape)) 1.427 + return nullptr; 1.428 + } 1.429 + 1.430 + return shape; 1.431 +} 1.432 + 1.433 +bool 1.434 +js::ObjectImpl::toDictionaryMode(ThreadSafeContext *cx) 1.435 +{ 1.436 + JS_ASSERT(!inDictionaryMode()); 1.437 + 1.438 + /* We allocate the shapes from cx->compartment(), so make sure it's right. */ 1.439 + JS_ASSERT(cx->isInsideCurrentCompartment(this)); 1.440 + 1.441 + /* 1.442 + * This function is thread safe as long as the object is thread local. It 1.443 + * does not modify the shared shapes, and only allocates newly allocated 1.444 + * (and thus also thread local) shapes. 1.445 + */ 1.446 + JS_ASSERT(cx->isThreadLocal(this)); 1.447 + 1.448 + uint32_t span = slotSpan(); 1.449 + 1.450 + Rooted<ObjectImpl*> self(cx, this); 1.451 + 1.452 + /* 1.453 + * Clone the shapes into a new dictionary list. Don't update the 1.454 + * last property of this object until done, otherwise a GC 1.455 + * triggered while creating the dictionary will get the wrong 1.456 + * slot span for this object. 1.457 + */ 1.458 + RootedShape root(cx); 1.459 + RootedShape dictionaryShape(cx); 1.460 + 1.461 + RootedShape shape(cx, lastProperty()); 1.462 + while (shape) { 1.463 + JS_ASSERT(!shape->inDictionary()); 1.464 + 1.465 + Shape *dprop = js_NewGCShape(cx); 1.466 + if (!dprop) { 1.467 + js_ReportOutOfMemory(cx); 1.468 + return false; 1.469 + } 1.470 + 1.471 + HeapPtrShape *listp = dictionaryShape 1.472 + ? &dictionaryShape->parent 1.473 + : (HeapPtrShape *) root.address(); 1.474 + 1.475 + StackShape child(shape); 1.476 + dprop->initDictionaryShape(child, self->numFixedSlots(), listp); 1.477 + 1.478 + JS_ASSERT(!dprop->hasTable()); 1.479 + dictionaryShape = dprop; 1.480 + shape = shape->previous(); 1.481 + } 1.482 + 1.483 + if (!Shape::hashify(cx, root)) { 1.484 + js_ReportOutOfMemory(cx); 1.485 + return false; 1.486 + } 1.487 + 1.488 + JS_ASSERT((Shape **) root->listp == root.address()); 1.489 + root->listp = &self->shape_; 1.490 + self->shape_ = root; 1.491 + 1.492 + JS_ASSERT(self->inDictionaryMode()); 1.493 + root->base()->setSlotSpan(span); 1.494 + 1.495 + return true; 1.496 +} 1.497 + 1.498 +/* 1.499 + * Normalize stub getter and setter values for faster is-stub testing in the 1.500 + * SHAPE_CALL_[GS]ETTER macros. 1.501 + */ 1.502 +static inline bool 1.503 +NormalizeGetterAndSetter(JSObject *obj, 1.504 + jsid id, unsigned attrs, unsigned flags, 1.505 + PropertyOp &getter, 1.506 + StrictPropertyOp &setter) 1.507 +{ 1.508 + if (setter == JS_StrictPropertyStub) { 1.509 + JS_ASSERT(!(attrs & JSPROP_SETTER)); 1.510 + setter = nullptr; 1.511 + } 1.512 + if (getter == JS_PropertyStub) { 1.513 + JS_ASSERT(!(attrs & JSPROP_GETTER)); 1.514 + getter = nullptr; 1.515 + } 1.516 + 1.517 + return true; 1.518 +} 1.519 + 1.520 +/* static */ Shape * 1.521 +JSObject::addProperty(ExclusiveContext *cx, HandleObject obj, HandleId id, 1.522 + PropertyOp getter, StrictPropertyOp setter, 1.523 + uint32_t slot, unsigned attrs, 1.524 + unsigned flags, bool allowDictionary) 1.525 +{ 1.526 + JS_ASSERT(!JSID_IS_VOID(id)); 1.527 + 1.528 + bool extensible; 1.529 + if (!JSObject::isExtensible(cx, obj, &extensible)) 1.530 + return nullptr; 1.531 + if (!extensible) { 1.532 + if (cx->isJSContext()) 1.533 + obj->reportNotExtensible(cx->asJSContext()); 1.534 + return nullptr; 1.535 + } 1.536 + 1.537 + NormalizeGetterAndSetter(obj, id, attrs, flags, getter, setter); 1.538 + 1.539 + Shape **spp = nullptr; 1.540 + if (obj->inDictionaryMode()) 1.541 + spp = obj->lastProperty()->table().search(id, true); 1.542 + 1.543 + return addPropertyInternal<SequentialExecution>(cx, obj, id, getter, setter, slot, attrs, 1.544 + flags, spp, allowDictionary); 1.545 +} 1.546 + 1.547 +static bool 1.548 +ShouldConvertToDictionary(JSObject *obj) 1.549 +{ 1.550 + /* 1.551 + * Use a lower limit if this object is likely a hashmap (SETELEM was used 1.552 + * to set properties). 1.553 + */ 1.554 + if (obj->hadElementsAccess()) 1.555 + return obj->lastProperty()->entryCount() >= PropertyTree::MAX_HEIGHT_WITH_ELEMENTS_ACCESS; 1.556 + return obj->lastProperty()->entryCount() >= PropertyTree::MAX_HEIGHT; 1.557 +} 1.558 + 1.559 +template <ExecutionMode mode> 1.560 +static inline UnownedBaseShape * 1.561 +GetOrLookupUnownedBaseShape(typename ExecutionModeTraits<mode>::ExclusiveContextType cx, 1.562 + StackBaseShape &base) 1.563 +{ 1.564 + if (mode == ParallelExecution) 1.565 + return BaseShape::lookupUnowned(cx, base); 1.566 + return BaseShape::getUnowned(cx->asExclusiveContext(), base); 1.567 +} 1.568 + 1.569 +template <ExecutionMode mode> 1.570 +/* static */ Shape * 1.571 +JSObject::addPropertyInternal(typename ExecutionModeTraits<mode>::ExclusiveContextType cx, 1.572 + HandleObject obj, HandleId id, 1.573 + PropertyOp getter, StrictPropertyOp setter, 1.574 + uint32_t slot, unsigned attrs, 1.575 + unsigned flags, Shape **spp, 1.576 + bool allowDictionary) 1.577 +{ 1.578 + JS_ASSERT(cx->isThreadLocal(obj)); 1.579 + JS_ASSERT_IF(!allowDictionary, !obj->inDictionaryMode()); 1.580 + 1.581 + AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter); 1.582 + 1.583 + /* 1.584 + * The code below deals with either converting obj to dictionary mode or 1.585 + * growing an object that's already in dictionary mode. Either way, 1.586 + * dictionray operations are safe if thread local. 1.587 + */ 1.588 + ShapeTable *table = nullptr; 1.589 + if (!obj->inDictionaryMode()) { 1.590 + bool stableSlot = 1.591 + (slot == SHAPE_INVALID_SLOT) || 1.592 + obj->lastProperty()->hasMissingSlot() || 1.593 + (slot == obj->lastProperty()->maybeSlot() + 1); 1.594 + JS_ASSERT_IF(!allowDictionary, stableSlot); 1.595 + if (allowDictionary && 1.596 + (!stableSlot || ShouldConvertToDictionary(obj))) 1.597 + { 1.598 + if (!obj->toDictionaryMode(cx)) 1.599 + return nullptr; 1.600 + table = &obj->lastProperty()->table(); 1.601 + spp = table->search(id, true); 1.602 + } 1.603 + } else { 1.604 + table = &obj->lastProperty()->table(); 1.605 + if (table->needsToGrow()) { 1.606 + if (!table->grow(cx)) 1.607 + return nullptr; 1.608 + spp = table->search(id, true); 1.609 + JS_ASSERT(!SHAPE_FETCH(spp)); 1.610 + } 1.611 + } 1.612 + 1.613 + JS_ASSERT(!!table == !!spp); 1.614 + 1.615 + /* Find or create a property tree node labeled by our arguments. */ 1.616 + RootedShape shape(cx); 1.617 + { 1.618 + RootedShape last(cx, obj->lastProperty()); 1.619 + 1.620 + uint32_t index; 1.621 + bool indexed = js_IdIsIndex(id, &index); 1.622 + 1.623 + Rooted<UnownedBaseShape*> nbase(cx); 1.624 + if (last->base()->matchesGetterSetter(getter, setter) && !indexed) { 1.625 + nbase = last->base()->unowned(); 1.626 + } else { 1.627 + StackBaseShape base(last->base()); 1.628 + base.updateGetterSetter(attrs, getter, setter); 1.629 + if (indexed) 1.630 + base.flags |= BaseShape::INDEXED; 1.631 + nbase = GetOrLookupUnownedBaseShape<mode>(cx, base); 1.632 + if (!nbase) 1.633 + return nullptr; 1.634 + } 1.635 + 1.636 + StackShape child(nbase, id, slot, attrs, flags); 1.637 + shape = getOrLookupChildProperty<mode>(cx, obj, last, child); 1.638 + } 1.639 + 1.640 + if (shape) { 1.641 + JS_ASSERT(shape == obj->lastProperty()); 1.642 + 1.643 + if (table) { 1.644 + /* Store the tree node pointer in the table entry for id. */ 1.645 + SHAPE_STORE_PRESERVING_COLLISION(spp, static_cast<Shape *>(shape)); 1.646 + ++table->entryCount; 1.647 + 1.648 + /* Pass the table along to the new last property, namely shape. */ 1.649 + JS_ASSERT(&shape->parent->table() == table); 1.650 + shape->parent->handoffTableTo(shape); 1.651 + } 1.652 + 1.653 + obj->checkShapeConsistency(); 1.654 + return shape; 1.655 + } 1.656 + 1.657 + obj->checkShapeConsistency(); 1.658 + return nullptr; 1.659 +} 1.660 + 1.661 +template /* static */ Shape * 1.662 +JSObject::addPropertyInternal<SequentialExecution>(ExclusiveContext *cx, 1.663 + HandleObject obj, HandleId id, 1.664 + PropertyOp getter, StrictPropertyOp setter, 1.665 + uint32_t slot, unsigned attrs, 1.666 + unsigned flags, Shape **spp, 1.667 + bool allowDictionary); 1.668 +template /* static */ Shape * 1.669 +JSObject::addPropertyInternal<ParallelExecution>(ForkJoinContext *cx, 1.670 + HandleObject obj, HandleId id, 1.671 + PropertyOp getter, StrictPropertyOp setter, 1.672 + uint32_t slot, unsigned attrs, 1.673 + unsigned flags, Shape **spp, 1.674 + bool allowDictionary); 1.675 + 1.676 +JSObject * 1.677 +js::NewReshapedObject(JSContext *cx, HandleTypeObject type, JSObject *parent, 1.678 + gc::AllocKind allocKind, HandleShape shape, NewObjectKind newKind) 1.679 +{ 1.680 + RootedObject res(cx, NewObjectWithType(cx, type, parent, allocKind, newKind)); 1.681 + if (!res) 1.682 + return nullptr; 1.683 + 1.684 + if (shape->isEmptyShape()) 1.685 + return res; 1.686 + 1.687 + /* Get all the ids in the object, in order. */ 1.688 + js::AutoIdVector ids(cx); 1.689 + { 1.690 + for (unsigned i = 0; i <= shape->slot(); i++) { 1.691 + if (!ids.append(JSID_VOID)) 1.692 + return nullptr; 1.693 + } 1.694 + Shape *nshape = shape; 1.695 + while (!nshape->isEmptyShape()) { 1.696 + ids[nshape->slot()] = nshape->propid(); 1.697 + nshape = nshape->previous(); 1.698 + } 1.699 + } 1.700 + 1.701 + /* Construct the new shape, without updating type information. */ 1.702 + RootedId id(cx); 1.703 + RootedShape newShape(cx, res->lastProperty()); 1.704 + for (unsigned i = 0; i < ids.length(); i++) { 1.705 + id = ids[i]; 1.706 + JS_ASSERT(!res->nativeContains(cx, id)); 1.707 + 1.708 + uint32_t index; 1.709 + bool indexed = js_IdIsIndex(id, &index); 1.710 + 1.711 + Rooted<UnownedBaseShape*> nbase(cx, newShape->base()->unowned()); 1.712 + if (indexed) { 1.713 + StackBaseShape base(nbase); 1.714 + base.flags |= BaseShape::INDEXED; 1.715 + nbase = GetOrLookupUnownedBaseShape<SequentialExecution>(cx, base); 1.716 + if (!nbase) 1.717 + return nullptr; 1.718 + } 1.719 + 1.720 + StackShape child(nbase, id, i, JSPROP_ENUMERATE, 0); 1.721 + newShape = cx->compartment()->propertyTree.getChild(cx, newShape, child); 1.722 + if (!newShape) 1.723 + return nullptr; 1.724 + if (!JSObject::setLastProperty(cx, res, newShape)) 1.725 + return nullptr; 1.726 + } 1.727 + 1.728 + return res; 1.729 +} 1.730 + 1.731 +/* 1.732 + * Check and adjust the new attributes for the shape to make sure that our 1.733 + * slot access optimizations are sound. It is responsibility of the callers to 1.734 + * enforce all restrictions from ECMA-262 v5 8.12.9 [[DefineOwnProperty]]. 1.735 + */ 1.736 +static inline bool 1.737 +CheckCanChangeAttrs(ThreadSafeContext *cx, JSObject *obj, Shape *shape, unsigned *attrsp) 1.738 +{ 1.739 + if (shape->configurable()) 1.740 + return true; 1.741 + 1.742 + /* A permanent property must stay permanent. */ 1.743 + *attrsp |= JSPROP_PERMANENT; 1.744 + 1.745 + /* Reject attempts to remove a slot from the permanent data property. */ 1.746 + if (shape->isDataDescriptor() && shape->hasSlot() && 1.747 + (*attrsp & (JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED))) 1.748 + { 1.749 + if (cx->isJSContext()) 1.750 + obj->reportNotConfigurable(cx->asJSContext(), shape->propid()); 1.751 + return false; 1.752 + } 1.753 + 1.754 + return true; 1.755 +} 1.756 + 1.757 +template <ExecutionMode mode> 1.758 +/* static */ Shape * 1.759 +JSObject::putProperty(typename ExecutionModeTraits<mode>::ExclusiveContextType cx, 1.760 + HandleObject obj, HandleId id, 1.761 + PropertyOp getter, StrictPropertyOp setter, 1.762 + uint32_t slot, unsigned attrs, unsigned flags) 1.763 +{ 1.764 + JS_ASSERT(cx->isThreadLocal(obj)); 1.765 + JS_ASSERT(!JSID_IS_VOID(id)); 1.766 + 1.767 +#ifdef DEBUG 1.768 + if (obj->is<ArrayObject>()) { 1.769 + ArrayObject *arr = &obj->as<ArrayObject>(); 1.770 + uint32_t index; 1.771 + if (js_IdIsIndex(id, &index)) 1.772 + JS_ASSERT(index < arr->length() || arr->lengthIsWritable()); 1.773 + } 1.774 +#endif 1.775 + 1.776 + NormalizeGetterAndSetter(obj, id, attrs, flags, getter, setter); 1.777 + 1.778 + AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter); 1.779 + 1.780 + /* 1.781 + * Search for id in order to claim its entry if table has been allocated. 1.782 + * 1.783 + * Note that we can only try to claim an entry in a table that is thread 1.784 + * local. An object may be thread local *without* its shape being thread 1.785 + * local. The only thread local objects that *also* have thread local 1.786 + * shapes are dictionaries that were allocated/converted thread 1.787 + * locally. Only for those objects we can try to claim an entry in its 1.788 + * shape table. 1.789 + */ 1.790 + Shape **spp; 1.791 + RootedShape shape(cx, (mode == ParallelExecution 1.792 + ? Shape::searchThreadLocal(cx, obj->lastProperty(), id, &spp, 1.793 + cx->isThreadLocal(obj->lastProperty())) 1.794 + : Shape::search(cx->asExclusiveContext(), obj->lastProperty(), id, 1.795 + &spp, true))); 1.796 + if (!shape) { 1.797 + /* 1.798 + * You can't add properties to a non-extensible object, but you can change 1.799 + * attributes of properties in such objects. 1.800 + */ 1.801 + bool extensible; 1.802 + 1.803 + if (mode == ParallelExecution) { 1.804 + if (obj->is<ProxyObject>()) 1.805 + return nullptr; 1.806 + extensible = obj->nonProxyIsExtensible(); 1.807 + } else { 1.808 + if (!JSObject::isExtensible(cx->asExclusiveContext(), obj, &extensible)) 1.809 + return nullptr; 1.810 + } 1.811 + 1.812 + if (!extensible) { 1.813 + if (cx->isJSContext()) 1.814 + obj->reportNotExtensible(cx->asJSContext()); 1.815 + return nullptr; 1.816 + } 1.817 + 1.818 + return addPropertyInternal<mode>(cx, obj, id, getter, setter, slot, attrs, flags, 1.819 + spp, true); 1.820 + } 1.821 + 1.822 + /* Property exists: search must have returned a valid *spp. */ 1.823 + JS_ASSERT_IF(spp, !SHAPE_IS_REMOVED(*spp)); 1.824 + 1.825 + if (!CheckCanChangeAttrs(cx, obj, shape, &attrs)) 1.826 + return nullptr; 1.827 + 1.828 + /* 1.829 + * If the caller wants to allocate a slot, but doesn't care which slot, 1.830 + * copy the existing shape's slot into slot so we can match shape, if all 1.831 + * other members match. 1.832 + */ 1.833 + bool hadSlot = shape->hasSlot(); 1.834 + uint32_t oldSlot = shape->maybeSlot(); 1.835 + if (!(attrs & JSPROP_SHARED) && slot == SHAPE_INVALID_SLOT && hadSlot) 1.836 + slot = oldSlot; 1.837 + 1.838 + Rooted<UnownedBaseShape*> nbase(cx); 1.839 + { 1.840 + uint32_t index; 1.841 + bool indexed = js_IdIsIndex(id, &index); 1.842 + StackBaseShape base(obj->lastProperty()->base()); 1.843 + base.updateGetterSetter(attrs, getter, setter); 1.844 + if (indexed) 1.845 + base.flags |= BaseShape::INDEXED; 1.846 + nbase = GetOrLookupUnownedBaseShape<mode>(cx, base); 1.847 + if (!nbase) 1.848 + return nullptr; 1.849 + } 1.850 + 1.851 + /* 1.852 + * Now that we've possibly preserved slot, check whether all members match. 1.853 + * If so, this is a redundant "put" and we can return without more work. 1.854 + */ 1.855 + if (shape->matchesParamsAfterId(nbase, slot, attrs, flags)) 1.856 + return shape; 1.857 + 1.858 + /* 1.859 + * Overwriting a non-last property requires switching to dictionary mode. 1.860 + * The shape tree is shared immutable, and we can't removeProperty and then 1.861 + * addPropertyInternal because a failure under add would lose data. 1.862 + */ 1.863 + if (shape != obj->lastProperty() && !obj->inDictionaryMode()) { 1.864 + if (!obj->toDictionaryMode(cx)) 1.865 + return nullptr; 1.866 + spp = obj->lastProperty()->table().search(shape->propid(), false); 1.867 + shape = SHAPE_FETCH(spp); 1.868 + } 1.869 + 1.870 + JS_ASSERT_IF(shape->hasSlot() && !(attrs & JSPROP_SHARED), shape->slot() == slot); 1.871 + 1.872 + if (obj->inDictionaryMode()) { 1.873 + /* 1.874 + * Updating some property in a dictionary-mode object. Create a new 1.875 + * shape for the existing property, and also generate a new shape for 1.876 + * the last property of the dictionary (unless the modified property 1.877 + * is also the last property). 1.878 + */ 1.879 + bool updateLast = (shape == obj->lastProperty()); 1.880 + shape = obj->replaceWithNewEquivalentShape(cx, shape); 1.881 + if (!shape) 1.882 + return nullptr; 1.883 + if (!updateLast && !obj->generateOwnShape(cx)) 1.884 + return nullptr; 1.885 + 1.886 + /* FIXME bug 593129 -- slot allocation and JSObject *this must move out of here! */ 1.887 + if (slot == SHAPE_INVALID_SLOT && !(attrs & JSPROP_SHARED)) { 1.888 + if (!allocSlot(cx, obj, &slot)) 1.889 + return nullptr; 1.890 + } 1.891 + 1.892 + if (updateLast) 1.893 + shape->base()->adoptUnowned(nbase); 1.894 + else 1.895 + shape->base_ = nbase; 1.896 + 1.897 + JS_ASSERT_IF(attrs & (JSPROP_GETTER | JSPROP_SETTER), attrs & JSPROP_SHARED); 1.898 + 1.899 + shape->setSlot(slot); 1.900 + shape->attrs = uint8_t(attrs); 1.901 + shape->flags = flags | Shape::IN_DICTIONARY; 1.902 + } else { 1.903 + /* 1.904 + * Updating the last property in a non-dictionary-mode object. Find an 1.905 + * alternate shared child of the last property's previous shape. 1.906 + */ 1.907 + StackBaseShape base(obj->lastProperty()->base()); 1.908 + base.updateGetterSetter(attrs, getter, setter); 1.909 + 1.910 + UnownedBaseShape *nbase = GetOrLookupUnownedBaseShape<mode>(cx, base); 1.911 + if (!nbase) 1.912 + return nullptr; 1.913 + 1.914 + JS_ASSERT(shape == obj->lastProperty()); 1.915 + 1.916 + /* Find or create a property tree node labeled by our arguments. */ 1.917 + StackShape child(nbase, id, slot, attrs, flags); 1.918 + RootedShape parent(cx, shape->parent); 1.919 + Shape *newShape = getOrLookupChildProperty<mode>(cx, obj, parent, child); 1.920 + 1.921 + if (!newShape) { 1.922 + obj->checkShapeConsistency(); 1.923 + return nullptr; 1.924 + } 1.925 + 1.926 + shape = newShape; 1.927 + } 1.928 + 1.929 + /* 1.930 + * Can't fail now, so free the previous incarnation's slot if the new shape 1.931 + * has no slot. But we do not need to free oldSlot (and must not, as trying 1.932 + * to will botch an assertion in JSObject::freeSlot) if the new last 1.933 + * property (shape here) has a slotSpan that does not cover it. 1.934 + */ 1.935 + if (hadSlot && !shape->hasSlot()) { 1.936 + if (oldSlot < obj->slotSpan()) 1.937 + obj->freeSlot(oldSlot); 1.938 + /* Note: The optimization based on propertyRemovals is only relevant to the main thread. */ 1.939 + if (cx->isJSContext()) 1.940 + ++cx->asJSContext()->runtime()->propertyRemovals; 1.941 + } 1.942 + 1.943 + obj->checkShapeConsistency(); 1.944 + 1.945 + return shape; 1.946 +} 1.947 + 1.948 +template /* static */ Shape * 1.949 +JSObject::putProperty<SequentialExecution>(ExclusiveContext *cx, 1.950 + HandleObject obj, HandleId id, 1.951 + PropertyOp getter, StrictPropertyOp setter, 1.952 + uint32_t slot, unsigned attrs, 1.953 + unsigned flags); 1.954 +template /* static */ Shape * 1.955 +JSObject::putProperty<ParallelExecution>(ForkJoinContext *cx, 1.956 + HandleObject obj, HandleId id, 1.957 + PropertyOp getter, StrictPropertyOp setter, 1.958 + uint32_t slot, unsigned attrs, 1.959 + unsigned flags); 1.960 + 1.961 +template <ExecutionMode mode> 1.962 +/* static */ Shape * 1.963 +JSObject::changeProperty(typename ExecutionModeTraits<mode>::ExclusiveContextType cx, 1.964 + HandleObject obj, HandleShape shape, unsigned attrs, 1.965 + unsigned mask, PropertyOp getter, StrictPropertyOp setter) 1.966 +{ 1.967 + JS_ASSERT(cx->isThreadLocal(obj)); 1.968 + JS_ASSERT(obj->nativeContainsPure(shape)); 1.969 + 1.970 + attrs |= shape->attrs & mask; 1.971 + JS_ASSERT_IF(attrs & (JSPROP_GETTER | JSPROP_SETTER), attrs & JSPROP_SHARED); 1.972 + 1.973 + /* Allow only shared (slotless) => unshared (slotful) transition. */ 1.974 + JS_ASSERT(!((attrs ^ shape->attrs) & JSPROP_SHARED) || 1.975 + !(attrs & JSPROP_SHARED)); 1.976 + 1.977 + if (mode == ParallelExecution) { 1.978 + if (!types::IsTypePropertyIdMarkedNonData(obj, shape->propid())) 1.979 + return nullptr; 1.980 + } else { 1.981 + types::MarkTypePropertyNonData(cx->asExclusiveContext(), obj, shape->propid()); 1.982 + } 1.983 + 1.984 + if (getter == JS_PropertyStub) 1.985 + getter = nullptr; 1.986 + if (setter == JS_StrictPropertyStub) 1.987 + setter = nullptr; 1.988 + 1.989 + if (!CheckCanChangeAttrs(cx, obj, shape, &attrs)) 1.990 + return nullptr; 1.991 + 1.992 + if (shape->attrs == attrs && shape->getter() == getter && shape->setter() == setter) 1.993 + return shape; 1.994 + 1.995 + /* 1.996 + * Let JSObject::putProperty handle this |overwriting| case, including 1.997 + * the conservation of shape->slot (if it's valid). We must not call 1.998 + * removeProperty because it will free an allocated shape->slot, and 1.999 + * putProperty won't re-allocate it. 1.1000 + */ 1.1001 + RootedId propid(cx, shape->propid()); 1.1002 + Shape *newShape = putProperty<mode>(cx, obj, propid, getter, setter, 1.1003 + shape->maybeSlot(), attrs, shape->flags); 1.1004 + 1.1005 + obj->checkShapeConsistency(); 1.1006 + return newShape; 1.1007 +} 1.1008 + 1.1009 +template /* static */ Shape * 1.1010 +JSObject::changeProperty<SequentialExecution>(ExclusiveContext *cx, 1.1011 + HandleObject obj, HandleShape shape, 1.1012 + unsigned attrs, unsigned mask, 1.1013 + PropertyOp getter, StrictPropertyOp setter); 1.1014 +template /* static */ Shape * 1.1015 +JSObject::changeProperty<ParallelExecution>(ForkJoinContext *cx, 1.1016 + HandleObject obj, HandleShape shape, 1.1017 + unsigned attrs, unsigned mask, 1.1018 + PropertyOp getter, StrictPropertyOp setter); 1.1019 + 1.1020 +bool 1.1021 +JSObject::removeProperty(ExclusiveContext *cx, jsid id_) 1.1022 +{ 1.1023 + RootedId id(cx, id_); 1.1024 + RootedObject self(cx, this); 1.1025 + 1.1026 + Shape **spp; 1.1027 + RootedShape shape(cx, Shape::search(cx, lastProperty(), id, &spp)); 1.1028 + if (!shape) 1.1029 + return true; 1.1030 + 1.1031 + /* 1.1032 + * If shape is not the last property added, or the last property cannot 1.1033 + * be removed, switch to dictionary mode. 1.1034 + */ 1.1035 + if (!self->inDictionaryMode() && (shape != self->lastProperty() || !self->canRemoveLastProperty())) { 1.1036 + if (!self->toDictionaryMode(cx)) 1.1037 + return false; 1.1038 + spp = self->lastProperty()->table().search(shape->propid(), false); 1.1039 + shape = SHAPE_FETCH(spp); 1.1040 + } 1.1041 + 1.1042 + /* 1.1043 + * If in dictionary mode, get a new shape for the last property after the 1.1044 + * removal. We need a fresh shape for all dictionary deletions, even of 1.1045 + * the last property. Otherwise, a shape could replay and caches might 1.1046 + * return deleted DictionaryShapes! See bug 595365. Do this before changing 1.1047 + * the object or table, so the remaining removal is infallible. 1.1048 + */ 1.1049 + RootedShape spare(cx); 1.1050 + if (self->inDictionaryMode()) { 1.1051 + spare = js_NewGCShape(cx); 1.1052 + if (!spare) 1.1053 + return false; 1.1054 + new (spare) Shape(shape->base()->unowned(), 0); 1.1055 + if (shape == self->lastProperty()) { 1.1056 + /* 1.1057 + * Get an up to date unowned base shape for the new last property 1.1058 + * when removing the dictionary's last property. Information in 1.1059 + * base shapes for non-last properties may be out of sync with the 1.1060 + * object's state. 1.1061 + */ 1.1062 + RootedShape previous(cx, self->lastProperty()->parent); 1.1063 + StackBaseShape base(self->lastProperty()->base()); 1.1064 + base.updateGetterSetter(previous->attrs, previous->getter(), previous->setter()); 1.1065 + BaseShape *nbase = BaseShape::getUnowned(cx, base); 1.1066 + if (!nbase) 1.1067 + return false; 1.1068 + previous->base_ = nbase; 1.1069 + } 1.1070 + } 1.1071 + 1.1072 + /* If shape has a slot, free its slot number. */ 1.1073 + if (shape->hasSlot()) { 1.1074 + self->freeSlot(shape->slot()); 1.1075 + if (cx->isJSContext()) 1.1076 + ++cx->asJSContext()->runtime()->propertyRemovals; 1.1077 + } 1.1078 + 1.1079 + /* 1.1080 + * A dictionary-mode object owns mutable, unique shapes on a non-circular 1.1081 + * doubly linked list, hashed by lastProperty()->table. So we can edit the 1.1082 + * list and hash in place. 1.1083 + */ 1.1084 + if (self->inDictionaryMode()) { 1.1085 + ShapeTable &table = self->lastProperty()->table(); 1.1086 + 1.1087 + if (SHAPE_HAD_COLLISION(*spp)) { 1.1088 + *spp = SHAPE_REMOVED; 1.1089 + ++table.removedCount; 1.1090 + --table.entryCount; 1.1091 + } else { 1.1092 + *spp = nullptr; 1.1093 + --table.entryCount; 1.1094 + 1.1095 +#ifdef DEBUG 1.1096 + /* 1.1097 + * Check the consistency of the table but limit the number of 1.1098 + * checks not to alter significantly the complexity of the 1.1099 + * delete in debug builds, see bug 534493. 1.1100 + */ 1.1101 + Shape *aprop = self->lastProperty(); 1.1102 + for (int n = 50; --n >= 0 && aprop->parent; aprop = aprop->parent) 1.1103 + JS_ASSERT_IF(aprop != shape, self->nativeContains(cx, aprop)); 1.1104 +#endif 1.1105 + } 1.1106 + 1.1107 + { 1.1108 + /* Remove shape from its non-circular doubly linked list. */ 1.1109 + Shape *oldLastProp = self->lastProperty(); 1.1110 + shape->removeFromDictionary(self); 1.1111 + 1.1112 + /* Hand off table from the old to new last property. */ 1.1113 + oldLastProp->handoffTableTo(self->lastProperty()); 1.1114 + } 1.1115 + 1.1116 + /* Generate a new shape for the object, infallibly. */ 1.1117 + JS_ALWAYS_TRUE(self->generateOwnShape(cx, spare)); 1.1118 + 1.1119 + /* Consider shrinking table if its load factor is <= .25. */ 1.1120 + uint32_t size = table.capacity(); 1.1121 + if (size > ShapeTable::MIN_SIZE && table.entryCount <= size >> 2) 1.1122 + (void) table.change(-1, cx); 1.1123 + } else { 1.1124 + /* 1.1125 + * Non-dictionary-mode shape tables are shared immutables, so all we 1.1126 + * need do is retract the last property and we'll either get or else 1.1127 + * lazily make via a later hashify the exact table for the new property 1.1128 + * lineage. 1.1129 + */ 1.1130 + JS_ASSERT(shape == self->lastProperty()); 1.1131 + self->removeLastProperty(cx); 1.1132 + } 1.1133 + 1.1134 + self->checkShapeConsistency(); 1.1135 + return true; 1.1136 +} 1.1137 + 1.1138 +/* static */ void 1.1139 +JSObject::clear(JSContext *cx, HandleObject obj) 1.1140 +{ 1.1141 + RootedShape shape(cx, obj->lastProperty()); 1.1142 + JS_ASSERT(obj->inDictionaryMode() == shape->inDictionary()); 1.1143 + 1.1144 + while (shape->parent) { 1.1145 + shape = shape->parent; 1.1146 + JS_ASSERT(obj->inDictionaryMode() == shape->inDictionary()); 1.1147 + } 1.1148 + JS_ASSERT(shape->isEmptyShape()); 1.1149 + 1.1150 + if (obj->inDictionaryMode()) 1.1151 + shape->listp = &obj->shape_; 1.1152 + 1.1153 + JS_ALWAYS_TRUE(JSObject::setLastProperty(cx, obj, shape)); 1.1154 + 1.1155 + ++cx->runtime()->propertyRemovals; 1.1156 + obj->checkShapeConsistency(); 1.1157 +} 1.1158 + 1.1159 +/* static */ bool 1.1160 +JSObject::rollbackProperties(ExclusiveContext *cx, HandleObject obj, uint32_t slotSpan) 1.1161 +{ 1.1162 + /* 1.1163 + * Remove properties from this object until it has a matching slot span. 1.1164 + * The object cannot have escaped in a way which would prevent safe 1.1165 + * removal of the last properties. 1.1166 + */ 1.1167 + JS_ASSERT(!obj->inDictionaryMode() && slotSpan <= obj->slotSpan()); 1.1168 + while (true) { 1.1169 + if (obj->lastProperty()->isEmptyShape()) { 1.1170 + JS_ASSERT(slotSpan == 0); 1.1171 + break; 1.1172 + } else { 1.1173 + uint32_t slot = obj->lastProperty()->slot(); 1.1174 + if (slot < slotSpan) 1.1175 + break; 1.1176 + JS_ASSERT(obj->getSlot(slot).isUndefined()); 1.1177 + } 1.1178 + if (!obj->removeProperty(cx, obj->lastProperty()->propid())) 1.1179 + return false; 1.1180 + } 1.1181 + 1.1182 + return true; 1.1183 +} 1.1184 + 1.1185 +Shape * 1.1186 +ObjectImpl::replaceWithNewEquivalentShape(ThreadSafeContext *cx, Shape *oldShape, Shape *newShape) 1.1187 +{ 1.1188 + JS_ASSERT(cx->isThreadLocal(this)); 1.1189 + JS_ASSERT(cx->isThreadLocal(oldShape)); 1.1190 + JS_ASSERT(cx->isInsideCurrentCompartment(oldShape)); 1.1191 + JS_ASSERT_IF(oldShape != lastProperty(), 1.1192 + inDictionaryMode() && 1.1193 + ((cx->isExclusiveContext() 1.1194 + ? nativeLookup(cx->asExclusiveContext(), oldShape->propidRef()) 1.1195 + : nativeLookupPure(oldShape->propidRef())) == oldShape)); 1.1196 + 1.1197 + ObjectImpl *self = this; 1.1198 + 1.1199 + if (!inDictionaryMode()) { 1.1200 + Rooted<ObjectImpl*> selfRoot(cx, self); 1.1201 + RootedShape newRoot(cx, newShape); 1.1202 + if (!toDictionaryMode(cx)) 1.1203 + return nullptr; 1.1204 + oldShape = selfRoot->lastProperty(); 1.1205 + self = selfRoot; 1.1206 + newShape = newRoot; 1.1207 + } 1.1208 + 1.1209 + if (!newShape) { 1.1210 + Rooted<ObjectImpl*> selfRoot(cx, self); 1.1211 + RootedShape oldRoot(cx, oldShape); 1.1212 + newShape = js_NewGCShape(cx); 1.1213 + if (!newShape) 1.1214 + return nullptr; 1.1215 + new (newShape) Shape(oldRoot->base()->unowned(), 0); 1.1216 + self = selfRoot; 1.1217 + oldShape = oldRoot; 1.1218 + } 1.1219 + 1.1220 + ShapeTable &table = self->lastProperty()->table(); 1.1221 + Shape **spp = oldShape->isEmptyShape() 1.1222 + ? nullptr 1.1223 + : table.search(oldShape->propidRef(), false); 1.1224 + 1.1225 + /* 1.1226 + * Splice the new shape into the same position as the old shape, preserving 1.1227 + * enumeration order (see bug 601399). 1.1228 + */ 1.1229 + StackShape nshape(oldShape); 1.1230 + newShape->initDictionaryShape(nshape, self->numFixedSlots(), oldShape->listp); 1.1231 + 1.1232 + JS_ASSERT(newShape->parent == oldShape); 1.1233 + oldShape->removeFromDictionary(self); 1.1234 + 1.1235 + if (newShape == self->lastProperty()) 1.1236 + oldShape->handoffTableTo(newShape); 1.1237 + 1.1238 + if (spp) 1.1239 + SHAPE_STORE_PRESERVING_COLLISION(spp, newShape); 1.1240 + return newShape; 1.1241 +} 1.1242 + 1.1243 +bool 1.1244 +JSObject::shadowingShapeChange(ExclusiveContext *cx, const Shape &shape) 1.1245 +{ 1.1246 + return generateOwnShape(cx); 1.1247 +} 1.1248 + 1.1249 +/* static */ bool 1.1250 +JSObject::clearParent(JSContext *cx, HandleObject obj) 1.1251 +{ 1.1252 + return setParent(cx, obj, NullPtr()); 1.1253 +} 1.1254 + 1.1255 +/* static */ bool 1.1256 +JSObject::setParent(JSContext *cx, HandleObject obj, HandleObject parent) 1.1257 +{ 1.1258 + if (parent && !parent->setDelegate(cx)) 1.1259 + return false; 1.1260 + 1.1261 + if (obj->inDictionaryMode()) { 1.1262 + StackBaseShape base(obj->lastProperty()); 1.1263 + base.parent = parent; 1.1264 + UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base); 1.1265 + if (!nbase) 1.1266 + return false; 1.1267 + 1.1268 + obj->lastProperty()->base()->adoptUnowned(nbase); 1.1269 + return true; 1.1270 + } 1.1271 + 1.1272 + Shape *newShape = Shape::setObjectParent(cx, parent, obj->getTaggedProto(), obj->shape_); 1.1273 + if (!newShape) 1.1274 + return false; 1.1275 + 1.1276 + obj->shape_ = newShape; 1.1277 + return true; 1.1278 +} 1.1279 + 1.1280 +/* static */ Shape * 1.1281 +Shape::setObjectParent(ExclusiveContext *cx, JSObject *parent, TaggedProto proto, Shape *last) 1.1282 +{ 1.1283 + if (last->getObjectParent() == parent) 1.1284 + return last; 1.1285 + 1.1286 + StackBaseShape base(last); 1.1287 + base.parent = parent; 1.1288 + 1.1289 + RootedShape lastRoot(cx, last); 1.1290 + return replaceLastProperty(cx, base, proto, lastRoot); 1.1291 +} 1.1292 + 1.1293 +/* static */ bool 1.1294 +JSObject::setMetadata(JSContext *cx, HandleObject obj, HandleObject metadata) 1.1295 +{ 1.1296 + if (obj->inDictionaryMode()) { 1.1297 + StackBaseShape base(obj->lastProperty()); 1.1298 + base.metadata = metadata; 1.1299 + UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base); 1.1300 + if (!nbase) 1.1301 + return false; 1.1302 + 1.1303 + obj->lastProperty()->base()->adoptUnowned(nbase); 1.1304 + return true; 1.1305 + } 1.1306 + 1.1307 + Shape *newShape = Shape::setObjectMetadata(cx, metadata, obj->getTaggedProto(), obj->shape_); 1.1308 + if (!newShape) 1.1309 + return false; 1.1310 + 1.1311 + obj->shape_ = newShape; 1.1312 + return true; 1.1313 +} 1.1314 + 1.1315 +/* static */ Shape * 1.1316 +Shape::setObjectMetadata(JSContext *cx, JSObject *metadata, TaggedProto proto, Shape *last) 1.1317 +{ 1.1318 + if (last->getObjectMetadata() == metadata) 1.1319 + return last; 1.1320 + 1.1321 + StackBaseShape base(last); 1.1322 + base.metadata = metadata; 1.1323 + 1.1324 + RootedShape lastRoot(cx, last); 1.1325 + return replaceLastProperty(cx, base, proto, lastRoot); 1.1326 +} 1.1327 + 1.1328 +/* static */ bool 1.1329 +js::ObjectImpl::preventExtensions(JSContext *cx, Handle<ObjectImpl*> obj) 1.1330 +{ 1.1331 +#ifdef DEBUG 1.1332 + bool extensible; 1.1333 + if (!JSObject::isExtensible(cx, obj, &extensible)) 1.1334 + return false; 1.1335 + MOZ_ASSERT(extensible, 1.1336 + "Callers must ensure |obj| is extensible before calling " 1.1337 + "preventExtensions"); 1.1338 +#endif 1.1339 + 1.1340 + if (Downcast(obj)->is<ProxyObject>()) { 1.1341 + RootedObject object(cx, obj->asObjectPtr()); 1.1342 + return js::Proxy::preventExtensions(cx, object); 1.1343 + } 1.1344 + 1.1345 + RootedObject self(cx, obj->asObjectPtr()); 1.1346 + 1.1347 + /* 1.1348 + * Force lazy properties to be resolved by iterating over the objects' own 1.1349 + * properties. 1.1350 + */ 1.1351 + AutoIdVector props(cx); 1.1352 + if (!js::GetPropertyNames(cx, self, JSITER_HIDDEN | JSITER_OWNONLY, &props)) 1.1353 + return false; 1.1354 + 1.1355 + /* 1.1356 + * Convert all dense elements to sparse properties. This will shrink the 1.1357 + * initialized length and capacity of the object to zero and ensure that no 1.1358 + * new dense elements can be added without calling growElements(), which 1.1359 + * checks isExtensible(). 1.1360 + */ 1.1361 + if (self->isNative() && !JSObject::sparsifyDenseElements(cx, self)) 1.1362 + return false; 1.1363 + 1.1364 + return self->setFlag(cx, BaseShape::NOT_EXTENSIBLE, GENERATE_SHAPE); 1.1365 +} 1.1366 + 1.1367 +bool 1.1368 +js::ObjectImpl::setFlag(ExclusiveContext *cx, /*BaseShape::Flag*/ uint32_t flag_, 1.1369 + GenerateShape generateShape) 1.1370 +{ 1.1371 + BaseShape::Flag flag = (BaseShape::Flag) flag_; 1.1372 + 1.1373 + if (lastProperty()->getObjectFlags() & flag) 1.1374 + return true; 1.1375 + 1.1376 + Rooted<ObjectImpl*> self(cx, this); 1.1377 + 1.1378 + if (inDictionaryMode()) { 1.1379 + if (generateShape == GENERATE_SHAPE && !generateOwnShape(cx)) 1.1380 + return false; 1.1381 + StackBaseShape base(self->lastProperty()); 1.1382 + base.flags |= flag; 1.1383 + UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base); 1.1384 + if (!nbase) 1.1385 + return false; 1.1386 + 1.1387 + self->lastProperty()->base()->adoptUnowned(nbase); 1.1388 + return true; 1.1389 + } 1.1390 + 1.1391 + Shape *newShape = 1.1392 + Shape::setObjectFlag(cx, flag, self->getTaggedProto(), self->lastProperty()); 1.1393 + if (!newShape) 1.1394 + return false; 1.1395 + 1.1396 + self->shape_ = newShape; 1.1397 + return true; 1.1398 +} 1.1399 + 1.1400 +bool 1.1401 +js::ObjectImpl::clearFlag(ExclusiveContext *cx, /*BaseShape::Flag*/ uint32_t flag) 1.1402 +{ 1.1403 + JS_ASSERT(inDictionaryMode()); 1.1404 + JS_ASSERT(lastProperty()->getObjectFlags() & flag); 1.1405 + 1.1406 + RootedObject self(cx, this->asObjectPtr()); 1.1407 + 1.1408 + StackBaseShape base(self->lastProperty()); 1.1409 + base.flags &= ~flag; 1.1410 + UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base); 1.1411 + if (!nbase) 1.1412 + return false; 1.1413 + 1.1414 + self->lastProperty()->base()->adoptUnowned(nbase); 1.1415 + return true; 1.1416 +} 1.1417 + 1.1418 +/* static */ Shape * 1.1419 +Shape::setObjectFlag(ExclusiveContext *cx, BaseShape::Flag flag, TaggedProto proto, Shape *last) 1.1420 +{ 1.1421 + if (last->getObjectFlags() & flag) 1.1422 + return last; 1.1423 + 1.1424 + StackBaseShape base(last); 1.1425 + base.flags |= flag; 1.1426 + 1.1427 + RootedShape lastRoot(cx, last); 1.1428 + return replaceLastProperty(cx, base, proto, lastRoot); 1.1429 +} 1.1430 + 1.1431 +/* static */ inline HashNumber 1.1432 +StackBaseShape::hash(const StackBaseShape *base) 1.1433 +{ 1.1434 + HashNumber hash = base->flags; 1.1435 + hash = RotateLeft(hash, 4) ^ (uintptr_t(base->clasp) >> 3); 1.1436 + hash = RotateLeft(hash, 4) ^ (uintptr_t(base->parent) >> 3); 1.1437 + hash = RotateLeft(hash, 4) ^ (uintptr_t(base->metadata) >> 3); 1.1438 + hash = RotateLeft(hash, 4) ^ uintptr_t(base->rawGetter); 1.1439 + hash = RotateLeft(hash, 4) ^ uintptr_t(base->rawSetter); 1.1440 + return hash; 1.1441 +} 1.1442 + 1.1443 +/* static */ inline bool 1.1444 +StackBaseShape::match(UnownedBaseShape *key, const StackBaseShape *lookup) 1.1445 +{ 1.1446 + return key->flags == lookup->flags 1.1447 + && key->clasp_ == lookup->clasp 1.1448 + && key->parent == lookup->parent 1.1449 + && key->metadata == lookup->metadata 1.1450 + && key->rawGetter == lookup->rawGetter 1.1451 + && key->rawSetter == lookup->rawSetter; 1.1452 +} 1.1453 + 1.1454 +void 1.1455 +StackBaseShape::trace(JSTracer *trc) 1.1456 +{ 1.1457 + if (parent) { 1.1458 + gc::MarkObjectRoot(trc, (JSObject**)&parent, 1.1459 + "StackBaseShape parent"); 1.1460 + } 1.1461 + if (metadata) { 1.1462 + gc::MarkObjectRoot(trc, (JSObject**)&metadata, 1.1463 + "StackBaseShape metadata"); 1.1464 + } 1.1465 + if ((flags & BaseShape::HAS_GETTER_OBJECT) && rawGetter) { 1.1466 + gc::MarkObjectRoot(trc, (JSObject**)&rawGetter, 1.1467 + "StackBaseShape getter"); 1.1468 + } 1.1469 + if ((flags & BaseShape::HAS_SETTER_OBJECT) && rawSetter) { 1.1470 + gc::MarkObjectRoot(trc, (JSObject**)&rawSetter, 1.1471 + "StackBaseShape setter"); 1.1472 + } 1.1473 +} 1.1474 + 1.1475 +/* static */ UnownedBaseShape* 1.1476 +BaseShape::getUnowned(ExclusiveContext *cx, StackBaseShape &base) 1.1477 +{ 1.1478 + BaseShapeSet &table = cx->compartment()->baseShapes; 1.1479 + 1.1480 + if (!table.initialized() && !table.init()) 1.1481 + return nullptr; 1.1482 + 1.1483 + DependentAddPtr<BaseShapeSet> p(cx, table, &base); 1.1484 + if (p) 1.1485 + return *p; 1.1486 + 1.1487 + RootedGeneric<StackBaseShape*> root(cx, &base); 1.1488 + 1.1489 + BaseShape *nbase_ = js_NewGCBaseShape<CanGC>(cx); 1.1490 + if (!nbase_) 1.1491 + return nullptr; 1.1492 + 1.1493 + new (nbase_) BaseShape(*root); 1.1494 + 1.1495 + UnownedBaseShape *nbase = static_cast<UnownedBaseShape *>(nbase_); 1.1496 + 1.1497 + if (!p.add(cx, table, root, nbase)) 1.1498 + return nullptr; 1.1499 + 1.1500 + return nbase; 1.1501 +} 1.1502 + 1.1503 +/* static */ UnownedBaseShape * 1.1504 +BaseShape::lookupUnowned(ThreadSafeContext *cx, const StackBaseShape &base) 1.1505 +{ 1.1506 + BaseShapeSet &table = cx->compartment_->baseShapes; 1.1507 + 1.1508 + if (!table.initialized()) 1.1509 + return nullptr; 1.1510 + 1.1511 + BaseShapeSet::Ptr p = table.readonlyThreadsafeLookup(&base); 1.1512 + return *p; 1.1513 +} 1.1514 + 1.1515 +void 1.1516 +BaseShape::assertConsistency() 1.1517 +{ 1.1518 +#ifdef DEBUG 1.1519 + if (isOwned()) { 1.1520 + UnownedBaseShape *unowned = baseUnowned(); 1.1521 + JS_ASSERT(hasGetterObject() == unowned->hasGetterObject()); 1.1522 + JS_ASSERT(hasSetterObject() == unowned->hasSetterObject()); 1.1523 + JS_ASSERT_IF(hasGetterObject(), getterObject() == unowned->getterObject()); 1.1524 + JS_ASSERT_IF(hasSetterObject(), setterObject() == unowned->setterObject()); 1.1525 + JS_ASSERT(getObjectParent() == unowned->getObjectParent()); 1.1526 + JS_ASSERT(getObjectMetadata() == unowned->getObjectMetadata()); 1.1527 + JS_ASSERT(getObjectFlags() == unowned->getObjectFlags()); 1.1528 + } 1.1529 +#endif 1.1530 +} 1.1531 + 1.1532 +void 1.1533 +JSCompartment::sweepBaseShapeTable() 1.1534 +{ 1.1535 + gcstats::AutoPhase ap(runtimeFromMainThread()->gcStats, 1.1536 + gcstats::PHASE_SWEEP_TABLES_BASE_SHAPE); 1.1537 + 1.1538 + if (baseShapes.initialized()) { 1.1539 + for (BaseShapeSet::Enum e(baseShapes); !e.empty(); e.popFront()) { 1.1540 + UnownedBaseShape *base = e.front(); 1.1541 + if (IsBaseShapeAboutToBeFinalized(&base)) 1.1542 + e.removeFront(); 1.1543 + } 1.1544 + } 1.1545 +} 1.1546 + 1.1547 +void 1.1548 +BaseShape::finalize(FreeOp *fop) 1.1549 +{ 1.1550 + if (table_) { 1.1551 + fop->delete_(table_); 1.1552 + table_ = nullptr; 1.1553 + } 1.1554 +} 1.1555 + 1.1556 +inline 1.1557 +InitialShapeEntry::InitialShapeEntry() : shape(nullptr), proto(nullptr) 1.1558 +{ 1.1559 +} 1.1560 + 1.1561 +inline 1.1562 +InitialShapeEntry::InitialShapeEntry(const ReadBarriered<Shape> &shape, TaggedProto proto) 1.1563 + : shape(shape), proto(proto) 1.1564 +{ 1.1565 +} 1.1566 + 1.1567 +inline InitialShapeEntry::Lookup 1.1568 +InitialShapeEntry::getLookup() const 1.1569 +{ 1.1570 + return Lookup(shape->getObjectClass(), proto, shape->getObjectParent(), shape->getObjectMetadata(), 1.1571 + shape->numFixedSlots(), shape->getObjectFlags()); 1.1572 +} 1.1573 + 1.1574 +/* static */ inline HashNumber 1.1575 +InitialShapeEntry::hash(const Lookup &lookup) 1.1576 +{ 1.1577 + HashNumber hash = uintptr_t(lookup.clasp) >> 3; 1.1578 + hash = RotateLeft(hash, 4) ^ 1.1579 + (uintptr_t(lookup.hashProto.toWord()) >> 3); 1.1580 + hash = RotateLeft(hash, 4) ^ 1.1581 + (uintptr_t(lookup.hashParent) >> 3) ^ 1.1582 + (uintptr_t(lookup.hashMetadata) >> 3); 1.1583 + return hash + lookup.nfixed; 1.1584 +} 1.1585 + 1.1586 +/* static */ inline bool 1.1587 +InitialShapeEntry::match(const InitialShapeEntry &key, const Lookup &lookup) 1.1588 +{ 1.1589 + const Shape *shape = *key.shape.unsafeGet(); 1.1590 + return lookup.clasp == shape->getObjectClass() 1.1591 + && lookup.matchProto.toWord() == key.proto.toWord() 1.1592 + && lookup.matchParent == shape->getObjectParent() 1.1593 + && lookup.matchMetadata == shape->getObjectMetadata() 1.1594 + && lookup.nfixed == shape->numFixedSlots() 1.1595 + && lookup.baseFlags == shape->getObjectFlags(); 1.1596 +} 1.1597 + 1.1598 +#ifdef JSGC_GENERATIONAL 1.1599 + 1.1600 +/* 1.1601 + * This class is used to add a post barrier on the initialShapes set, as the key 1.1602 + * is calculated based on several objects which may be moved by generational GC. 1.1603 + */ 1.1604 +class InitialShapeSetRef : public BufferableRef 1.1605 +{ 1.1606 + InitialShapeSet *set; 1.1607 + const Class *clasp; 1.1608 + TaggedProto proto; 1.1609 + JSObject *parent; 1.1610 + JSObject *metadata; 1.1611 + size_t nfixed; 1.1612 + uint32_t objectFlags; 1.1613 + 1.1614 + public: 1.1615 + InitialShapeSetRef(InitialShapeSet *set, 1.1616 + const Class *clasp, 1.1617 + TaggedProto proto, 1.1618 + JSObject *parent, 1.1619 + JSObject *metadata, 1.1620 + size_t nfixed, 1.1621 + uint32_t objectFlags) 1.1622 + : set(set), 1.1623 + clasp(clasp), 1.1624 + proto(proto), 1.1625 + parent(parent), 1.1626 + metadata(metadata), 1.1627 + nfixed(nfixed), 1.1628 + objectFlags(objectFlags) 1.1629 + {} 1.1630 + 1.1631 + void mark(JSTracer *trc) { 1.1632 + TaggedProto priorProto = proto; 1.1633 + JSObject *priorParent = parent; 1.1634 + JSObject *priorMetadata = metadata; 1.1635 + if (proto.isObject()) 1.1636 + Mark(trc, reinterpret_cast<JSObject**>(&proto), "initialShapes set proto"); 1.1637 + if (parent) 1.1638 + Mark(trc, &parent, "initialShapes set parent"); 1.1639 + if (metadata) 1.1640 + Mark(trc, &metadata, "initialShapes set metadata"); 1.1641 + if (proto == priorProto && parent == priorParent && metadata == priorMetadata) 1.1642 + return; 1.1643 + 1.1644 + /* Find the original entry, which must still be present. */ 1.1645 + InitialShapeEntry::Lookup lookup(clasp, priorProto, 1.1646 + priorParent, parent, 1.1647 + priorMetadata, metadata, 1.1648 + nfixed, objectFlags); 1.1649 + InitialShapeSet::Ptr p = set->lookup(lookup); 1.1650 + JS_ASSERT(p); 1.1651 + 1.1652 + /* Update the entry's possibly-moved proto, and ensure lookup will still match. */ 1.1653 + InitialShapeEntry &entry = const_cast<InitialShapeEntry&>(*p); 1.1654 + entry.proto = proto; 1.1655 + lookup.matchProto = proto; 1.1656 + 1.1657 + /* Rekey the entry. */ 1.1658 + set->rekeyAs(lookup, 1.1659 + InitialShapeEntry::Lookup(clasp, proto, parent, metadata, nfixed, objectFlags), 1.1660 + *p); 1.1661 + } 1.1662 +}; 1.1663 + 1.1664 +#ifdef JS_GC_ZEAL 1.1665 +void 1.1666 +JSCompartment::checkInitialShapesTableAfterMovingGC() 1.1667 +{ 1.1668 + if (!initialShapes.initialized()) 1.1669 + return; 1.1670 + 1.1671 + /* 1.1672 + * Assert that the postbarriers have worked and that nothing is left in 1.1673 + * initialShapes that points into the nursery, and that the hash table 1.1674 + * entries are discoverable. 1.1675 + */ 1.1676 + JS::shadow::Runtime *rt = JS::shadow::Runtime::asShadowRuntime(runtimeFromMainThread()); 1.1677 + for (InitialShapeSet::Enum e(initialShapes); !e.empty(); e.popFront()) { 1.1678 + InitialShapeEntry entry = e.front(); 1.1679 + TaggedProto proto = entry.proto; 1.1680 + Shape *shape = entry.shape.get(); 1.1681 + 1.1682 + JS_ASSERT_IF(proto.isObject(), !IsInsideNursery(rt, proto.toObject())); 1.1683 + JS_ASSERT(!IsInsideNursery(rt, shape->getObjectParent())); 1.1684 + JS_ASSERT(!IsInsideNursery(rt, shape->getObjectMetadata())); 1.1685 + 1.1686 + InitialShapeEntry::Lookup lookup(shape->getObjectClass(), 1.1687 + proto, 1.1688 + shape->getObjectParent(), 1.1689 + shape->getObjectMetadata(), 1.1690 + shape->numFixedSlots(), 1.1691 + shape->getObjectFlags()); 1.1692 + InitialShapeSet::Ptr ptr = initialShapes.lookup(lookup); 1.1693 + JS_ASSERT(ptr.found() && &*ptr == &e.front()); 1.1694 + } 1.1695 +} 1.1696 +#endif 1.1697 + 1.1698 +#endif 1.1699 + 1.1700 +/* static */ Shape * 1.1701 +EmptyShape::getInitialShape(ExclusiveContext *cx, const Class *clasp, TaggedProto proto, 1.1702 + JSObject *parent, JSObject *metadata, 1.1703 + size_t nfixed, uint32_t objectFlags) 1.1704 +{ 1.1705 + JS_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject())); 1.1706 + JS_ASSERT_IF(parent, cx->isInsideCurrentCompartment(parent)); 1.1707 + 1.1708 + InitialShapeSet &table = cx->compartment()->initialShapes; 1.1709 + 1.1710 + if (!table.initialized() && !table.init()) 1.1711 + return nullptr; 1.1712 + 1.1713 + typedef InitialShapeEntry::Lookup Lookup; 1.1714 + DependentAddPtr<InitialShapeSet> 1.1715 + p(cx, table, Lookup(clasp, proto, parent, metadata, nfixed, objectFlags)); 1.1716 + if (p) 1.1717 + return p->shape; 1.1718 + 1.1719 + Rooted<TaggedProto> protoRoot(cx, proto); 1.1720 + RootedObject parentRoot(cx, parent); 1.1721 + RootedObject metadataRoot(cx, metadata); 1.1722 + 1.1723 + StackBaseShape base(cx, clasp, parent, metadata, objectFlags); 1.1724 + Rooted<UnownedBaseShape*> nbase(cx, BaseShape::getUnowned(cx, base)); 1.1725 + if (!nbase) 1.1726 + return nullptr; 1.1727 + 1.1728 + Shape *shape = cx->compartment()->propertyTree.newShape(cx); 1.1729 + if (!shape) 1.1730 + return nullptr; 1.1731 + new (shape) EmptyShape(nbase, nfixed); 1.1732 + 1.1733 + Lookup lookup(clasp, protoRoot, parentRoot, metadataRoot, nfixed, objectFlags); 1.1734 + if (!p.add(cx, table, lookup, InitialShapeEntry(shape, protoRoot))) 1.1735 + return nullptr; 1.1736 + 1.1737 +#ifdef JSGC_GENERATIONAL 1.1738 + if (cx->hasNursery()) { 1.1739 + if ((protoRoot.isObject() && cx->nursery().isInside(protoRoot.toObject())) || 1.1740 + cx->nursery().isInside(parentRoot.get()) || 1.1741 + cx->nursery().isInside(metadataRoot.get())) 1.1742 + { 1.1743 + InitialShapeSetRef ref( 1.1744 + &table, clasp, protoRoot, parentRoot, metadataRoot, nfixed, objectFlags); 1.1745 + cx->asJSContext()->runtime()->gcStoreBuffer.putGeneric(ref); 1.1746 + } 1.1747 + } 1.1748 +#endif 1.1749 + 1.1750 + return shape; 1.1751 +} 1.1752 + 1.1753 +/* static */ Shape * 1.1754 +EmptyShape::getInitialShape(ExclusiveContext *cx, const Class *clasp, TaggedProto proto, 1.1755 + JSObject *parent, JSObject *metadata, 1.1756 + AllocKind kind, uint32_t objectFlags) 1.1757 +{ 1.1758 + return getInitialShape(cx, clasp, proto, parent, metadata, GetGCKindSlots(kind, clasp), objectFlags); 1.1759 +} 1.1760 + 1.1761 +void 1.1762 +NewObjectCache::invalidateEntriesForShape(JSContext *cx, HandleShape shape, HandleObject proto) 1.1763 +{ 1.1764 + const Class *clasp = shape->getObjectClass(); 1.1765 + 1.1766 + gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots()); 1.1767 + if (CanBeFinalizedInBackground(kind, clasp)) 1.1768 + kind = GetBackgroundAllocKind(kind); 1.1769 + 1.1770 + Rooted<GlobalObject *> global(cx, &shape->getObjectParent()->global()); 1.1771 + Rooted<types::TypeObject *> type(cx, cx->getNewType(clasp, proto.get())); 1.1772 + 1.1773 + EntryIndex entry; 1.1774 + if (lookupGlobal(clasp, global, kind, &entry)) 1.1775 + PodZero(&entries[entry]); 1.1776 + if (!proto->is<GlobalObject>() && lookupProto(clasp, proto, kind, &entry)) 1.1777 + PodZero(&entries[entry]); 1.1778 + if (lookupType(type, kind, &entry)) 1.1779 + PodZero(&entries[entry]); 1.1780 +} 1.1781 + 1.1782 +/* static */ void 1.1783 +EmptyShape::insertInitialShape(ExclusiveContext *cx, HandleShape shape, HandleObject proto) 1.1784 +{ 1.1785 + InitialShapeEntry::Lookup lookup(shape->getObjectClass(), TaggedProto(proto), 1.1786 + shape->getObjectParent(), shape->getObjectMetadata(), 1.1787 + shape->numFixedSlots(), shape->getObjectFlags()); 1.1788 + 1.1789 + InitialShapeSet::Ptr p = cx->compartment()->initialShapes.lookup(lookup); 1.1790 + JS_ASSERT(p); 1.1791 + 1.1792 + InitialShapeEntry &entry = const_cast<InitialShapeEntry &>(*p); 1.1793 + 1.1794 + /* The new shape had better be rooted at the old one. */ 1.1795 +#ifdef DEBUG 1.1796 + Shape *nshape = shape; 1.1797 + while (!nshape->isEmptyShape()) 1.1798 + nshape = nshape->previous(); 1.1799 + JS_ASSERT(nshape == entry.shape); 1.1800 +#endif 1.1801 + 1.1802 + entry.shape = shape.get(); 1.1803 + 1.1804 + /* 1.1805 + * This affects the shape that will be produced by the various NewObject 1.1806 + * methods, so clear any cache entry referring to the old shape. This is 1.1807 + * not required for correctness: the NewObject must always check for a 1.1808 + * nativeEmpty() result and generate the appropriate properties if found. 1.1809 + * Clearing the cache entry avoids this duplicate regeneration. 1.1810 + * 1.1811 + * Clearing is not necessary when this context is running off the main 1.1812 + * thread, as it will not use the new object cache for allocations. 1.1813 + */ 1.1814 + if (cx->isJSContext()) { 1.1815 + JSContext *ncx = cx->asJSContext(); 1.1816 + ncx->runtime()->newObjectCache.invalidateEntriesForShape(ncx, shape, proto); 1.1817 + } 1.1818 +} 1.1819 + 1.1820 +void 1.1821 +JSCompartment::sweepInitialShapeTable() 1.1822 +{ 1.1823 + gcstats::AutoPhase ap(runtimeFromMainThread()->gcStats, 1.1824 + gcstats::PHASE_SWEEP_TABLES_INITIAL_SHAPE); 1.1825 + 1.1826 + if (initialShapes.initialized()) { 1.1827 + for (InitialShapeSet::Enum e(initialShapes); !e.empty(); e.popFront()) { 1.1828 + const InitialShapeEntry &entry = e.front(); 1.1829 + Shape *shape = entry.shape; 1.1830 + JSObject *proto = entry.proto.raw(); 1.1831 + if (IsShapeAboutToBeFinalized(&shape) || (entry.proto.isObject() && IsObjectAboutToBeFinalized(&proto))) { 1.1832 + e.removeFront(); 1.1833 + } else { 1.1834 +#ifdef DEBUG 1.1835 + DebugOnly<JSObject *> parent = shape->getObjectParent(); 1.1836 + JS_ASSERT(!parent || !IsObjectAboutToBeFinalized(&parent)); 1.1837 + JS_ASSERT(parent == shape->getObjectParent()); 1.1838 +#endif 1.1839 + if (shape != entry.shape || proto != entry.proto.raw()) { 1.1840 + InitialShapeEntry newKey(shape, proto); 1.1841 + e.rekeyFront(newKey.getLookup(), newKey); 1.1842 + } 1.1843 + } 1.1844 + } 1.1845 + } 1.1846 +} 1.1847 + 1.1848 +void 1.1849 +AutoRooterGetterSetter::Inner::trace(JSTracer *trc) 1.1850 +{ 1.1851 + if ((attrs & JSPROP_GETTER) && *pgetter) 1.1852 + gc::MarkObjectRoot(trc, (JSObject**) pgetter, "AutoRooterGetterSetter getter"); 1.1853 + if ((attrs & JSPROP_SETTER) && *psetter) 1.1854 + gc::MarkObjectRoot(trc, (JSObject**) psetter, "AutoRooterGetterSetter setter"); 1.1855 +}