summaryrefslogtreecommitdiffstats
path: root/js/src/gc
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/gc')
-rw-r--r--js/src/gc/Marking.cpp46
-rw-r--r--js/src/gc/Tracer.cpp69
2 files changed, 112 insertions, 3 deletions
diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp
index a206a8762..43e325394 100644
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -18,7 +18,6 @@
#include "builtin/ModuleObject.h"
#include "gc/GCInternals.h"
#include "gc/Policy.h"
-#include "gc/StoreBuffer-inl.h"
#include "jit/IonCode.h"
#include "js/SliceBudget.h"
#include "vm/ArgumentsObject.h"
@@ -29,6 +28,7 @@
#include "vm/Shape.h"
#include "vm/Symbol.h"
#include "vm/TypedArrayObject.h"
+#include "vm/UnboxedObject.h"
#include "wasm/WasmJS.h"
#include "jscompartmentinlines.h"
@@ -37,6 +37,7 @@
#include "gc/Nursery-inl.h"
#include "vm/String-inl.h"
+#include "vm/UnboxedObject-inl.h"
using namespace js;
using namespace js::gc;
@@ -1394,6 +1395,14 @@ js::ObjectGroup::traceChildren(JSTracer* trc)
if (maybePreliminaryObjects())
maybePreliminaryObjects()->trace(trc);
+ if (maybeUnboxedLayout())
+ unboxedLayout().trace(trc);
+
+ if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup()) {
+ TraceManuallyBarrieredEdge(trc, &unboxedGroup, "group_original_unboxed_group");
+ setOriginalUnboxedGroup(unboxedGroup);
+ }
+
if (JSObject* descr = maybeTypeDescr()) {
TraceManuallyBarrieredEdge(trc, &descr, "group_type_descr");
setTypeDescr(&descr->as<TypeDescr>());
@@ -1427,6 +1436,12 @@ js::GCMarker::lazilyMarkChildren(ObjectGroup* group)
if (group->maybePreliminaryObjects())
group->maybePreliminaryObjects()->trace(this);
+ if (group->maybeUnboxedLayout())
+ group->unboxedLayout().trace(this);
+
+ if (ObjectGroup* unboxedGroup = group->maybeOriginalUnboxedGroup())
+ traverseEdge(group, unboxedGroup);
+
if (TypeDescr* descr = group->maybeTypeDescr())
traverseEdge(group, static_cast<JSObject*>(descr));
@@ -1469,6 +1484,23 @@ CallTraceHook(Functor f, JSTracer* trc, JSObject* obj, CheckGeneration check, Ar
return nullptr;
}
+ if (clasp == &UnboxedPlainObject::class_) {
+ JSObject** pexpando = obj->as<UnboxedPlainObject>().addressOfExpando();
+ if (*pexpando)
+ f(pexpando, mozilla::Forward<Args>(args)...);
+
+ UnboxedPlainObject& unboxed = obj->as<UnboxedPlainObject>();
+ const UnboxedLayout& layout = check == CheckGeneration::DoChecks
+ ? unboxed.layout()
+ : unboxed.layoutDontCheckGeneration();
+ if (layout.traceList()) {
+ VisitTraceList(f, layout.traceList(), unboxed.data(),
+ mozilla::Forward<Args>(args)...);
+ }
+
+ return nullptr;
+ }
+
clasp->doTrace(trc, obj);
if (!clasp->isNative())
@@ -2261,6 +2293,18 @@ static inline void
TraceWholeCell(TenuringTracer& mover, JSObject* object)
{
mover.traceObject(object);
+
+ // Additionally trace the expando object attached to any unboxed plain
+ // objects. Baseline and Ion can write properties to the expando while
+ // only adding a post barrier to the owning unboxed object. Note that
+ // it isn't possible for a nursery unboxed object to have a tenured
+ // expando, so that adding a post barrier on the original object will
+ // capture any tenured->nursery edges in the expando as well.
+
+ if (object->is<UnboxedPlainObject>()) {
+ if (UnboxedExpandoObject* expando = object->as<UnboxedPlainObject>().maybeExpando())
+ expando->traceChildren(&mover);
+ }
}
static inline void
diff --git a/js/src/gc/Tracer.cpp b/js/src/gc/Tracer.cpp
index 3416464dd..63cd9b08a 100644
--- a/js/src/gc/Tracer.cpp
+++ b/js/src/gc/Tracer.cpp
@@ -201,15 +201,80 @@ gc::TraceCycleCollectorChildren(JS::CallbackTracer* trc, Shape* shape)
} while (shape);
}
+// Object groups can point to other object groups via an UnboxedLayout or the
+// the original unboxed group link. There can potentially be deep or cyclic
+// chains of such groups to trace through without going through a thing that
+// participates in cycle collection. These need to be handled iteratively to
+// avoid blowing the stack when running the cycle collector's callback tracer.
+struct ObjectGroupCycleCollectorTracer : public JS::CallbackTracer
+{
+ explicit ObjectGroupCycleCollectorTracer(JS::CallbackTracer* innerTracer)
+ : JS::CallbackTracer(innerTracer->runtime(), DoNotTraceWeakMaps),
+ innerTracer(innerTracer)
+ {}
+
+ void onChild(const JS::GCCellPtr& thing) override;
+
+ JS::CallbackTracer* innerTracer;
+ Vector<ObjectGroup*, 4, SystemAllocPolicy> seen, worklist;
+};
+
+void
+ObjectGroupCycleCollectorTracer::onChild(const JS::GCCellPtr& thing)
+{
+ if (thing.is<BaseShape>()) {
+ // The CC does not care about BaseShapes, and no additional GC things
+ // will be reached by following this edge.
+ return;
+ }
+
+ if (thing.is<JSObject>() || thing.is<JSScript>()) {
+ // Invoke the inner cycle collector callback on this child. It will not
+ // recurse back into TraceChildren.
+ innerTracer->onChild(thing);
+ return;
+ }
+
+ if (thing.is<ObjectGroup>()) {
+ // If this group is required to be in an ObjectGroup chain, trace it
+ // via the provided worklist rather than continuing to recurse.
+ ObjectGroup& group = thing.as<ObjectGroup>();
+ if (group.maybeUnboxedLayout()) {
+ for (size_t i = 0; i < seen.length(); i++) {
+ if (seen[i] == &group)
+ return;
+ }
+ if (seen.append(&group) && worklist.append(&group)) {
+ return;
+ } else {
+ // If append fails, keep tracing normally. The worst that will
+ // happen is we end up overrecursing.
+ }
+ }
+ }
+
+ TraceChildren(this, thing.asCell(), thing.kind());
+}
+
void
gc::TraceCycleCollectorChildren(JS::CallbackTracer* trc, ObjectGroup* group)
{
MOZ_ASSERT(trc->isCallbackTracer());
- group->traceChildren(trc);
-}
+ // Early return if this group is not required to be in an ObjectGroup chain.
+ if (!group->maybeUnboxedLayout())
+ return group->traceChildren(trc);
+
+ ObjectGroupCycleCollectorTracer groupTracer(trc->asCallbackTracer());
+ group->traceChildren(&groupTracer);
+ while (!groupTracer.worklist.empty()) {
+ ObjectGroup* innerGroup = groupTracer.worklist.popCopy();
+ innerGroup->traceChildren(&groupTracer);
+ }
+}
+
/*** Traced Edge Printer *************************************************************************/
static size_t