js/src/vm/Shape.cpp

changeset 0
6474c204b198
     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 +}

mercurial