|
@@ -0,0 +1,695 @@
|
|
|
+# HG changeset patch
|
|
|
+# User Ted Campbell <tcampbell@mozilla.com>
|
|
|
+# Date 1517224740 -7200
|
|
|
+# Mon Jan 29 13:19:00 2018 +0200
|
|
|
+# Node ID 8009cda1a18e5e2f7bcb5b970ecab9637d124dd9
|
|
|
+# Parent ee2d404c4a84b0438f1cf1711d1c096e1e72f6f3
|
|
|
+Bug 1433837 - Use accessors for ShapedObject::shape_ field. r=jandem
|
|
|
+MozReview-Commit-ID: 4Kw6iAudVyH
|
|
|
+
|
|
|
+diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp
|
|
|
+--- a/js/src/builtin/TypedObject.cpp
|
|
|
++++ b/js/src/builtin/TypedObject.cpp
|
|
|
+@@ -1598,17 +1598,17 @@ ReportTypedObjTypeError(JSContext* cx,
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* static */ void
|
|
|
+ OutlineTypedObject::obj_trace(JSTracer* trc, JSObject* object)
|
|
|
+ {
|
|
|
+ OutlineTypedObject& typedObj = object->as<OutlineTypedObject>();
|
|
|
+
|
|
|
+- TraceEdge(trc, &typedObj.shape_, "OutlineTypedObject_shape");
|
|
|
++ TraceEdge(trc, typedObj.shapePtr(), "OutlineTypedObject_shape");
|
|
|
+
|
|
|
+ if (!typedObj.owner_)
|
|
|
+ return;
|
|
|
+
|
|
|
+ TypeDescr& descr = typedObj.typeDescr();
|
|
|
+
|
|
|
+ // Mark the owner, watching in case it is moved by the tracer.
|
|
|
+ JSObject* oldOwner = typedObj.owner_;
|
|
|
+@@ -2110,17 +2110,17 @@ InlineTypedObject::createCopy(JSContext*
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* static */ void
|
|
|
+ InlineTypedObject::obj_trace(JSTracer* trc, JSObject* object)
|
|
|
+ {
|
|
|
+ InlineTypedObject& typedObj = object->as<InlineTypedObject>();
|
|
|
+
|
|
|
+- TraceEdge(trc, &typedObj.shape_, "InlineTypedObject_shape");
|
|
|
++ TraceEdge(trc, typedObj.shapePtr(), "InlineTypedObject_shape");
|
|
|
+
|
|
|
+ // Inline transparent objects do not have references and do not need more
|
|
|
+ // tracing. If there is an entry in the compartment's LazyArrayBufferTable,
|
|
|
+ // tracing that reference will be taken care of by the table itself.
|
|
|
+ if (typedObj.is<InlineTransparentTypedObject>())
|
|
|
+ return;
|
|
|
+
|
|
|
+ typedObj.typeDescr().traceInstances(trc, typedObj.inlineTypedMem(), 1);
|
|
|
+diff --git a/js/src/builtin/TypedObject.h b/js/src/builtin/TypedObject.h
|
|
|
+--- a/js/src/builtin/TypedObject.h
|
|
|
++++ b/js/src/builtin/TypedObject.h
|
|
|
+@@ -587,17 +587,19 @@ class TypedObject : public ShapedObject
|
|
|
+ // User-accessible constructor (`new TypeDescriptor(...)`). Note that the
|
|
|
+ // callee here is the type descriptor.
|
|
|
+ static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
|
|
|
+
|
|
|
+ /* Accessors for self hosted code. */
|
|
|
+ static MOZ_MUST_USE bool GetBuffer(JSContext* cx, unsigned argc, Value* vp);
|
|
|
+ static MOZ_MUST_USE bool GetByteOffset(JSContext* cx, unsigned argc, Value* vp);
|
|
|
+
|
|
|
+- Shape** addressOfShapeFromGC() { return shape_.unsafeUnbarrieredForTracing(); }
|
|
|
++ Shape** addressOfShapeFromGC() {
|
|
|
++ return shapeRef().unsafeUnbarrieredForTracing();
|
|
|
++ }
|
|
|
+ };
|
|
|
+
|
|
|
+ typedef Handle<TypedObject*> HandleTypedObject;
|
|
|
+
|
|
|
+ class OutlineTypedObject : public TypedObject
|
|
|
+ {
|
|
|
+ // The object which owns the data this object points to. Because this
|
|
|
+ // pointer is managed in tandem with |data|, this is not a GCPtr and
|
|
|
+diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp
|
|
|
+--- a/js/src/jsobj.cpp
|
|
|
++++ b/js/src/jsobj.cpp
|
|
|
+@@ -1526,20 +1526,20 @@ NativeObject::fillInAfterSwap(JSContext*
|
|
|
+ {
|
|
|
+ // This object has just been swapped with some other object, and its shape
|
|
|
+ // no longer reflects its allocated size. Correct this information and
|
|
|
+ // fill the slots in with the specified values.
|
|
|
+ MOZ_ASSERT(obj->slotSpan() == values.length());
|
|
|
+
|
|
|
+ // Make sure the shape's numFixedSlots() is correct.
|
|
|
+ size_t nfixed = gc::GetGCKindSlots(obj->asTenured().getAllocKind(), obj->getClass());
|
|
|
+- if (nfixed != obj->shape_->numFixedSlots()) {
|
|
|
++ if (nfixed != obj->shape()->numFixedSlots()) {
|
|
|
+ if (!NativeObject::generateOwnShape(cx, obj))
|
|
|
+ return false;
|
|
|
+- obj->shape_->setNumFixedSlots(nfixed);
|
|
|
++ obj->shape()->setNumFixedSlots(nfixed);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (obj->hasPrivate())
|
|
|
+ obj->setPrivate(priv);
|
|
|
+ else
|
|
|
+ MOZ_ASSERT(!priv);
|
|
|
+
|
|
|
+ if (obj->slots_) {
|
|
|
+@@ -1559,17 +1559,17 @@ NativeObject::fillInAfterSwap(JSContext*
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ JSObject::fixDictionaryShapeAfterSwap()
|
|
|
+ {
|
|
|
+ // Dictionary shapes can point back to their containing objects, so after
|
|
|
+ // swapping the guts of those objects fix the pointers up.
|
|
|
+ if (isNative() && as<NativeObject>().inDictionaryMode())
|
|
|
+- as<NativeObject>().shape_->listp = &as<NativeObject>().shape_;
|
|
|
++ as<NativeObject>().shape()->listp = as<NativeObject>().shapePtr();
|
|
|
+ }
|
|
|
+
|
|
|
+ static MOZ_MUST_USE bool
|
|
|
+ CopyProxyValuesBeforeSwap(ProxyObject* proxy, Vector<Value>& values)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(values.empty());
|
|
|
+
|
|
|
+ // Remove the GCPtrValues we're about to swap from the store buffer, to
|
|
|
+diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h
|
|
|
+--- a/js/src/jsobjinlines.h
|
|
|
++++ b/js/src/jsobjinlines.h
|
|
|
+@@ -134,29 +134,29 @@ JSObject::finalize(js::FreeOp* fop)
|
|
|
+
|
|
|
+ MOZ_ALWAYS_INLINE void
|
|
|
+ js::NativeObject::sweepDictionaryListPointer()
|
|
|
+ {
|
|
|
+ // For dictionary objects (which must be native), it's possible that
|
|
|
+ // unreachable shapes may be marked whose listp points into this object. In
|
|
|
+ // case this happens, null out the shape's pointer so that a moving GC will
|
|
|
+ // not try to access the dead object.
|
|
|
+- if (shape_->listp == &shape_)
|
|
|
+- shape_->listp = nullptr;
|
|
|
++ if (shape()->listp == shapePtr())
|
|
|
++ shape()->listp = nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_ALWAYS_INLINE void
|
|
|
+ js::NativeObject::updateDictionaryListPointerAfterMinorGC(NativeObject* old)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(this == Forwarded(old));
|
|
|
+
|
|
|
+ // Dictionary objects can be allocated in the nursery and when they are
|
|
|
+ // tenured the shape's pointer into the object needs to be updated.
|
|
|
+- if (shape_->listp == &old->shape_)
|
|
|
+- shape_->listp = &shape_;
|
|
|
++ if (shape()->listp == old->shapePtr())
|
|
|
++ shape()->listp = shapePtr();
|
|
|
+ }
|
|
|
+
|
|
|
+ inline void
|
|
|
+ js::gc::MakeAccessibleAfterMovingGC(JSObject* obj)
|
|
|
+ {
|
|
|
+ if (obj->isNative())
|
|
|
+ obj->as<NativeObject>().updateShapeAfterMovingGC();
|
|
|
+ }
|
|
|
+diff --git a/js/src/proxy/Proxy.cpp b/js/src/proxy/Proxy.cpp
|
|
|
+--- a/js/src/proxy/Proxy.cpp
|
|
|
++++ b/js/src/proxy/Proxy.cpp
|
|
|
+@@ -680,17 +680,17 @@ ProxyObject::traceEdgeToTarget(JSTracer*
|
|
|
+ TraceCrossCompartmentEdge(trc, obj, obj->slotOfPrivate(), "proxy target");
|
|
|
+ }
|
|
|
+
|
|
|
+ /* static */ void
|
|
|
+ ProxyObject::trace(JSTracer* trc, JSObject* obj)
|
|
|
+ {
|
|
|
+ ProxyObject* proxy = &obj->as<ProxyObject>();
|
|
|
+
|
|
|
+- TraceEdge(trc, &proxy->shape_, "ProxyObject_shape");
|
|
|
++ TraceEdge(trc, proxy->shapePtr(), "ProxyObject_shape");
|
|
|
+
|
|
|
+ #ifdef DEBUG
|
|
|
+ if (TlsContext.get()->isStrictProxyCheckingEnabled() && proxy->is<WrapperObject>()) {
|
|
|
+ JSObject* referent = MaybeForwarded(proxy->target());
|
|
|
+ if (referent->compartment() != proxy->compartment()) {
|
|
|
+ /*
|
|
|
+ * Assert that this proxy is tracked in the wrapper map. We maintain
|
|
|
+ * the invariant that the wrapped object is the key in the wrapper map.
|
|
|
+diff --git a/js/src/vm/Caches.h b/js/src/vm/Caches.h
|
|
|
+--- a/js/src/vm/Caches.h
|
|
|
++++ b/js/src/vm/Caches.h
|
|
|
+@@ -208,18 +208,20 @@ class NewObjectCache
|
|
|
+ entry->kind = kind;
|
|
|
+
|
|
|
+ entry->nbytes = gc::Arena::thingSize(kind);
|
|
|
+ js_memcpy(&entry->templateObject, obj, entry->nbytes);
|
|
|
+ }
|
|
|
+
|
|
|
+ static void copyCachedToObject(NativeObject* dst, NativeObject* src, gc::AllocKind kind) {
|
|
|
+ js_memcpy(dst, src, gc::Arena::thingSize(kind));
|
|
|
+- Shape::writeBarrierPost(&dst->shape_, nullptr, dst->shape_);
|
|
|
+- ObjectGroup::writeBarrierPost(&dst->group_, nullptr, dst->group_);
|
|
|
++
|
|
|
++ // Initialize with barriers
|
|
|
++ dst->initGroup(src->group());
|
|
|
++ dst->initShape(src->shape());
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ class RuntimeCaches
|
|
|
+ {
|
|
|
+ UniquePtr<js::MathCache> mathCache_;
|
|
|
+
|
|
|
+ js::MathCache* createMathCache(JSContext* cx);
|
|
|
+diff --git a/js/src/vm/NativeObject-inl.h b/js/src/vm/NativeObject-inl.h
|
|
|
+--- a/js/src/vm/NativeObject-inl.h
|
|
|
++++ b/js/src/vm/NativeObject-inl.h
|
|
|
+@@ -651,24 +651,24 @@ NativeObject::setLastProperty(JSContext*
|
|
|
+ MOZ_ASSERT(shape->zone() == zone());
|
|
|
+ MOZ_ASSERT(shape->numFixedSlots() == numFixedSlots());
|
|
|
+ MOZ_ASSERT(shape->getObjectClass() == getClass());
|
|
|
+
|
|
|
+ size_t oldSpan = lastProperty()->slotSpan();
|
|
|
+ size_t newSpan = shape->slotSpan();
|
|
|
+
|
|
|
+ if (oldSpan == newSpan) {
|
|
|
+- shape_ = shape;
|
|
|
++ setShape(shape);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (MOZ_UNLIKELY(!updateSlotsForSpan(cx, oldSpan, newSpan)))
|
|
|
+ return false;
|
|
|
+
|
|
|
+- shape_ = shape;
|
|
|
++ setShape(shape);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline js::gc::AllocKind
|
|
|
+ NativeObject::allocKindForTenure() const
|
|
|
+ {
|
|
|
+ using namespace js::gc;
|
|
|
+ AllocKind kind = GetGCObjectFixedSlotsKind(numFixedSlots());
|
|
|
+diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp
|
|
|
+--- a/js/src/vm/NativeObject.cpp
|
|
|
++++ b/js/src/vm/NativeObject.cpp
|
|
|
+@@ -165,17 +165,17 @@ js::NativeObject::checkShapeConsistency(
|
|
|
+ }
|
|
|
+
|
|
|
+ shape = lastProperty();
|
|
|
+ while (shape) {
|
|
|
+ MOZ_ASSERT_IF(!shape->isEmptyShape() && shape->isDataProperty(),
|
|
|
+ shape->slot() < slotSpan());
|
|
|
+ if (!prev) {
|
|
|
+ MOZ_ASSERT(lastProperty() == shape);
|
|
|
+- MOZ_ASSERT(shape->listp == &shape_);
|
|
|
++ MOZ_ASSERT(shape->listp == &shapeRef());
|
|
|
+ } else {
|
|
|
+ MOZ_ASSERT(shape->listp == &prev->parent);
|
|
|
+ }
|
|
|
+ prev = shape;
|
|
|
+ shape = shape->parent;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ while (shape->parent) {
|
|
|
+@@ -196,19 +196,19 @@ js::NativeObject::checkShapeConsistency(
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ #endif
|
|
|
+
|
|
|
+ void
|
|
|
+ NativeObject::updateShapeAfterMovingGC()
|
|
|
+ {
|
|
|
+- Shape* shape = shape_;
|
|
|
++ Shape* shape = this->shape();
|
|
|
+ if (IsForwarded(shape))
|
|
|
+- shape_.unsafeSet(Forwarded(shape));
|
|
|
++ shapeRef().unsafeSet(Forwarded(shape));
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ js::NativeObject::initializeSlotRange(uint32_t start, uint32_t length)
|
|
|
+ {
|
|
|
+ /*
|
|
|
+ * No bounds check, as this is used when the object's shape does not
|
|
|
+ * reflect its allocated slots (updateSlotsForSpan).
|
|
|
+@@ -309,17 +309,17 @@ NativeObject::setLastPropertyShrinkFixed
|
|
|
+ DebugOnly<size_t> oldFixed = numFixedSlots();
|
|
|
+ DebugOnly<size_t> newFixed = shape->numFixedSlots();
|
|
|
+ MOZ_ASSERT(newFixed < oldFixed);
|
|
|
+ MOZ_ASSERT(shape->slotSpan() <= oldFixed);
|
|
|
+ MOZ_ASSERT(shape->slotSpan() <= newFixed);
|
|
|
+ MOZ_ASSERT(dynamicSlotsCount(oldFixed, shape->slotSpan(), getClass()) == 0);
|
|
|
+ MOZ_ASSERT(dynamicSlotsCount(newFixed, shape->slotSpan(), getClass()) == 0);
|
|
|
+
|
|
|
+- shape_ = shape;
|
|
|
++ setShape(shape);
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ NativeObject::setLastPropertyMakeNonNative(Shape* shape)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(!inDictionaryMode());
|
|
|
+ MOZ_ASSERT(!shape->getObjectClass()->isNative());
|
|
|
+ MOZ_ASSERT(shape->zone() == zone());
|
|
|
+@@ -328,30 +328,30 @@ NativeObject::setLastPropertyMakeNonNati
|
|
|
+
|
|
|
+ if (hasDynamicElements())
|
|
|
+ js_free(getUnshiftedElementsHeader());
|
|
|
+ if (hasDynamicSlots()) {
|
|
|
+ js_free(slots_);
|
|
|
+ slots_ = nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+- shape_ = shape;
|
|
|
++ setShape(shape);
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ NativeObject::setLastPropertyMakeNative(JSContext* cx, Shape* shape)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(getClass()->isNative());
|
|
|
+ MOZ_ASSERT(shape->getObjectClass()->isNative());
|
|
|
+ MOZ_ASSERT(!shape->inDictionary());
|
|
|
+
|
|
|
+ // This method is used to convert unboxed objects into native objects. In
|
|
|
+ // this case, the shape_ field was previously used to store other data and
|
|
|
+ // this should be treated as an initialization.
|
|
|
+- shape_.init(shape);
|
|
|
++ initShape(shape);
|
|
|
+
|
|
|
+ slots_ = nullptr;
|
|
|
+ elements_ = emptyObjectElements;
|
|
|
+
|
|
|
+ size_t oldSpan = shape->numFixedSlots();
|
|
|
+ size_t newSpan = shape->slotSpan();
|
|
|
+
|
|
|
+ initializeSlotRange(0, oldSpan);
|
|
|
+diff --git a/js/src/vm/NativeObject.h b/js/src/vm/NativeObject.h
|
|
|
+--- a/js/src/vm/NativeObject.h
|
|
|
++++ b/js/src/vm/NativeObject.h
|
|
|
+@@ -408,17 +408,17 @@ enum class DenseElementResult {
|
|
|
+ enum class ShouldUpdateTypes {
|
|
|
+ Update,
|
|
|
+ DontUpdate
|
|
|
+ };
|
|
|
+
|
|
|
+ /*
|
|
|
+ * NativeObject specifies the internal implementation of a native object.
|
|
|
+ *
|
|
|
+- * Native objects use ShapedObject::shape_ to record property information. Two
|
|
|
++ * Native objects use ShapedObject::shape to record property information. Two
|
|
|
+ * native objects with the same shape are guaranteed to have the same number of
|
|
|
+ * fixed slots.
|
|
|
+ *
|
|
|
+ * Native objects extend the base implementation of an object with storage for
|
|
|
+ * the object's named properties and indexed elements.
|
|
|
+ *
|
|
|
+ * These are stored separately from one another. Objects are followed by a
|
|
|
+ * variable-sized array of values for inline storage, which may be used by
|
|
|
+@@ -471,18 +471,18 @@ class NativeObject : public ShapedObject
|
|
|
+ static_assert(MAX_FIXED_SLOTS <= Shape::FIXED_SLOTS_MAX,
|
|
|
+ "verify numFixedSlots() bitfield is big enough");
|
|
|
+ static_assert(sizeof(NativeObject) + MAX_FIXED_SLOTS * sizeof(Value) == JSObject::MAX_BYTE_SIZE,
|
|
|
+ "inconsistent maximum object size");
|
|
|
+ }
|
|
|
+
|
|
|
+ public:
|
|
|
+ Shape* lastProperty() const {
|
|
|
+- MOZ_ASSERT(shape_);
|
|
|
+- return shape_;
|
|
|
++ MOZ_ASSERT(shape());
|
|
|
++ return shape();
|
|
|
+ }
|
|
|
+
|
|
|
+ uint32_t propertyCount() const {
|
|
|
+ return lastProperty()->entryCount();
|
|
|
+ }
|
|
|
+
|
|
|
+ bool hasShapeTable() const {
|
|
|
+ return lastProperty()->hasTable();
|
|
|
+@@ -736,17 +736,17 @@ class NativeObject : public ShapedObject
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The methods below shadow methods on JSObject and are more efficient for
|
|
|
+ * known-native objects.
|
|
|
+ */
|
|
|
+ bool hasAllFlags(js::BaseShape::Flag flags) const {
|
|
|
+ MOZ_ASSERT(flags);
|
|
|
+- return shape_->hasAllObjectFlags(flags);
|
|
|
++ return shape()->hasAllObjectFlags(flags);
|
|
|
+ }
|
|
|
+ bool nonProxyIsExtensible() const {
|
|
|
+ return !hasAllFlags(js::BaseShape::NOT_EXTENSIBLE);
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Whether there may be indexed properties on this object, excluding any in
|
|
|
+ * the object's elements.
|
|
|
+diff --git a/js/src/vm/Shape.cpp b/js/src/vm/Shape.cpp
|
|
|
+--- a/js/src/vm/Shape.cpp
|
|
|
++++ b/js/src/vm/Shape.cpp
|
|
|
+@@ -77,25 +77,25 @@ ShapeTable::init(JSContext* cx, Shape* l
|
|
|
+
|
|
|
+ void
|
|
|
+ Shape::removeFromDictionary(NativeObject* obj)
|
|
|
+ {
|
|
|
+ MOZ_ASSERT(inDictionary());
|
|
|
+ MOZ_ASSERT(obj->inDictionaryMode());
|
|
|
+ MOZ_ASSERT(listp);
|
|
|
+
|
|
|
+- MOZ_ASSERT(obj->shape_->inDictionary());
|
|
|
+- MOZ_ASSERT(obj->shape_->listp == &obj->shape_);
|
|
|
++ MOZ_ASSERT(obj->shape()->inDictionary());
|
|
|
++ MOZ_ASSERT(obj->shape()->listp == obj->shapePtr());
|
|
|
+
|
|
|
+ if (parent)
|
|
|
+ parent->listp = listp;
|
|
|
+ *listp = parent;
|
|
|
+ listp = nullptr;
|
|
|
+
|
|
|
+- obj->shape_->clearCachedBigEnoughForShapeTable();
|
|
|
++ obj->shape()->clearCachedBigEnoughForShapeTable();
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ Shape::insertIntoDictionary(GCPtrShape* dictp)
|
|
|
+ {
|
|
|
+ // Don't assert inDictionaryMode() here because we may be called from
|
|
|
+ // NativeObject::toDictionaryMode via Shape::initDictionaryShape.
|
|
|
+ MOZ_ASSERT(inDictionary());
|
|
|
+@@ -335,17 +335,17 @@ NativeObject::getChildDataProperty(JSCon
|
|
|
+ if (!shape)
|
|
|
+ return nullptr;
|
|
|
+ if (child.slot() >= obj->lastProperty()->base()->slotSpan()) {
|
|
|
+ if (!obj->setSlotSpan(cx, child.slot() + 1)) {
|
|
|
+ new (shape) Shape(obj->lastProperty()->base()->unowned(), 0);
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+ }
|
|
|
+- shape->initDictionaryShape(child, obj->numFixedSlots(), &obj->shape_);
|
|
|
++ shape->initDictionaryShape(child, obj->numFixedSlots(), obj->shapePtr());
|
|
|
+ return shape;
|
|
|
+ }
|
|
|
+
|
|
|
+ Shape* shape = cx->zone()->propertyTree().inlinedGetChild(cx, parent, child);
|
|
|
+ if (!shape)
|
|
|
+ return nullptr;
|
|
|
+
|
|
|
+ MOZ_ASSERT(shape->parent == parent);
|
|
|
+@@ -366,17 +366,17 @@ NativeObject::getChildAccessorProperty(J
|
|
|
+ // Accessor properties have no slot, but slot_ will reflect that of parent.
|
|
|
+ child.setSlot(parent->maybeSlot());
|
|
|
+
|
|
|
+ if (obj->inDictionaryMode()) {
|
|
|
+ MOZ_ASSERT(parent == obj->lastProperty());
|
|
|
+ Shape* shape = Allocate<AccessorShape>(cx);
|
|
|
+ if (!shape)
|
|
|
+ return nullptr;
|
|
|
+- shape->initDictionaryShape(child, obj->numFixedSlots(), &obj->shape_);
|
|
|
++ shape->initDictionaryShape(child, obj->numFixedSlots(), obj->shapePtr());
|
|
|
+ return shape;
|
|
|
+ }
|
|
|
+
|
|
|
+ Shape* shape = cx->zone()->propertyTree().inlinedGetChild(cx, parent, child);
|
|
|
+ if (!shape)
|
|
|
+ return nullptr;
|
|
|
+
|
|
|
+ MOZ_ASSERT(shape->parent == parent);
|
|
|
+@@ -431,18 +431,18 @@ js::NativeObject::toDictionaryMode(JSCon
|
|
|
+ if (IsInsideNursery(obj) &&
|
|
|
+ !cx->nursery().queueDictionaryModeObjectToSweep(obj))
|
|
|
+ {
|
|
|
+ ReportOutOfMemory(cx);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_ASSERT(root->listp == nullptr);
|
|
|
+- root->listp = &obj->shape_;
|
|
|
+- obj->shape_ = root;
|
|
|
++ root->listp = obj->shapePtr();
|
|
|
++ obj->setShape(root);
|
|
|
+
|
|
|
+ MOZ_ASSERT(obj->inDictionaryMode());
|
|
|
+ root->base()->setSlotSpan(span);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ static bool
|
|
|
+@@ -733,17 +733,17 @@ NativeObject::addEnumerableDataProperty(
|
|
|
+ if (!shape)
|
|
|
+ return nullptr;
|
|
|
+ if (slot >= obj->lastProperty()->base()->slotSpan()) {
|
|
|
+ if (MOZ_UNLIKELY(!obj->setSlotSpan(cx, slot + 1))) {
|
|
|
+ new (shape) Shape(obj->lastProperty()->base()->unowned(), 0);
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+ }
|
|
|
+- shape->initDictionaryShape(child, obj->numFixedSlots(), &obj->shape_);
|
|
|
++ shape->initDictionaryShape(child, obj->numFixedSlots(), obj->shapePtr());
|
|
|
+ } else {
|
|
|
+ uint32_t slot = obj->slotSpan();
|
|
|
+ MOZ_ASSERT(slot >= JSSLOT_FREE(obj->getClass()));
|
|
|
+ // Objects with many properties are converted to dictionary
|
|
|
+ // mode, so we can't overflow SHAPE_MAXIMUM_SLOT here.
|
|
|
+ MOZ_ASSERT(slot < JSSLOT_FREE(obj->getClass()) + PropertyTree::MAX_HEIGHT);
|
|
|
+ MOZ_ASSERT(slot < SHAPE_MAXIMUM_SLOT);
|
|
|
+
|
|
|
+@@ -1225,17 +1225,17 @@ NativeObject::clear(JSContext* cx, Handl
|
|
|
+
|
|
|
+ while (shape->parent) {
|
|
|
+ shape = shape->parent;
|
|
|
+ MOZ_ASSERT(obj->inDictionaryMode() == shape->inDictionary());
|
|
|
+ }
|
|
|
+ MOZ_ASSERT(shape->isEmptyShape());
|
|
|
+
|
|
|
+ if (obj->inDictionaryMode())
|
|
|
+- shape->listp = &obj->shape_;
|
|
|
++ shape->listp = obj->shapePtr();
|
|
|
+
|
|
|
+ JS_ALWAYS_TRUE(obj->setLastProperty(cx, shape));
|
|
|
+
|
|
|
+ obj->checkShapeConsistency();
|
|
|
+ }
|
|
|
+
|
|
|
+ /* static */ bool
|
|
|
+ NativeObject::rollbackProperties(JSContext* cx, HandleNativeObject obj, uint32_t slotSpan)
|
|
|
+@@ -1805,17 +1805,17 @@ Shape::fixupDictionaryShapeAfterMovingGC
|
|
|
+ // listp points to the parent field of the next shape.
|
|
|
+ Shape* next = reinterpret_cast<Shape*>(uintptr_t(listp) - offsetof(Shape, parent));
|
|
|
+ if (gc::IsForwarded(next))
|
|
|
+ listp = &gc::Forwarded(next)->parent;
|
|
|
+ } else {
|
|
|
+ // listp points to the shape_ field of an object.
|
|
|
+ JSObject* last = reinterpret_cast<JSObject*>(uintptr_t(listp) - ShapedObject::offsetOfShape());
|
|
|
+ if (gc::IsForwarded(last))
|
|
|
+- listp = &gc::Forwarded(last)->as<NativeObject>().shape_;
|
|
|
++ listp = gc::Forwarded(last)->as<NativeObject>().shapePtr();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ Shape::fixupShapeTreeAfterMovingGC()
|
|
|
+ {
|
|
|
+ if (kids.isNull())
|
|
|
+ return;
|
|
|
+diff --git a/js/src/vm/ShapedObject.h b/js/src/vm/ShapedObject.h
|
|
|
+--- a/js/src/vm/ShapedObject.h
|
|
|
++++ b/js/src/vm/ShapedObject.h
|
|
|
+@@ -19,33 +19,40 @@ namespace js {
|
|
|
+ * never created as a most-derived class.
|
|
|
+ */
|
|
|
+ class ShapedObject : public JSObject
|
|
|
+ {
|
|
|
+ protected:
|
|
|
+ // Property layout description and other state.
|
|
|
+ GCPtrShape shape_;
|
|
|
+
|
|
|
++ MOZ_ALWAYS_INLINE const GCPtrShape& shapeRef() const {
|
|
|
++ return shape_;
|
|
|
++ }
|
|
|
++ MOZ_ALWAYS_INLINE GCPtrShape& shapeRef() {
|
|
|
++ return shape_;
|
|
|
++ }
|
|
|
++
|
|
|
++ // Used for GC tracing and Shape::listp
|
|
|
++ MOZ_ALWAYS_INLINE GCPtrShape* shapePtr() {
|
|
|
++ return &shape_;
|
|
|
++ }
|
|
|
++
|
|
|
+ public:
|
|
|
+ // Set the shape of an object. This pointer is valid for native objects and
|
|
|
+ // some non-native objects. After creating an object, the objects for which
|
|
|
+ // the shape pointer is invalid need to overwrite this pointer before a GC
|
|
|
+ // can occur.
|
|
|
+- void initShape(Shape* shape) {
|
|
|
+- this->shape_.init(shape);
|
|
|
+- }
|
|
|
++ void initShape(Shape* shape) { shapeRef().init(shape); }
|
|
|
+
|
|
|
+- void setShape(Shape* shape) {
|
|
|
+- this->shape_ = shape;
|
|
|
+- }
|
|
|
+-
|
|
|
+- Shape* shape() const { return this->shape_; }
|
|
|
++ void setShape(Shape* shape) { shapeRef() = shape; }
|
|
|
++ Shape* shape() const { return shapeRef(); }
|
|
|
+
|
|
|
+ void traceShape(JSTracer* trc) {
|
|
|
+- TraceEdge(trc, &shape_, "shape");
|
|
|
++ TraceEdge(trc, shapePtr(), "shape");
|
|
|
+ }
|
|
|
+
|
|
|
+ static size_t offsetOfShape() { return offsetof(ShapedObject, shape_); }
|
|
|
+
|
|
|
+ private:
|
|
|
+ static void staticAsserts() {
|
|
|
+ static_assert(offsetof(ShapedObject, shape_) == offsetof(shadow::Object, shape),
|
|
|
+ "shadow shape must match actual shape");
|
|
|
+diff --git a/js/src/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp
|
|
|
+--- a/js/src/vm/UnboxedObject.cpp
|
|
|
++++ b/js/src/vm/UnboxedObject.cpp
|
|
|
+@@ -294,28 +294,27 @@ UnboxedPlainObject::getValue(const Unbox
|
|
|
+ {
|
|
|
+ uint8_t* p = &data_[property.offset];
|
|
|
+ return GetUnboxedValue(p, property.type, maybeUninitialized);
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ UnboxedPlainObject::trace(JSTracer* trc, JSObject* obj)
|
|
|
+ {
|
|
|
+- if (obj->as<UnboxedPlainObject>().expando_) {
|
|
|
+- TraceManuallyBarrieredEdge(trc,
|
|
|
+- reinterpret_cast<NativeObject**>(&obj->as<UnboxedPlainObject>().expando_),
|
|
|
+- "unboxed_expando");
|
|
|
+- }
|
|
|
++ UnboxedPlainObject* uobj = &obj->as<UnboxedPlainObject>();
|
|
|
+
|
|
|
+- const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layoutDontCheckGeneration();
|
|
|
++ if (uobj->maybeExpando())
|
|
|
++ TraceManuallyBarrieredEdge(trc, uobj->addressOfExpando(), "unboxed_expando");
|
|
|
++
|
|
|
++ const UnboxedLayout& layout = uobj->layoutDontCheckGeneration();
|
|
|
+ const int32_t* list = layout.traceList();
|
|
|
+ if (!list)
|
|
|
+ return;
|
|
|
+
|
|
|
+- uint8_t* data = obj->as<UnboxedPlainObject>().data();
|
|
|
++ uint8_t* data = uobj->data();
|
|
|
+ while (*list != -1) {
|
|
|
+ GCPtrString* heap = reinterpret_cast<GCPtrString*>(data + *list);
|
|
|
+ TraceEdge(trc, heap, "unboxed_string");
|
|
|
+ list++;
|
|
|
+ }
|
|
|
+ list++;
|
|
|
+ while (*list != -1) {
|
|
|
+ GCPtrObject* heap = reinterpret_cast<GCPtrObject*>(data + *list);
|
|
|
+@@ -325,18 +324,18 @@ UnboxedPlainObject::trace(JSTracer* trc,
|
|
|
+
|
|
|
+ // Unboxed objects don't have Values to trace.
|
|
|
+ MOZ_ASSERT(*(list + 1) == -1);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* static */ UnboxedExpandoObject*
|
|
|
+ UnboxedPlainObject::ensureExpando(JSContext* cx, Handle<UnboxedPlainObject*> obj)
|
|
|
+ {
|
|
|
+- if (obj->expando_)
|
|
|
+- return obj->expando_;
|
|
|
++ if (obj->maybeExpando())
|
|
|
++ return obj->maybeExpando();
|
|
|
+
|
|
|
+ UnboxedExpandoObject* expando =
|
|
|
+ NewObjectWithGivenProto<UnboxedExpandoObject>(cx, nullptr, gc::AllocKind::OBJECT4);
|
|
|
+ if (!expando)
|
|
|
+ return nullptr;
|
|
|
+
|
|
|
+ // Don't track property types for expando objects. This allows Baseline
|
|
|
+ // and Ion AddSlot ICs to guard on the unboxed group without guarding on
|
|
|
+@@ -351,17 +350,17 @@ UnboxedPlainObject::ensureExpando(JSCont
|
|
|
+
|
|
|
+ // As with setValue(), we need to manually trigger post barriers on the
|
|
|
+ // whole object. If we treat the field as a GCPtrObject and later
|
|
|
+ // convert the object to its native representation, we will end up with a
|
|
|
+ // corrupted store buffer entry.
|
|
|
+ if (IsInsideNursery(expando) && !IsInsideNursery(obj))
|
|
|
+ cx->zone()->group()->storeBuffer().putWholeCell(obj);
|
|
|
+
|
|
|
+- obj->expando_ = expando;
|
|
|
++ obj->setExpandoUnsafe(expando);
|
|
|
+ return expando;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ UnboxedPlainObject::containsUnboxedOrExpandoProperty(JSContext* cx, jsid id) const
|
|
|
+ {
|
|
|
+ if (layoutDontCheckGeneration().lookup(id))
|
|
|
+ return true;
|
|
|
+diff --git a/js/src/vm/UnboxedObject.h b/js/src/vm/UnboxedObject.h
|
|
|
+--- a/js/src/vm/UnboxedObject.h
|
|
|
++++ b/js/src/vm/UnboxedObject.h
|
|
|
+@@ -270,16 +270,20 @@ class UnboxedPlainObject : public Unboxe
|
|
|
+ uint8_t* data() {
|
|
|
+ return &data_[0];
|
|
|
+ }
|
|
|
+
|
|
|
+ UnboxedExpandoObject* maybeExpando() const {
|
|
|
+ return expando_;
|
|
|
+ }
|
|
|
+
|
|
|
++ void setExpandoUnsafe(UnboxedExpandoObject* expando) {
|
|
|
++ expando_ = expando;
|
|
|
++ }
|
|
|
++
|
|
|
+ void initExpando() {
|
|
|
+ expando_ = nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ // For use during GC.
|
|
|
+ JSObject** addressOfExpando() {
|
|
|
+ return reinterpret_cast<JSObject**>(&expando_);
|
|
|
+ }
|