Skip to content
Merged
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
29 changes: 29 additions & 0 deletions node-test/napi/__tests__/values.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,35 @@ test("object", (t) => {
t.deepEqual(bindings.listObjKeys({ z: 1, a: 2 }).sort(), ["a", "z"]);
});

test("native wrap", (t) => {
bindings.resetNativeWrapDeinitCount();
const wrapped = bindings.createNativeWrap(7);
t.is(wrapped.kind, "native-wrap");
t.true(bindings.nativeWrapMatches(wrapped));
t.is(bindings.getNativeWrapValue(wrapped), 7);
t.is(bindings.getNativeWrapFromEnv(wrapped), 7);

bindings.mutateNativeWrapValue(wrapped, 42);
t.is(bindings.getNativeWrapValue(wrapped), 42);

t.throws(() => bindings.getNativeWrapWrongType(wrapped), {
message: /Native wrapped object type does not match expected type/,
});
t.throws(() => bindings.dropNativeWrapWrongType(wrapped), {
message: /Native wrapped object type does not match expected type/,
});
t.is(bindings.getNativeWrapValue(wrapped), 42);
t.is(bindings.nativeWrapDeinitCount(), 0);

t.throws(() => bindings.getNativeWrapValue({}), {
message: /Object is not wrapped by zig-napi/,
});

bindings.dropNativeWrap(wrapped);
t.is(bindings.nativeWrapDeinitCount(), 1);
t.false(bindings.nativeWrapMatches(wrapped));
});

