Skip to content

Commit

Permalink
Add a custom trace hook example to tracing.cpp
Browse files Browse the repository at this point in the history
  • Loading branch information
hotsphink committed Jul 9, 2024
1 parent 6ec6d2d commit e9331ec
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 4 deletions.
101 changes: 98 additions & 3 deletions examples/tracing.cpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
#include <memory>
#include <vector>

#include <js/Object.h>
#include <jsapi.h>

#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.

////////////////////////////////////////////////////////////

Expand Down Expand Up @@ -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<CustomObject*>(obj);
}
static JSObject* asObject(CustomObject* custom) {
return reinterpret_cast<JSObject*>(custom);
}

// Retrieve the SafeBox* from the reserved slot.
SafeBox* box() {
JSObject* obj = CustomObject::asObject(this);
return static_cast<SafeBox*>(JS::GetReservedSlot(obj, BoxSlot).toPrivate());
}
};

JSObject* CustomObject::create(JSContext* cx, SafeBox* storedBox) {
JS::Rooted<JSObject*> 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;
Expand All @@ -157,6 +249,9 @@ static bool TracingExample(JSContext* cx) {
if (!EmbeddingRootExample(cx)) {
return false;
}
if (!CustomObjectExample(cx)) {
return false;
}

return true;
}
Expand Down
2 changes: 1 addition & 1 deletion meson.build
Original file line number Diff line number Diff line change
@@ -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')

Expand Down

0 comments on commit e9331ec

Please sign in to comment.