Skip to content

Commit 29ef2d7

Browse files
committed
internal(neon): Implement wrap and unwrap as doc hidden for use in macros
1 parent 5793b62 commit 29ef2d7

File tree

5 files changed

+203
-0
lines changed

5 files changed

+203
-0
lines changed

crates/neon/src/object/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ use crate::{
5151
#[cfg(feature = "napi-6")]
5252
use crate::{result::JsResult, types::JsArray};
5353

54+
#[doc(hidden)]
55+
pub use self::wrap::{unwrap, wrap};
56+
57+
mod wrap;
58+
5459
/// A property key in a JavaScript object.
5560
pub trait PropertyKey: Copy {
5661
unsafe fn get_from<'c, C: Context<'c>>(

crates/neon/src/object/wrap.rs

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
use std::{any::Any, error, ffi::c_void, fmt, mem::MaybeUninit, ptr};
2+
3+
use crate::{
4+
context::{
5+
internal::{ContextInternal, Env},
6+
Context, Cx,
7+
},
8+
handle::Handle,
9+
object::Object,
10+
result::{NeonResult, ResultExt, Throw},
11+
sys,
12+
types::Finalize,
13+
};
14+
15+
type BoxAny = Box<dyn Any + 'static>;
16+
17+
#[derive(Debug)]
18+
pub struct WrapError(WrapErrorType);
19+
20+
impl WrapError {
21+
fn already_wrapped() -> Self {
22+
Self(WrapErrorType::AlreadyWrapped)
23+
}
24+
25+
fn not_wrapped() -> Self {
26+
Self(WrapErrorType::NotWrapped)
27+
}
28+
29+
#[cfg(feature = "napi-8")]
30+
fn foreign_type() -> Self {
31+
Self(WrapErrorType::ForeignType)
32+
}
33+
}
34+
35+
impl fmt::Display for WrapError {
36+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37+
write!(f, "{}", self.0)
38+
}
39+
}
40+
41+
impl error::Error for WrapError {}
42+
43+
impl<T> ResultExt<T> for Result<T, WrapError> {
44+
fn or_throw<'cx, C>(self, cx: &mut C) -> NeonResult<T>
45+
where
46+
C: Context<'cx>,
47+
{
48+
match self {
49+
Ok(v) => Ok(v),
50+
Err(err) => cx.throw_error(err.to_string()),
51+
}
52+
}
53+
}
54+
55+
#[derive(Debug)]
56+
enum WrapErrorType {
57+
AlreadyWrapped,
58+
NotWrapped,
59+
#[cfg(feature = "napi-8")]
60+
ForeignType,
61+
}
62+
63+
impl fmt::Display for WrapErrorType {
64+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
65+
match self {
66+
Self::AlreadyWrapped => write!(f, "Object is already wrapped"),
67+
Self::NotWrapped => write!(f, "Object is not wrapped"),
68+
#[cfg(feature = "napi-8")]
69+
Self::ForeignType => write!(f, "Object is wrapped by another addon"),
70+
}
71+
}
72+
}
73+
74+
pub fn wrap<T, V>(cx: &mut Cx, o: Handle<V>, v: T) -> NeonResult<Result<(), WrapError>>
75+
where
76+
T: Finalize + 'static,
77+
V: Object,
78+
{
79+
let env = cx.env().to_raw();
80+
let o = o.to_local();
81+
let v = Box::into_raw(Box::new(Box::new(v) as BoxAny));
82+
83+
unsafe extern "C" fn finalize<T>(env: sys::Env, data: *mut c_void, _hint: *mut c_void)
84+
where
85+
T: Finalize + 'static,
86+
{
87+
let data = Box::from_raw(data.cast::<BoxAny>());
88+
let data = *data.downcast::<T>().unwrap();
89+
let env = Env::from(env);
90+
91+
Cx::with_context(env, move |mut cx| data.finalize(&mut cx));
92+
}
93+
94+
unsafe {
95+
match sys::wrap(
96+
env,
97+
o,
98+
v.cast(),
99+
Some(finalize::<T>),
100+
ptr::null_mut(),
101+
ptr::null_mut(),
102+
) {
103+
Err(sys::Status::InvalidArg) => {
104+
let _ = Box::from_raw(v);
105+
106+
return Ok(Err(WrapError::already_wrapped()));
107+
}
108+
Err(sys::Status::PendingException) => {
109+
let _ = Box::from_raw(v);
110+
111+
return Err(Throw::new());
112+
}
113+
res => res.unwrap(),
114+
}
115+
}
116+
117+
#[cfg(feature = "napi-8")]
118+
unsafe {
119+
match sys::type_tag_object(env, o, &*crate::MODULE_TAG) {
120+
Err(sys::Status::InvalidArg) => return Ok(Err(WrapError::foreign_type())),
121+
res => res.unwrap(),
122+
}
123+
}
124+
125+
Ok(Ok(()))
126+
}
127+
128+
pub fn unwrap<'cx, T, V>(cx: &mut Cx, o: Handle<'cx, V>) -> NeonResult<Result<&'cx T, WrapError>>
129+
where
130+
T: Finalize + 'static,
131+
V: Object,
132+
{
133+
let env = cx.env().to_raw();
134+
let o = o.to_local();
135+
136+
#[cfg(feature = "napi-8")]
137+
unsafe {
138+
let mut is_tagged = false;
139+
140+
match sys::check_object_type_tag(env, o, &*crate::MODULE_TAG, &mut is_tagged) {
141+
Err(sys::Status::PendingException) => return Err(Throw::new()),
142+
res => res.unwrap(),
143+
}
144+
145+
if !is_tagged {
146+
return Ok(Err(WrapError::not_wrapped()));
147+
}
148+
}
149+
150+
let data = unsafe {
151+
let mut data = MaybeUninit::<*mut BoxAny>::uninit();
152+
153+
match sys::unwrap(env, o, data.as_mut_ptr().cast()) {
154+
Err(sys::Status::PendingException) => return Err(Throw::new()),
155+
res => res.unwrap(),
156+
}
157+
158+
&*data.assume_init()
159+
};
160+
161+
Ok(data.downcast_ref().ok_or_else(WrapError::not_wrapped))
162+
}

crates/neon/src/sys/bindings/functions.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,17 @@ mod napi1 {
258258
message: *const c_char,
259259
message_len: usize,
260260
);
261+
262+
fn wrap(
263+
env: Env,
264+
js_object: Value,
265+
native_object: *mut c_void,
266+
finalize_cb: Finalize,
267+
finalize_hint: *mut c_void,
268+
result: *mut Ref,
269+
) -> Status;
270+
271+
fn unwrap(env: Env, js_object: Value, result: *mut *mut c_void) -> Status;
261272
}
262273
);
263274
}

test/napi/lib/boxed.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,19 @@ describe("boxed", function () {
7272

7373
assert.throws(() => addon.person_greet(unit), /failed to downcast/);
7474
});
75+
76+
it("should be able to wrap a Rust value in an object", () => {
77+
const msg = "Hello, World!";
78+
const o = {};
79+
80+
addon.wrapString(o, msg);
81+
assert.strictEqual(addon.unwrapString(o), msg);
82+
});
83+
84+
it("should not be able to wrap an object twice", () => {
85+
const o = {};
86+
87+
addon.wrapString(o, "Hello, World!");
88+
assert.throws(() => addon.wrapString(o, "nope"), /already wrapped/);
89+
});
7590
});

test/napi/src/js/boxed.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,13 @@ fn boxed_string_concat(Boxed(this): Boxed<String>, rhs: String) -> String {
8888
fn boxed_string_repeat(_cx: &mut FunctionContext, this: Boxed<String>, n: f64) -> String {
8989
this.0.repeat(n as usize)
9090
}
91+
92+
#[neon::export]
93+
fn wrap_string(cx: &mut Cx, o: Handle<JsObject>, s: String) -> NeonResult<()> {
94+
neon::object::wrap(cx, o, s)?.or_throw(cx)
95+
}
96+
97+
#[neon::export]
98+
fn unwrap_string(cx: &mut Cx, o: Handle<JsObject>) -> NeonResult<String> {
99+
neon::object::unwrap(cx, o)?.map(String::clone).or_throw(cx)
100+
}

0 commit comments

Comments
 (0)