test("global, undefined, null and symbol", (t) => {
t.is(bindings.getGlobal(), globalThis);
t.is(bindings.getUndefined(), undefined);
Expand Down
10 changes: 10 additions & 0 deletions node-test/napi/src/lib.zig
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ pub const callFunctionWithArg = values.callFunctionWithArg;
pub const createFunction = values.createFunction;
pub const createObj = values.createObj;
pub const listObjKeys = values.listObjKeys;
pub const createNativeWrap = values.createNativeWrap;
pub const getNativeWrapValue = values.getNativeWrapValue;
pub const mutateNativeWrapValue = values.mutateNativeWrapValue;
pub const dropNativeWrap = values.dropNativeWrap;
pub const dropNativeWrapWrongType = values.dropNativeWrapWrongType;
pub const getNativeWrapWrongType = values.getNativeWrapWrongType;
pub const getNativeWrapFromEnv = values.getNativeWrapFromEnv;
pub const nativeWrapMatches = values.nativeWrapMatches;
pub const resetNativeWrapDeinitCount = values.resetNativeWrapDeinitCount;
pub const nativeWrapDeinitCount = values.nativeWrapDeinitCount;
pub const getGlobal = values.getGlobal;
pub const getUndefined = values.getUndefined;
pub const getNull = values.getNull;
Expand Down
63 changes: 63 additions & 0 deletions node-test/napi/src/values.zig
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,21 @@ pub const ExternalPoint = struct {
y: i32,
};

pub const NativeWrapPayload = struct {
value: u32,

const Self = @This();

pub fn deinit(self: *Self) void {
_ = self;
_ = native_wrap_deinits.fetchAdd(1, .monotonic);
}
};

pub const OtherNativeWrapPayload = struct {
value: u32,
};

const NumberOrString = union(enum) {
number: i32,
string: []const u8,
Expand All @@ -36,6 +51,7 @@ const ExternalEither = union(enum) {
};

var detached_external_deinits = std.atomic.Value(usize).init(0);
var native_wrap_deinits = std.atomic.Value(usize).init(0);

const DetachedExternalPayload = struct {
value: u32,
Expand Down Expand Up @@ -184,6 +200,53 @@ pub fn indexmapPassthrough(object: napi.Object) c.napi_value {
return object.raw;
}

pub fn createNativeWrap(env: napi.Env, value: u32) !napi.Object {
var object = try napi.Object.Create(env);
try object.Set("kind", "native-wrap");
try object.wrapWithSizeHint(NativeWrapPayload{ .value = value }, 64);
return object;
}

pub fn getNativeWrapValue(object: napi.Object) !u32 {
const payload = try object.unwrap(NativeWrapPayload);
return payload.value;
}

pub fn mutateNativeWrapValue(object: napi.Object, value: u32) !void {
const payload = try object.unwrap(NativeWrapPayload);
payload.value = value;
}

pub fn dropNativeWrap(object: napi.Object) !void {
try object.dropWrapped(NativeWrapPayload);
}

pub fn dropNativeWrapWrongType(object: napi.Object) !void {
try object.dropWrapped(OtherNativeWrapPayload);
}

pub fn getNativeWrapWrongType(object: napi.Object) !u32 {
const payload = try object.unwrap(OtherNativeWrapPayload);
return payload.value;
}

pub fn getNativeWrapFromEnv(env: napi.Env, object: napi.Object) !u32 {
const payload = try env.unwrap(object, NativeWrapPayload);
return payload.value;
}

pub fn nativeWrapMatches(object: napi.Object) bool {
return object.matchesWrapped(NativeWrapPayload);
}

pub fn resetNativeWrapDeinitCount() void {
native_wrap_deinits.store(0, .monotonic);
}

pub fn nativeWrapDeinitCount() usize {
return native_wrap_deinits.load(.monotonic);
}

pub fn enumToI32(value: CustomNumEnum) i32 {
return @intFromEnum(value);
}
Expand Down
2 changes: 2 additions & 0 deletions src/napi.zig
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const typedarray = @import("./napi/wrapper/typedarray.zig");
const dataview = @import("./napi/wrapper/dataview.zig");
const reference = @import("./napi/wrapper/reference.zig");
const external = @import("./napi/wrapper/external.zig");
const native_wrap = @import("./napi/wrapper/native_wrap.zig");
const global_allocator = @import("./napi/util/allocator.zig");
const options = @import("./napi/options.zig");

Expand Down Expand Up @@ -70,6 +71,7 @@ pub const DataView = dataview.DataView;
pub const Reference = reference.Reference;
pub const Ref = reference.Reference;
pub const External = external.External;
pub const NativeWrap = native_wrap;
pub fn FunctionRef(comptime Args: type, comptime Return: type) type {
return reference.Reference(function.Function(Args, Return));
}
Expand Down
37 changes: 37 additions & 0 deletions src/napi/env.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const napi = @import("napi-sys").napi_sys;
const Undefined = @import("./value/undefined.zig").Undefined;
const Null = @import("./value/null.zig").Null;
const native_wrap = @import("./wrapper/native_wrap.zig");

pub const Env = struct {
raw: napi.napi_env,
Expand All @@ -22,4 +23,40 @@ pub const Env = struct {
_ = napi.napi_get_null(self.raw, &result);
return Null.from_raw(self.raw, result);
}

pub fn wrap(self: Env, js_object: anytype, payload: anytype) !void {
return self.wrapWithSizeHint(js_object, payload, 0);
}

pub fn wrapWithSizeHint(self: Env, js_object: anytype, payload: anytype, size_hint: usize) !void {
try native_wrap.wrap(self.raw, objectRaw(js_object), payload, size_hint);
}

pub fn unwrap(self: Env, js_object: anytype, comptime T: type) !*T {
return try native_wrap.unwrap(self.raw, objectRaw(js_object), T);
}

pub fn unwrapConst(self: Env, js_object: anytype, comptime T: type) !*const T {
return try native_wrap.unwrapConst(self.raw, objectRaw(js_object), T);
}

pub fn dropWrapped(self: Env, js_object: anytype, comptime T: type) !void {
try native_wrap.dropWrapped(self.raw, objectRaw(js_object), T);
}

pub fn matchesWrapped(self: Env, js_object: anytype, comptime T: type) bool {
return native_wrap.matches(self.raw, objectRaw(js_object), T);
}
};

fn objectRaw(js_object: anytype) napi.napi_value {
const ObjectType = @TypeOf(js_object);
const ValueType = switch (@typeInfo(ObjectType)) {
.pointer => |ptr| ptr.child,
else => ObjectType,
};
if (!@hasField(ValueType, "raw")) {
@compileError("Expected an object-like value with a raw napi_value field, got: " ++ @typeName(ObjectType));
}
return js_object.raw;
}
41 changes: 41 additions & 0 deletions src/napi/value/object.zig
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const helper = @import("../util/helper.zig");
const Napi = @import("../util/napi.zig").Napi;
const NapiError = @import("../wrapper/error.zig");
const Reference = @import("../wrapper/reference.zig").Reference;
const native_wrap = @import("../wrapper/native_wrap.zig");

pub const Object = struct {
env: napi.napi_env,
Expand Down Expand Up @@ -134,4 +135,44 @@ pub const Object = struct {
pub fn CreateRef(self: Object) !Reference(Object) {
return Reference(Object).New(Env.from_raw(self.env), self);
}

pub fn Wrap(self: Object, payload: anytype) !void {
try self.wrap(payload);
}

pub fn WrapWithSizeHint(self: Object, payload: anytype, size_hint: usize) !void {
try self.wrapWithSizeHint(payload, size_hint);
}

pub fn wrap(self: Object, payload: anytype) !void {
try self.wrapWithSizeHint(payload, 0);
}

pub fn wrapWithSizeHint(self: Object, payload: anytype, size_hint: usize) !void {
try native_wrap.wrap(self.env, self.raw, payload, size_hint);
}

pub fn Unwrap(self: Object, comptime T: type) !*T {
return try self.unwrap(T);
}

pub fn unwrap(self: Object, comptime T: type) !*T {
return try native_wrap.unwrap(self.env, self.raw, T);
}

pub fn unwrapConst(self: Object, comptime T: type) !*const T {
return try native_wrap.unwrapConst(self.env, self.raw, T);
}

pub fn DropWrapped(self: Object, comptime T: type) !void {
try self.dropWrapped(T);
}

pub fn dropWrapped(self: Object, comptime T: type) !void {
try native_wrap.dropWrapped(self.env, self.raw, T);
}

pub fn matchesWrapped(self: Object, comptime T: type) bool {
return native_wrap.matches(self.env, self.raw, T);
}
};
Loading
Loading