Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 20 additions & 10 deletions src/js/class_runtime.zig
Original file line number Diff line number Diff line change
Expand Up @@ -85,27 +85,37 @@ pub fn registerClass(comptime T: type, env: napi.Env, ctor: napi.Value) !void {
try env.addEnvCleanupHook(State.Entry, entry, State.cleanupHook);
}

/// Per-thread marker set by `materializeClassInstance` to tell the generated
/// constructor "this `new` call comes from the DSL — don't allocate a placeholder,
/// I'll wrap with the real object after `napi_new_instance` returns."
/// Compared by identity against `internalCtorMarkerPtr(T)`.
threadlocal var materialize_target: ?*const anyopaque = null;

pub fn isMaterializing(comptime T: type) bool {
return materialize_target == @as(?*const anyopaque, @ptrCast(internalCtorMarkerPtr(T)));
}

pub fn materializeClassInstance(comptime T: type, env: napi.Env, instance: T, preferred_ctor: ?napi.Value) !napi.Value {
const ctor = preferred_ctor orelse try getConstructor(T, env);
const internal_arg = try env.createExternal(@ptrCast(internalCtorMarkerPtr(T)), null, null);
var raw_args = [_]napi.c.napi_value{internal_arg.value};

const obj_ptr = try std.heap.c_allocator.create(T);
errdefer std.heap.c_allocator.destroy(obj_ptr);
obj_ptr.* = instance;

const prev = materialize_target;
materialize_target = @ptrCast(internalCtorMarkerPtr(T));
defer materialize_target = prev;

var js_instance_raw: napi.c.napi_value = null;
try napi.status.check(napi.c.napi_new_instance(
env.env,
ctor.value,
1,
&raw_args,
0,
null,
&js_instance_raw,
));

const js_instance = napi.Value{ .env = env.env, .value = js_instance_raw };
const placeholder = try env.removeWrapChecked(T, js_instance, typeTag(T));
destroyInternalPlaceholder(T, placeholder);

const obj_ptr = try std.heap.c_allocator.create(T);
obj_ptr.* = instance;

try wrapTaggedObject(T, env, js_instance, obj_ptr, null);
return js_instance;
}
Expand Down
7 changes: 7 additions & 0 deletions src/js/wrap_class.zig
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,13 @@ pub fn wrapClass(comptime T: type) type {
return null;
};

// Fast path: materializeClassInstance is creating this instance.
// Skip the placeholder alloc/wrap — materialize will wrap directly
// with the real native pointer after napi_new_instance returns.
if (class_runtime.isMaterializing(T)) {
return this_arg;
}

if (actual_argc == 1) {
const internal_arg = napi.Value{ .env = raw_env, .value = raw_args[0] };
if ((internal_arg.typeof() catch null) == .external) {
Expand Down
Loading