From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- js/src/jsapi-tests/testGCHeapBarriers.cpp | 828 ++++++++++++++++++++++++++++++ 1 file changed, 828 insertions(+) create mode 100644 js/src/jsapi-tests/testGCHeapBarriers.cpp (limited to 'js/src/jsapi-tests/testGCHeapBarriers.cpp') diff --git a/js/src/jsapi-tests/testGCHeapBarriers.cpp b/js/src/jsapi-tests/testGCHeapBarriers.cpp new file mode 100644 index 0000000000..9493049e16 --- /dev/null +++ b/js/src/jsapi-tests/testGCHeapBarriers.cpp @@ -0,0 +1,828 @@ +/* -*- 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/. */ + +#include "mozilla/Maybe.h" +#include "mozilla/UniquePtr.h" + +#include "gc/AllocKind.h" +#include "gc/Cell.h" +#include "gc/GCInternals.h" +#include "gc/GCRuntime.h" +#include "js/ArrayBuffer.h" // JS::NewArrayBuffer +#include "js/experimental/TypedData.h" +#include "js/PropertyAndElement.h" // JS_DefineProperty, JS_GetProperty +#include "js/RootingAPI.h" +#include "jsapi-tests/tests.h" +#include "vm/Runtime.h" +#include "vm/TypedArrayObject.h" + +#include "vm/JSContext-inl.h" + +using namespace js; + +static js::gc::CellColor GetColor(JSObject* obj) { return obj->color(); } +static js::gc::CellColor GetColor(const JS::ArrayBufferOrView& view) { + return view.asObjectUnbarriered()->color(); +} + +static MOZ_MAYBE_UNUSED bool IsInsideNursery(gc::Cell* cell) { + return !cell->isTenured(); +} +static MOZ_MAYBE_UNUSED bool IsInsideNursery( + const JS::ArrayBufferOrView& view) { + return IsInsideNursery(view.asObjectUnbarriered()); +} + +// A heap-allocated structure containing one of our barriered pointer wrappers +// to test. +template +struct TestStruct { + W wrapper; + + void trace(JSTracer* trc) { + TraceNullableEdge(trc, &wrapper, "TestStruct::wrapper"); + } + + TestStruct() {} + explicit TestStruct(T init) : wrapper(init) {} +}; + +template +static T CreateNurseryGCThing(JSContext* cx) = delete; + +template <> +JSObject* CreateNurseryGCThing(JSContext* cx) { + JS::RootedObject obj(cx, JS_NewPlainObject(cx)); + if (!obj) { + return nullptr; + } + JS_DefineProperty(cx, obj, "x", 42, 0); + MOZ_ASSERT(IsInsideNursery(obj)); + return obj; +} + +template <> +JSFunction* CreateNurseryGCThing(JSContext* cx) { + /* + * We don't actually use the function as a function, so here we cheat and + * cast a JSObject. + */ + return static_cast(CreateNurseryGCThing(cx)); +} + +template <> +JS::Uint8Array CreateNurseryGCThing(JSContext* cx) { + JS::Rooted arr(cx, JS::Uint8Array::create(cx, 100)); + JS::RootedObject obj(cx, arr.asObject()); + JS_DefineProperty(cx, obj, "x", 42, 0); + MOZ_ASSERT(IsInsideNursery(obj)); + return arr; +} + +template +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::ArrayBuffer CreateTenuredGCThing(JSContext* cx) { + return JS::ArrayBuffer::fromObject(CreateTenuredGCThing(cx)); +} + +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); +} + +template +void* CreateHiddenTenuredGCThing(JSContext* cx) { + return CreateTenuredGCThing(cx); +} + +template <> +void* CreateHiddenTenuredGCThing(JSContext* cx) { + return CreateTenuredGCThing(cx).asObjectUnbarriered(); +} + +template <> +void* CreateHiddenTenuredGCThing(JSContext* cx) { + return CreateTenuredGCThing(cx).asObjectUnbarriered(); +} + +static uintptr_t UnbarrieredCastToInt(gc::Cell* cell) { + return reinterpret_cast(cell); +} +static uintptr_t UnbarrieredCastToInt(const JS::ArrayBufferOrView& view) { + return UnbarrieredCastToInt(view.asObjectUnbarriered()); +} + +template +T RecoverHiddenGCThing(void* ptr) { + return reinterpret_cast(ptr); +} + +template <> +JS::ArrayBuffer RecoverHiddenGCThing(void* ptr) { + return JS::ArrayBuffer::fromObject(RecoverHiddenGCThing(ptr)); +} + +template <> +JS::Uint8Array RecoverHiddenGCThing(void* ptr) { + return JS::Uint8Array::fromObject(RecoverHiddenGCThing(ptr)); +} + +static void MakeGray(JSObject* obj) { + gc::TenuredCell* cell = &obj->asTenured(); + cell->unmark(); + cell->markIfUnmarked(gc::MarkColor::Gray); + MOZ_ASSERT(obj->isMarkedGray()); +} + +static void MakeGray(const JS::ArrayBufferOrView& view) { + MakeGray(view.asObjectUnbarriered()); +} + +// 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); + + /* Sanity check - objects start in the nursery and then become tenured. */ + JS_GC(cx); + JS::RootedObject obj(cx, CreateNurseryGCThing(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()); + CHECK(TestHeapPostBarriersForType()); + CHECK(TestHeapPostBarriersForType()); + // Bug 1599378: Add string tests. + + return true; +} + +bool CanAccessObject(JSObject* obj) { + JS::RootedObject rootedObj(cx, obj); + JS::RootedValue value(cx); + CHECK(JS_GetProperty(cx, rootedObj, "x", &value)); + CHECK(value.isInt32()); + CHECK(value.toInt32() == 42); + return true; +} +bool CanAccessObject(const JS::ArrayBufferOrView& view) { + return CanAccessObject(view.asObject()); +} + +template +bool TestHeapPostBarriersForType() { + CHECK((TestHeapPostBarriersForWrapper())); + CHECK((TestHeapPostBarriersForMovableWrapper())); + CHECK((TestHeapPostBarriersForMovableWrapper())); + CHECK((TestHeapPostBarriersForMovableWrapper())); + return true; +} + +template