Shape * PropertyTree::lookupChild(ThreadSafeContext *cx, Shape *parent, const StackShape &child) { /* Keep this in sync with the logic of getChild above. */ Shape *shape = nullptr; JS_ASSERT(parent); KidsPointer *kidp = &parent->kids; if (kidp->isShape()) { Shape *kid = kidp->toShape(); if (kid->matches(child)) shape = kid; } else if (kidp->isHash()) { if (KidsHash::Ptr p = kidp->toHash()->readonlyThreadsafeLookup(child)) shape = *p; } else { return nullptr; } #if defined(JSGC_INCREMENTAL) && defined(DEBUG) if (shape) { JS::Zone *zone = shape->arenaHeader()->zone; JS_ASSERT(!zone->needsIncrementalBarrier()); JS_ASSERT(!(zone->isGCSweeping() && !shape->isMarked() && !shape->arenaHeader()->allocatedDuringIncremental)); } #endif return shape; }
void PropertyTree::removeChild(Shape *child) { JS_ASSERT(!child->inDictionary()); Shape *parent = child->parent; JS_ASSERT(parent); JS_ASSERT(!JSID_IS_VOID(parent->id)); KidsPointer *kidp = &parent->kids; if (kidp->isShape()) { Shape *kid = kidp->toShape(); if (kid == child) parent->kids.setNull(); return; } kidp->toHash()->remove(child); }
Shape * PropertyTree::getChild(JSContext *cx, Shape *parent, const Shape &child) { Shape *shape; JS_ASSERT(parent); JS_ASSERT(!JSID_IS_VOID(parent->id)); /* * The property tree has extremely low fan-out below its root in * popular embeddings with real-world workloads. Patterns such as * defining closures that capture a constructor's environment as * getters or setters on the new object that is passed in as * |this| can significantly increase fan-out below the property * tree root -- see bug 335700 for details. */ KidsPointer *kidp = &parent->kids; if (kidp->isShape()) { shape = kidp->toShape(); if (shape->matches(&child)) return shape; } else if (kidp->isHash()) { shape = *kidp->toHash()->lookup(&child); if (shape) return shape; } else { /* If kidp->isNull(), we always insert. */ } shape = newShape(cx); if (!shape) return NULL; new (shape) Shape(child.id, child.rawGetter, child.rawSetter, child.slot, child.attrs, child.flags, child.shortid, js_GenerateShape(cx)); if (!insertChild(cx, parent, shape)) return NULL; return shape; }
void Shape::removeChild(Shape *child) { JS_ASSERT(!child->inDictionary()); JS_ASSERT(child->parent == this); KidsPointer *kidp = &kids; if (kidp->isShape()) { JS_ASSERT(kidp->toShape() == child); kidp->setNull(); child->parent = nullptr; return; } KidsHash *hash = kidp->toHash(); JS_ASSERT(hash->count() >= 2); /* otherwise kidp->isShape() should be true */ hash->remove(StackShape(child)); child->parent = nullptr; if (hash->count() == 1) { /* Convert from HASH form back to SHAPE form. */ KidsHash::Range r = hash->all(); Shape *otherChild = r.front(); JS_ASSERT((r.popFront(), r.empty())); /* No more elements! */ kidp->setShape(otherChild); js_delete(hash); } }
JS_ALWAYS_INLINE void js::PropertyTree::orphanChildren(Shape *shape) { KidsPointer *kidp = &shape->kids; JS_ASSERT(!kidp->isNull()); if (kidp->isShape()) { Shape *kid = kidp->toShape(); if (!JSID_IS_VOID(kid->id)) { JS_ASSERT(kid->parent == shape); kid->parent = NULL; } } else { KidsHash *hash = kidp->toHash(); for (KidsHash::Range range = hash->all(); !range.empty(); range.popFront()) { Shape *kid = range.front(); if (!JSID_IS_VOID(kid->id)) { JS_ASSERT(kid->parent == shape); kid->parent = NULL; } } hash->~KidsHash(); js_free(hash); } kidp->setNull(); }
bool PropertyTree::insertChild(JSContext *cx, Shape *parent, Shape *child) { JS_ASSERT(!parent->inDictionary()); JS_ASSERT(!child->parent); JS_ASSERT(!child->inDictionary()); JS_ASSERT(!JSID_IS_VOID(parent->id)); JS_ASSERT(!JSID_IS_VOID(child->id)); JS_ASSERT(cx->compartment == compartment); JS_ASSERT(child->compartment == parent->compartment); KidsPointer *kidp = &parent->kids; if (kidp->isNull()) { child->setParent(parent); kidp->setShape(child); return true; } if (kidp->isShape()) { Shape *shape = kidp->toShape(); JS_ASSERT(shape != child); JS_ASSERT(!shape->matches(child)); KidsHash *hash = HashChildren(shape, child); if (!hash) { JS_ReportOutOfMemory(cx); return false; } kidp->setHash(hash); child->setParent(parent); return true; } KidsHash *hash = kidp->toHash(); KidsHash::AddPtr addPtr = hash->lookupForAdd(child); JS_ASSERT(!addPtr.found()); if (!hash->add(addPtr, child)) { JS_ReportOutOfMemory(cx); return false; } child->setParent(parent); return true; }
bool PropertyTree::insertChild(ExclusiveContext *cx, Shape *parent, Shape *child) { JS_ASSERT(!parent->inDictionary()); JS_ASSERT(!child->parent); JS_ASSERT(!child->inDictionary()); JS_ASSERT(child->compartment() == parent->compartment()); JS_ASSERT(cx->isInsideCurrentCompartment(this)); KidsPointer *kidp = &parent->kids; if (kidp->isNull()) { child->setParent(parent); kidp->setShape(child); return true; } if (kidp->isShape()) { Shape *shape = kidp->toShape(); JS_ASSERT(shape != child); JS_ASSERT(!shape->matches(child)); KidsHash *hash = HashChildren(shape, child); if (!hash) { js_ReportOutOfMemory(cx); return false; } kidp->setHash(hash); child->setParent(parent); return true; } if (!kidp->toHash()->putNew(StackShape(child), child)) { js_ReportOutOfMemory(cx); return false; } child->setParent(parent); return true; }
bool PropertyTree::insertChild(JSContext *cx, UnrootedShape parent, UnrootedShape child) { JS_ASSERT(!parent->inDictionary()); JS_ASSERT(!child->parent); JS_ASSERT(!child->inDictionary()); JS_ASSERT(cx->compartment == compartment); JS_ASSERT(child->compartment() == parent->compartment()); KidsPointer *kidp = &parent->kids; if (kidp->isNull()) { child->setParent(parent); kidp->setShape(child); return true; } if (kidp->isShape()) { UnrootedShape shape = kidp->toShape(); JS_ASSERT(shape != child); JS_ASSERT(!shape->matches(child)); KidsHash *hash = HashChildren(shape, child); if (!hash) { JS_ReportOutOfMemory(cx); return false; } kidp->setHash(hash); child->setParent(parent); return true; } if (!kidp->toHash()->putNew(child, child)) { JS_ReportOutOfMemory(cx); return false; } child->setParent(parent); return true; }
Shape * PropertyTree::getChild(ExclusiveContext *cx, Shape *parentArg, StackShape &unrootedChild) { RootedShape parent(cx, parentArg); JS_ASSERT(parent); Shape *existingShape = nullptr; /* * The property tree has extremely low fan-out below its root in * popular embeddings with real-world workloads. Patterns such as * defining closures that capture a constructor's environment as * getters or setters on the new object that is passed in as * |this| can significantly increase fan-out below the property * tree root -- see bug 335700 for details. */ KidsPointer *kidp = &parent->kids; if (kidp->isShape()) { Shape *kid = kidp->toShape(); if (kid->matches(unrootedChild)) existingShape = kid; } else if (kidp->isHash()) { if (KidsHash::Ptr p = kidp->toHash()->lookup(unrootedChild)) existingShape = *p; } else { /* If kidp->isNull(), we always insert. */ } #ifdef JSGC_INCREMENTAL if (existingShape) { JS::Zone *zone = existingShape->zone(); if (zone->needsIncrementalBarrier()) { /* * We need a read barrier for the shape tree, since these are weak * pointers. */ Shape *tmp = existingShape; MarkShapeUnbarriered(zone->barrierTracer(), &tmp, "read barrier"); JS_ASSERT(tmp == existingShape); } else if (zone->isGCSweeping() && !existingShape->isMarked() && !existingShape->arenaHeader()->allocatedDuringIncremental) { /* * The shape we've found is unreachable and due to be finalized, so * remove our weak reference to it and don't use it. */ JS_ASSERT(parent->isMarked()); parent->removeChild(existingShape); existingShape = nullptr; } } #endif if (existingShape) return existingShape; RootedGeneric<StackShape*> child(cx, &unrootedChild); Shape *shape = newShape(cx); if (!shape) return nullptr; new (shape) Shape(*child, parent->numFixedSlots()); if (!insertChild(cx, parent, shape)) return nullptr; return shape; }
UnrootedShape PropertyTree::getChild(JSContext *cx, Shape *parent_, uint32_t nfixed, const StackShape &child) { AssertCanGC(); { UnrootedShape shape = NULL; JS_ASSERT(parent_); /* * The property tree has extremely low fan-out below its root in * popular embeddings with real-world workloads. Patterns such as * defining closures that capture a constructor's environment as * getters or setters on the new object that is passed in as * |this| can significantly increase fan-out below the property * tree root -- see bug 335700 for details. */ KidsPointer *kidp = &parent_->kids; if (kidp->isShape()) { UnrootedShape kid = kidp->toShape(); if (kid->matches(child)) shape = kid; } else if (kidp->isHash()) { if (KidsHash::Ptr p = kidp->toHash()->lookup(child)) shape = *p; } else { /* If kidp->isNull(), we always insert. */ } #ifdef JSGC_INCREMENTAL if (shape) { JS::Zone *zone = shape->zone(); if (zone->needsBarrier()) { /* * We need a read barrier for the shape tree, since these are weak * pointers. */ Shape *tmp = shape; MarkShapeUnbarriered(zone->barrierTracer(), &tmp, "read barrier"); JS_ASSERT(tmp == shape); } else if (zone->isGCSweeping() && !shape->isMarked() && !shape->arenaHeader()->allocatedDuringIncremental) { /* * The shape we've found is unreachable and due to be finalized, so * remove our weak reference to it and don't use it. */ JS_ASSERT(parent_->isMarked()); parent_->removeChild(shape); shape = NULL; } } #endif if (shape) return shape; } StackShape::AutoRooter childRoot(cx, &child); RootedShape parent(cx, parent_); UnrootedShape shape = newShape(cx); if (!shape) return UnrootedShape(NULL); new (shape) Shape(child, nfixed); if (!insertChild(cx, parent, shape)) return UnrootedShape(NULL); return shape; }