From e9331ec57bff07886e7f961d26195193ee115dee Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Tue, 9 Jul 2024 16:49:46 -0700 Subject: [PATCH] Add a custom trace hook example to tracing.cpp --- examples/tracing.cpp | 101 +++++++++++++++++++++++++++++++++++++++++-- meson.build | 2 +- 2 files changed, 99 insertions(+), 4 deletions(-) diff --git a/examples/tracing.cpp b/examples/tracing.cpp index 2ef312f..7b51009 100644 --- a/examples/tracing.cpp +++ b/examples/tracing.cpp @@ -1,14 +1,18 @@ #include #include +#include #include #include "boilerplate.h" // This example illustrates how to safely store GC pointers in the embedding's -// data structures by implementing appropriate tracing mechanisms. This example -// covers using strong references where C++ keeps the JS objects alive. Weak -// references use a different implementation strategy that is not covered here. +// data structures, and vice versa, by implementing appropriate tracing +// mechanisms. +// +// This example covers using strong references where C++ keeps the JS objects +// alive. Weak references use a different implementation strategy that is not +// covered here. //////////////////////////////////////////////////////////// @@ -144,6 +148,94 @@ static bool EmbeddingRootExample(JSContext* cx) { //////////////////////////////////////////////////////////// +// The other way around: to store a pointer to a C++ struct from a JS object, +// use a JSClass with a trace hook. Note that this is only required if the C++ +// struct can reach a GC pointer. + +struct CustomObject { + enum Slots { BoxSlot, SlotCount }; // Store box in reserved slot 0. + + // When the CustomObject is collected, delete the stored box. + static void finalize(JS::GCContext* gcx, JSObject* obj); + + // When a CustomObject is traced, it must trace the stored box. + static void trace(JSTracer* trc, JSObject* obj); + + static constexpr JSClassOps classOps = { + // Use .finalize if CustomObject owns the C++ object and should delete it + // when the CustomObject dies. + .finalize = finalize, + + // Use .trace whenever the JS object points to a C++ object that can reach + // other JS objects. + .trace = trace + }; + + static constexpr JSClass clasp = { + .name = "Custom", + .flags = + JSCLASS_HAS_RESERVED_SLOTS(SlotCount) | JSCLASS_FOREGROUND_FINALIZE, + .cOps = &classOps}; + + static JSObject* create(JSContext* cx, SafeBox* storedBox); + + // Full type of JSObject is not known, so we can't inherit. + static CustomObject* fromObject(JSObject* obj) { + return reinterpret_cast(obj); + } + static JSObject* asObject(CustomObject* custom) { + return reinterpret_cast(custom); + } + + // Retrieve the SafeBox* from the reserved slot. + SafeBox* box() { + JSObject* obj = CustomObject::asObject(this); + return static_cast(JS::GetReservedSlot(obj, BoxSlot).toPrivate()); + } +}; + +JSObject* CustomObject::create(JSContext* cx, SafeBox* storedBox) { + JS::Rooted obj(cx, JS_NewObject(cx, &clasp)); + if (!obj) { + return nullptr; + } + JS_SetReservedSlot(obj, BoxSlot, JS::PrivateValue(storedBox)); + return obj; +} + +void CustomObject::finalize(JS::GCContext* gcx, JSObject* obj) { + delete CustomObject::fromObject(obj)->box(); +} + +void CustomObject::trace(JSTracer* trc, JSObject* obj) { + SafeBox* b = CustomObject::fromObject(obj)->box(); + if (b) { + b->trace(trc); + } +} + +static bool CustomObjectExample(JSContext* cx) { + JS::RootedObject global(cx, boilerplate::CreateGlobal(cx)); + if (!global) { + return false; + } + + JSAutoRealm ar(cx, global); + + SafeBox* box = new SafeBox(); + JSObject* obj = CustomObject::create(cx, box); + if (!obj) { + return false; + } + + // The CustomObject will be collected, since it hasn't been stored anywhere + // else, and the SafeBox allocated just above will be destroyed. + JS_GC(cx); + + return true; +} +//////////////////////////////////////////////////////////// + static bool TracingExample(JSContext* cx) { if (!CustomTypeExample(cx)) { return false; @@ -157,6 +249,9 @@ static bool TracingExample(JSContext* cx) { if (!EmbeddingRootExample(cx)) { return false; } + if (!CustomObjectExample(cx)) { + return false; + } return true; } diff --git a/meson.build b/meson.build index 5ee5a78..85edc00 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project('spidermonkey-embedding-examples', 'cpp', version: 'esr115', meson_version: '>= 0.43.0', - default_options: ['cpp_std=c++17', 'warning_level=3']) + default_options: ['cpp_std=c++20', 'warning_level=3']) cxx = meson.get_compiler('cpp')