Last active
January 28, 2025 21:54
-
-
Save hotsphink/90c77af0775ed2a998f2f07996f7fa2d to your computer and use it in GitHub Desktop.
D231442 with added tests
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Commit ID: a7ee783e48150681ac62b8e89ca95ccf290f2cf3 | |
Change ID: zvsrxnskvqpvzqwnswzkzuxvkprwzlmq | |
Author: Steve Fink <[email protected]> (2025-01-27 11:05:12) | |
Committer: Steve Fink <[email protected]> (2025-01-28 13:54:04) | |
Bug 1935738 - Implement GCPolicy<Heap<T>>::needsSweep. | |
Without a needsSweep() implementation for JS::GCPolicy<JS::Heap<T>>, | |
embedders cannot put heap pointers inside containers like JS::WeakCache. | |
(Firefox is not affected by this because it uses HeapPtr instead, which | |
is not available to embedders.) | |
diff --git a/js/public/GCPolicyAPI.h b/js/public/GCPolicyAPI.h | |
index e805939332..549e26330e 100644 | |
--- a/js/public/GCPolicyAPI.h | |
+++ b/js/public/GCPolicyAPI.h | |
@@ -165,6 +165,10 @@ | |
static bool traceWeak(JSTracer* trc, JS::Heap<T>* thingp) { | |
return !*thingp || js::gc::TraceWeakEdge(trc, thingp); | |
} | |
+ static bool needsSweep(JSTracer* trc, const JS::Heap<T>* thingp) { | |
+ JS::Heap<T> thing{*thingp}; | |
+ return thing && !js::gc::TraceWeakEdge(trc, &thing); | |
+ } | |
}; | |
// GCPolicy<UniquePtr<T>> forwards the contained pointer to GCPolicy<T>. | |
diff --git a/js/src/jsapi-tests/testGCWeakCache.cpp b/js/src/jsapi-tests/testGCWeakCache.cpp | |
index 332692e41e..baae3a91be 100644 | |
--- a/js/src/jsapi-tests/testGCWeakCache.cpp | |
+++ b/js/src/jsapi-tests/testGCWeakCache.cpp | |
@@ -125,6 +125,82 @@ | |
} | |
END_TEST(testWeakCacheMapWithUniquePtr) | |
+// Exercise WeakCache<GCHashSet> using Heap<T>. | |
+BEGIN_TEST(testWeakCacheSetWithJSHeap) { | |
+ // Create two objects tenured and two in the nursery. If zeal is on, | |
+ // this may fail and we'll get more tenured objects. That's fine: | |
+ // the test will continue to work, it will just not test as much. | |
+ JS::RootedObject tenured1(cx, JS_NewPlainObject(cx)); | |
+ JS::RootedObject tenured2(cx, JS_NewPlainObject(cx)); | |
+ JS_GC(cx); | |
+ JS::RootedObject nursery1(cx, JS_NewPlainObject(cx)); | |
+ JS::RootedObject nursery2(cx, JS_NewPlainObject(cx)); | |
+ | |
+ using ObjectSet = | |
+ GCHashSet<JS::Heap<JSObject*>, js::StableCellHasher<JS::Heap<JSObject*>>, | |
+ SystemAllocPolicy>; | |
+ using Cache = JS::WeakCache<ObjectSet>; | |
+ Cache cache(JS::GetObjectZone(tenured1)); | |
+ | |
+ cache.put(tenured1); | |
+ cache.put(tenured2); | |
+ cache.put(nursery1); | |
+ cache.put(nursery2); | |
+ | |
+ // Verify relocation and that we don't sweep too aggressively. | |
+ JS_GC(cx); | |
+ CHECK(cache.has(tenured1)); | |
+ CHECK(cache.has(tenured2)); | |
+ CHECK(cache.has(nursery1)); | |
+ CHECK(cache.has(nursery2)); | |
+ | |
+ // Unroot two entries and verify that they get removed. | |
+ tenured2 = nursery2 = nullptr; | |
+ JS_GC(cx); | |
+ CHECK(cache.has(tenured1)); | |
+ CHECK(cache.has(nursery1)); | |
+ CHECK(cache.count() == 2); | |
+ | |
+ return true; | |
+} | |
+END_TEST(testWeakCacheSetWithJSHeap) | |
+ | |
+// Exercise WeakCache<GCHashMap> using Heap<T> keys and values. | |
+BEGIN_TEST(testWeakCacheMapWithJSHeap) { | |
+ // Create two objects tenured and two in the nursery. If zeal is on, | |
+ // this may fail and we'll get more tenured objects. That's fine: | |
+ // the test will continue to work, it will just not test as much. | |
+ JS::RootedObject tenured1(cx, JS_NewPlainObject(cx)); | |
+ JS::RootedObject tenured2(cx, JS_NewPlainObject(cx)); | |
+ JS_GC(cx); | |
+ JS::RootedObject nursery1(cx, JS_NewPlainObject(cx)); | |
+ JS::RootedObject nursery2(cx, JS_NewPlainObject(cx)); | |
+ | |
+ using ObjectMap = js::GCHashMap<JS::Heap<JSObject*>, JS::Heap<JSObject*>, | |
+ js::StableCellHasher<JS::Heap<JSObject*>>>; | |
+ using Cache = JS::WeakCache<ObjectMap>; | |
+ Cache cache(JS::GetObjectZone(tenured1), cx); | |
+ | |
+ cache.put(tenured1, tenured1); | |
+ cache.put(tenured2, tenured2); | |
+ cache.put(nursery1, nursery2); // These do not map to themselves. | |
+ cache.put(nursery2, nursery1); | |
+ | |
+ JS_GC(cx); | |
+ CHECK(cache.has(tenured1)); | |
+ CHECK(cache.has(tenured2)); | |
+ CHECK(cache.has(nursery1)); | |
+ CHECK(cache.has(nursery2)); | |
+ | |
+ tenured2 = nursery2 = nullptr; | |
+ JS_GC(cx); | |
+ CHECK(cache.has(tenured1)); | |
+ CHECK(cache.count() == 1); // nursery1 is gone because value is dead. | |
+ | |
+ return true; | |
+} | |
+END_TEST(testWeakCacheMapWithJSHeap) | |
+ | |
// Exercise WeakCache<GCVector>. | |
BEGIN_TEST(testWeakCacheGCVector) { | |
// Create two objects tenured and two in the nursery. If zeal is on, | |
@@ -161,6 +237,44 @@ | |
} | |
END_TEST(testWeakCacheGCVector) | |
+// Exercise WeakCache<GCVector> using Heap<T> elements. | |
+BEGIN_TEST(testWeakCacheGCVectorWithJSHeap) { | |
+ // Create two objects tenured and two in the nursery. | |
+ JS::RootedObject tenured1(cx, JS_NewPlainObject(cx)); | |
+ JS::RootedObject tenured2(cx, JS_NewPlainObject(cx)); | |
+ JS_GC(cx); | |
+ JS::RootedObject nursery1(cx, JS_NewPlainObject(cx)); | |
+ JS::RootedObject nursery2(cx, JS_NewPlainObject(cx)); | |
+ | |
+ using ObjectVector = JS::WeakCache<GCVector<JS::Heap<JSObject*>>>; | |
+ ObjectVector cache(JS::GetObjectZone(tenured1), cx); | |
+ | |
+ CHECK(cache.append(tenured1)); | |
+ CHECK(cache.append(tenured2)); | |
+ CHECK(cache.append(nursery1)); | |
+ CHECK(cache.append(nursery2)); | |
+ CHECK(cache.append(nullptr)); | |
+ CHECK(cache.get().length() == 5); | |
+ | |
+ JS_GC(cx); | |
+ CHECK(cache.get().length() == 5); | |
+ CHECK(cache.get()[0] == tenured1); | |
+ CHECK(cache.get()[1] == tenured2); | |
+ CHECK(cache.get()[2] == nursery1); | |
+ CHECK(cache.get()[3] == nursery2); | |
+ CHECK(cache.get()[4] == nullptr); | |
+ | |
+ tenured2 = nursery2 = nullptr; | |
+ JS_GC(cx); | |
+ CHECK(cache.get().length() == 3); | |
+ CHECK(cache.get()[0] == tenured1); | |
+ CHECK(cache.get()[1] == nursery1); | |
+ CHECK(cache.get()[2] == nullptr); | |
+ | |
+ return true; | |
+} | |
+END_TEST(testWeakCacheGCVectorWithJSHeap) | |
+ | |
#ifdef JS_GC_ZEAL | |
// A simple structure that embeds an object pointer. We cripple the hash |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment