1use std::{any::Any, error, ffi::c_void, fmt, mem::MaybeUninit, ptr};
2
3use crate::{
4 context::{
5 internal::{ContextInternal, Env},
6 Context, Cx,
7 },
8 handle::Handle,
9 object::Object,
10 result::{JsResult, NeonResult, ResultExt, Throw},
11 sys,
12 types::{extract::TryIntoJs, Finalize, Value},
13};
14
15type BoxAny = Box<dyn Any + 'static>;
16
17#[derive(Debug)]
18pub struct WrapError(WrapErrorType);
19
20impl WrapError {
21 fn object_expected() -> Self {
22 Self(WrapErrorType::ObjectExpected)
23 }
24
25 fn already_wrapped() -> Self {
26 Self(WrapErrorType::AlreadyWrapped)
27 }
28
29 #[cfg(feature = "napi-8")]
30 fn not_wrapped() -> Self {
31 Self(WrapErrorType::NotWrapped)
32 }
33
34 fn wrong_type(expected: &'static str) -> Self {
35 Self(WrapErrorType::WrongType(expected))
36 }
37
38 #[cfg(feature = "napi-8")]
39 fn foreign_type() -> Self {
40 Self(WrapErrorType::ForeignType)
41 }
42}
43
44impl fmt::Display for WrapError {
45 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
46 write!(f, "{}", self.0)
47 }
48}
49
50impl error::Error for WrapError {}
51
52impl crate::types::extract::private::Sealed for WrapError {}
53
54impl<'cx> TryIntoJs<'cx> for WrapError {
55 type Value = crate::types::JsError;
56
57 fn try_into_js(self, cx: &mut Cx<'cx>) -> JsResult<'cx, Self::Value> {
58 match self.0 {
59 WrapErrorType::ObjectExpected => cx.type_error("object expected"),
60 _ => cx.type_error(self.to_string()),
61 }
62 }
63}
64
65impl<T> ResultExt<T> for Result<T, WrapError> {
66 fn or_throw<'cx, C>(self, cx: &mut C) -> NeonResult<T>
67 where
68 C: Context<'cx>,
69 {
70 match self {
71 Ok(v) => Ok(v),
72 Err(WrapError(WrapErrorType::ObjectExpected)) => cx.throw_type_error("object expected"),
73 Err(err) => cx.throw_type_error(err.to_string()),
74 }
75 }
76}
77
78#[derive(Debug)]
79enum WrapErrorType {
80 ObjectExpected,
81 AlreadyWrapped,
82 #[cfg(feature = "napi-8")]
83 NotWrapped,
84 WrongType(&'static str),
85 #[cfg(feature = "napi-8")]
86 ForeignType,
87}
88
89fn ref_cell_target_type_name(s: &str) -> Option<String> {
90 if let Some(start) = s.find('<') {
91 let s = &s[start + 1..];
92 if let Some(end) = s.find('>') {
93 return Some(s[0..end].to_string());
94 }
95 }
96 None
97}
98
99impl fmt::Display for WrapErrorType {
100 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
101 match self {
102 Self::ObjectExpected => write!(f, "object expected"),
103 Self::AlreadyWrapped => write!(f, "non-class instance expected"),
104 #[cfg(feature = "napi-8")]
105 Self::NotWrapped => write!(f, "class instance expected"),
106 Self::WrongType(expected) => {
107 let target_type_name =
108 ref_cell_target_type_name(expected).unwrap_or(expected.to_string());
109 write!(f, "expected instance of {}", target_type_name)
110 }
111 #[cfg(feature = "napi-8")]
112 Self::ForeignType => write!(f, "Neon object expected"),
113 }
114 }
115}
116
117pub fn wrap<T, V>(cx: &mut Cx, o: Handle<V>, v: T) -> NeonResult<Result<(), WrapError>>
118where
119 T: Finalize + 'static,
120 V: Object,
121{
122 let env = cx.env().to_raw();
123 let o = o.to_local();
124 let v = Box::into_raw(Box::new(Box::new(v) as BoxAny));
125
126 unsafe extern "C" fn finalize<T>(env: sys::Env, data: *mut c_void, _hint: *mut c_void)
131 where
132 T: Finalize + 'static,
133 {
134 let data = Box::from_raw(data.cast::<BoxAny>());
135 let data = *data.downcast::<T>().unwrap();
136 let env = Env::from(env);
137
138 Cx::with_context(env, move |mut cx| data.finalize(&mut cx));
139 }
140
141 unsafe {
145 match sys::wrap(
146 env,
147 o,
148 v.cast(),
149 Some(finalize::<T>),
150 ptr::null_mut(),
151 ptr::null_mut(),
152 ) {
153 Err(sys::Status::InvalidArg) => {
154 let _ = Box::from_raw(v);
156
157 return Ok(Err(WrapError::already_wrapped()));
158 }
159 Err(sys::Status::PendingException) => {
160 let _ = Box::from_raw(v);
162
163 return Err(Throw::new());
164 }
165 res => res.unwrap(),
168 }
169
170 #[cfg(feature = "napi-8")]
171 match sys::type_tag_object(env, o, &*crate::MODULE_TAG) {
172 Err(sys::Status::InvalidArg) => {
173 sys::remove_wrap(env, o, ptr::null_mut()).unwrap();
174
175 let _ = Box::from_raw(v);
177
178 return Ok(Err(WrapError::foreign_type()));
179 }
180 res => res.unwrap(),
181 }
182 }
183
184 Ok(Ok(()))
185}
186
187pub fn unwrap<'cx, T, V>(cx: &mut Cx, o: Handle<'cx, V>) -> NeonResult<Result<&'cx T, WrapError>>
188where
189 T: Finalize + 'static,
190 V: Value,
191{
192 let env = cx.env().to_raw();
193 let o = o.to_local();
194
195 #[cfg(feature = "napi-8")]
196 unsafe {
199 let mut is_tagged = false;
200
201 match sys::check_object_type_tag(env, o, &*crate::MODULE_TAG, &mut is_tagged) {
202 Err(sys::Status::PendingException) => return Err(Throw::new()),
203 Err(sys::Status::ObjectExpected) => return Ok(Err(WrapError::object_expected())),
204 res => res.unwrap(),
205 }
206
207 if !is_tagged {
208 return Ok(Err(WrapError::not_wrapped()));
209 }
210 }
211
212 let data = unsafe {
215 let mut data = MaybeUninit::<*mut BoxAny>::uninit();
216
217 match sys::unwrap(env, o, data.as_mut_ptr().cast()) {
218 Err(sys::Status::PendingException) => return Err(Throw::new()),
219 Err(sys::Status::ObjectExpected) => return Ok(Err(WrapError::object_expected())),
220 res => res.unwrap(),
221 }
222
223 &*data.assume_init()
227 };
228
229 match data.downcast_ref() {
230 Some(result) => Ok(Ok(result)),
231 None => Ok(Err(WrapError::wrong_type(std::any::type_name::<T>()))),
232 }
233}