/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80:
*/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// A heap-allocated structure containing one of our barriered pointer wrappers // to test. template <typename W, typename T> struct TestStruct {
W wrapper;
template <>
JSFunction* CreateNurseryGCThing(JSContext* cx) { /* * We don't actually use the function as a function, so here we cheat and * cast a JSObject.
*/ returnstatic_cast<JSFunction*>(CreateNurseryGCThing<JSObject*>(cx));
}
template <typename T> static T CreateTenuredGCThing(JSContext* cx) = delete;
template <>
JSObject* CreateTenuredGCThing(JSContext* cx) { // Use ArrayBuffers because they have finalizers, which allows using them in // TenuredHeap<> without awkward conversations about nursery allocatability. // Note that at some point ArrayBuffers might become nursery allocated at // which point this test will have to change.
JSObject* obj = JS::NewArrayBuffer(cx, 20);
MOZ_ASSERT(!IsInsideNursery(obj));
MOZ_ASSERT(obj->getClass()->hasFinalize() &&
!(obj->getClass()->flags & JSCLASS_SKIP_NURSERY_FINALIZE)); return obj;
}
template <>
JS::Uint8Array CreateTenuredGCThing(JSContext* cx) { // Use internal APIs that lets us specify the InitialHeap so we can ensure // that this is tenured.
JSObject* obj = js::NewUint8ArrayWithLength(cx, 100, gc::Heap::Tenured);
MOZ_ASSERT(!IsInsideNursery(obj)); return JS::Uint8Array::fromObject(obj);
}
// Test post-barrier implementation on wrapper types. The following wrapper // types have post barriers: // - JS::Heap // - GCPtr // - HeapPtr // - WeakHeapPtr
BEGIN_TEST(testGCHeapPostBarriers) {
AutoLeaveZeal nozeal(cx);
AutoGCParameter disableSemispace(cx, JSGC_SEMISPACE_NURSERY_ENABLED, 0);
/* Sanity check - objects start in the nursery and then become tenured. */
JS_GC(cx);
JS::RootedObject obj(cx, CreateNurseryGCThing<JSObject*>(cx));
CHECK(IsInsideNursery(obj.get()));
JS_GC(cx);
CHECK(!IsInsideNursery(obj.get()));
JS::RootedObject tenuredObject(cx, obj);
/* JSObject and JSFunction objects are nursery allocated. */
CHECK(TestHeapPostBarriersForType<JSObject*>());
CHECK(TestHeapPostBarriersForType<JSFunction*>());
CHECK(TestHeapPostBarriersForType<JS::Uint8Array>()); // Bug 1599378: Add string tests.
template <template <typename> class W, typename T>
[[nodiscard]] bool TestHeapPostBarriersForWrapper() {
CHECK((TestHeapPostBarrierConstruction<W<T>, T>()));
CHECK((TestHeapPostBarrierConstruction<const W<T>, T>()));
CHECK((TestHeapPostBarrierUpdate<W<T>, T>())); if constexpr (!std::is_same_v<W<T>, GCPtr<T>>) { // It is not allowed to delete heap memory containing GCPtrs on // initialization failure like this and doing so will cause an assertion to // fail in the GCPtr destructor (although we disable this in some places in // this test).
CHECK((TestHeapPostBarrierInitFailure<W<T>, T>()));
CHECK((TestHeapPostBarrierInitFailure<const W<T>, T>()));
} returntrue;
}
{ // We don't root our structure because that would end up tracing it on minor // GC and we're testing that heap post barrier works for things that aren't // roots.
JS::AutoSuppressGCAnalysis noAnalysis(cx);
// Disable the check that GCPtrs are only destroyed by the GC. What happens // on destruction isn't relevant to the test.
gc::AutoSetThreadIsFinalizing threadIsFinalizing;
js_delete(testStruct);
}
cx->minorGC(JS::GCReason::API);
returntrue;
}
template <typename W, typename T>
[[nodiscard]] bool TestHeapPostBarrierUpdate() { // Normal case - allocate a heap object, write a nursery pointer into it and // check that it gets updated on minor GC.
T initialObj = CreateNurseryGCThing<T>(cx);
CHECK(initialObj);
CHECK(IsInsideNursery(initialObj));
uintptr_t initialObjAsInt = UnbarrieredCastToInt(initialObj);
{ // We don't root our structure because that would end up tracing it on minor // GC and we're testing that heap post barrier works for things that aren't // roots.
JS::AutoSuppressGCAnalysis noAnalysis(cx);
// Disable the check that GCPtrs are only destroyed by the GC. What happens // on destruction isn't relevant to the test.
gc::AutoSetThreadIsFinalizing threadIsFinalizing;
js_delete(testStruct);
}
cx->minorGC(JS::GCReason::API);
returntrue;
}
template <typename W, typename T>
[[nodiscard]] bool TestHeapPostBarrierInitFailure() { // Failure case - allocate a heap object, write a nursery pointer into it // and fail to complete initialization.
T initialObj = CreateNurseryGCThing<T>(cx);
CHECK(initialObj);
CHECK(IsInsideNursery(initialObj));
{ // We don't root our structure because that would end up tracing it on minor // GC and we're testing that heap post barrier works for things that aren't // roots.
JS::AutoSuppressGCAnalysis noAnalysis(cx);
auto testStruct = cx->make_unique<TestStruct<W, T>>(initialObj);
CHECK(testStruct);
{ // We don't root our structure because that would end up tracing it on minor // GC and we're testing that heap post barrier works for things that aren't // roots.
JS::AutoSuppressGCAnalysis noAnalysis(cx);
W wrapper1(initialObj);
CHECK(wrapper1 == initialObj);
W wrapper2(std::move(wrapper1));
CHECK(wrapper2 == initialObj);
{ // We don't root our structure because that would end up tracing it on minor // GC and we're testing that heap post barrier works for things that aren't // roots.
JS::AutoSuppressGCAnalysis noAnalysis(cx);
W wrapper1(initialObj);
CHECK(wrapper1 == initialObj);
W wrapper2;
wrapper2 = std::move(wrapper1);
CHECK(wrapper2 == initialObj);
// Test read barrier implementation on wrapper types. The following wrapper // types have read barriers: // - JS::Heap // - JS::TenuredHeap // - WeakHeapPtr // // Also check that equality comparisons on wrappers do not trigger the read // barrier.
BEGIN_TEST(testGCHeapReadBarriers) {
AutoLeaveZeal nozeal(cx);
template <typename WrapperT, typename ObjectT>
[[nodiscard]] bool TestWrapperType() { // Check that the read barrier normally marks gray things black.
CHECK((TestReadBarrierUnmarksGray<WrapperT, ObjectT>()));
// Check that the read barrier marks gray and white things black during an // incremental GC.
CHECK((TestReadBarrierMarksBlack<WrapperT, ObjectT>(true)));
CHECK((TestReadBarrierMarksBlack<WrapperT, ObjectT>(false)));
// Allocate test objects and make them gray. We will make sure they stay // gray. (For most reads, the barrier will unmark gray.)
Rooted<ObjectT> obj1(cx, CreateTenuredGCThing<ObjectT>(cx));
Rooted<ObjectT> obj2(cx, CreateTenuredGCThing<ObjectT>(cx));
MakeGray(obj1);
MakeGray(obj2);
using ObjectVector = Vector<JSObject*, 0, SystemAllocPolicy>;
// Test pre-barrier implementation on wrapper types. The following wrapper types // have a pre-barrier: // - GCPtr // - HeapPtr // - PreBarriered
BEGIN_TEST(testGCHeapPreBarriers) {
AutoLeaveZeal nozeal(cx);
// Create a bunch of objects. These are unrooted and will be used to test // whether barriers have fired by checking whether they have been marked // black.
size_t objectCount = 100; // Increase this if necessary when adding tests.
ObjectVector testObjects; for (size_t i = 0; i < objectCount; i++) {
JSObject* obj = CreateTenuredGCThing<JSObject*>(cx);
CHECK(obj);
CHECK(testObjects.append(obj));
}
// Start an incremental GC so we can detect if we cause barriers to fire, as // these will mark objects black.
JS::PrepareForFullGC(cx);
JS::SliceBudget budget(JS::WorkBudget(1));
gc::GCRuntime* gc = &cx->runtime()->gc;
gc->startDebugGC(JS::GCOptions::Normal, budget); while (gc->state() != gc::State::Mark) {
gc->debugGCSlice(budget);
}
MOZ_ASSERT(cx->zone()->needsIncrementalBarrier());
// GCPtr is different because 1) it doesn't support move operations as it's // supposed to be part of a GC thing and 2) it doesn't perform a pre-barrier // in its destructor because these are only destroyed as part of a GC where // the barrier is unnecessary.
CHECK(TestGCPtr(testObjects));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.