1use std::any::type_name;
2use std::ffi::CString;
3use std::marker::PhantomData;
4use std::ops::{Deref, DerefMut};
5use std::ptr;
6
7use crate::{
8 bindgen_runtime::{
9 raw_finalize_unchecked, FromNapiValue, JsObjectValue, MaybeTypeTag, Object, ObjectFinalize,
10 Reference, Result, TypeName, ValidateNapiValue,
11 },
12 check_status, sys, Env, JsValue, Property, PropertyAttributes, Value, ValueType,
13};
14
15#[derive(Clone, Copy)]
16pub struct This<'env, T = Object<'env>> {
17 pub object: T,
18 _phantom: &'env PhantomData<()>,
19}
20
21impl<T> From<T> for This<'_, T> {
22 fn from(value: T) -> Self {
23 Self {
24 object: value,
25 _phantom: &PhantomData,
26 }
27 }
28}
29
30impl<T> Deref for This<'_, T> {
31 type Target = T;
32
33 fn deref(&self) -> &Self::Target {
34 &self.object
35 }
36}
37
38impl<T> DerefMut for This<'_, T> {
39 fn deref_mut(&mut self) -> &mut Self::Target {
40 &mut self.object
41 }
42}
43
44impl<'env, T: JsValue<'env>> JsValue<'env> for This<'_, T> {
45 fn value(&self) -> Value {
46 self.object.value()
47 }
48}
49
50impl<T: FromNapiValue> FromNapiValue for This<'_, T> {
51 unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
52 Ok(Self {
53 object: T::from_napi_value(env, napi_val)?,
54 _phantom: &PhantomData,
55 })
56 }
57}
58
59#[derive(Clone, Copy)]
60pub struct ClassInstance<'env, T: 'env> {
61 pub value: sys::napi_value,
62 env: sys::napi_env,
63 inner: *mut T,
64 _phantom: &'env PhantomData<()>,
65}
66
67impl<'env, T: 'env + MaybeTypeTag> JsValue<'env> for ClassInstance<'env, T> {
73 fn value(&self) -> Value {
74 Value {
75 env: self.env,
76 value: self.value,
77 value_type: ValueType::Object,
78 }
79 }
80}
81
82impl<'env, T: 'env + MaybeTypeTag> JsObjectValue<'env> for ClassInstance<'env, T> {}
83
84impl<'env, T: 'env> ClassInstance<'env, T> {
85 #[doc(hidden)]
86 pub unsafe fn new(value: sys::napi_value, env: sys::napi_env, inner: *mut T) -> Self {
87 Self {
88 value,
89 env,
90 inner: unsafe { &mut *inner },
91 _phantom: &PhantomData,
92 }
93 }
94
95 pub fn as_object<'a>(&self, env: &'a Env) -> Object<'a> {
96 Object(
97 Value {
98 env: env.raw(),
99 value: self.value,
100 value_type: ValueType::Object,
101 },
102 PhantomData,
103 )
104 }
105
106 pub fn assign_to_this<'a, 'this, U>(
110 &'a self,
111 name: &'a str,
112 this: &'a mut This<U>,
113 ) -> Result<ClassInstance<'this, T>>
114 where
115 'this: 'env,
116 U: FromNapiValue + JsValue<'this>,
117 {
118 let name = CString::new(name)?;
119 check_status!(
120 unsafe {
121 sys::napi_set_named_property(self.env, this.object.raw(), name.as_ptr(), self.value)
122 },
123 "Failed to assign ClassInstance<{}> to this",
124 std::any::type_name::<T>()
125 )?;
126 let val: ClassInstance<'this, T> = ClassInstance {
127 value: self.value,
128 env: self.env,
129 inner: self.inner,
130 _phantom: &PhantomData,
131 };
132 Ok(val)
133 }
134
135 pub fn assign_to_this_with_attributes<'a, 'this, U>(
139 &'a self,
140 name: &'a str,
141 attributes: PropertyAttributes,
142 this: &'a mut This<U>,
143 ) -> Result<ClassInstance<'this, T>>
144 where
145 'this: 'env,
146 T: MaybeTypeTag,
150 U: FromNapiValue + JsValue<'this>,
151 {
152 let property = Property::new()
153 .with_utf8_name(name)?
154 .with_value(self)
155 .with_property_attributes(attributes);
156
157 check_status!(
158 unsafe {
159 sys::napi_define_properties(
160 self.env,
161 this.object.value().value,
162 1,
163 [property.raw()].as_ptr(),
164 )
165 },
166 "Failed to define properties on This in `assign_to_this_with_attributes`"
167 )?;
168
169 let val: ClassInstance<'this, T> = ClassInstance {
170 value: self.value,
171 env: self.env,
172 inner: self.inner,
173 _phantom: &PhantomData,
174 };
175 Ok(val)
176 }
177}
178
179impl<'env, T: 'env> TypeName for ClassInstance<'env, T>
180where
181 &'env T: TypeName,
182{
183 fn type_name() -> &'static str {
184 type_name::<&T>()
185 }
186
187 fn value_type() -> ValueType {
188 <&T>::value_type()
189 }
190}
191
192impl<'env, T: 'env> ValidateNapiValue for ClassInstance<'env, T>
193where
194 &'env T: ValidateNapiValue,
195{
196 unsafe fn validate(
197 env: sys::napi_env,
198 napi_val: sys::napi_value,
199 ) -> crate::Result<sys::napi_value> {
200 unsafe { <&'env T>::validate(env, napi_val) }
201 }
202}
203
204impl<'env, T: 'env + MaybeTypeTag> FromNapiValue for ClassInstance<'env, T> {
205 unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> crate::Result<Self> {
206 let mut value = ptr::null_mut();
207 check_status!(
208 unsafe { sys::napi_unwrap(env, napi_val, &mut value) },
209 "Unwrap value [{}] from class failed",
210 type_name::<T>(),
211 )?;
212
213 #[cfg(all(feature = "napi8", not(target_family = "wasm")))]
217 unsafe {
218 crate::bindgen_runtime::validate_type_tag(env, napi_val, &T::type_tag(), type_name::<T>())?;
219 }
220
221 let value = unsafe { Box::from_raw(value as *mut T) };
222 Ok(Self {
223 value: napi_val,
224 inner: Box::leak(value),
225 env,
226 _phantom: &PhantomData,
227 })
228 }
229}
230
231impl<'env, T: 'env> Deref for ClassInstance<'env, T> {
232 type Target = T;
233
234 fn deref(&self) -> &Self::Target {
235 unsafe { &*self.inner }
236 }
237}
238
239impl<'env, T: 'env> DerefMut for ClassInstance<'env, T> {
240 fn deref_mut(&mut self) -> &mut Self::Target {
241 unsafe { &mut *self.inner }
242 }
243}
244
245impl<'env, T: 'env> AsRef<T> for ClassInstance<'env, T> {
246 fn as_ref(&self) -> &T {
247 unsafe { &*self.inner }
248 }
249}
250
251pub trait JavaScriptClassExt: Sized {
252 fn into_instance(self, env: &Env) -> Result<ClassInstance<'_, Self>>;
253 fn into_reference(self, env: Env) -> Result<Reference<Self>>;
254 fn instance_of<'env, V: JsValue<'env>>(env: &Env, value: &V) -> Result<bool>;
255}
256
257#[doc(hidden)]
261pub unsafe fn new_instance<T: 'static + ObjectFinalize + MaybeTypeTag>(
262 env: sys::napi_env,
263 wrapped_value: *mut std::ffi::c_void,
264 ctor_ref: sys::napi_ref,
265) -> Result<sys::napi_value> {
266 let mut ctor = std::ptr::null_mut();
267 check_status!(
268 sys::napi_get_reference_value(env, ctor_ref, &mut ctor),
269 "Failed to get constructor reference of class `{}`",
270 type_name::<T>(),
271 )?;
272
273 let mut result = std::ptr::null_mut();
274 crate::__private::___CALL_FROM_FACTORY.with(|inner| inner.set(true));
275 check_status!(
276 sys::napi_new_instance(env, ctor, 0, std::ptr::null_mut(), &mut result),
277 "Failed to construct class `{}`",
278 type_name::<T>(),
279 )?;
280 crate::__private::___CALL_FROM_FACTORY.with(|inner| inner.set(false));
281 let mut object_ref = std::ptr::null_mut();
282 let initial_finalize: Box<dyn FnOnce()> = Box::new(|| {});
283 #[allow(clippy::arc_with_non_send_sync)]
286 let finalize_callbacks_ptr = std::sync::Arc::into_raw(std::sync::Arc::new(std::cell::Cell::new(
287 Box::into_raw(initial_finalize),
288 )));
289 check_status!(
290 sys::napi_wrap(
291 env,
292 result,
293 wrapped_value,
294 Some(raw_finalize_unchecked::<T>),
295 std::ptr::null_mut(),
296 &mut object_ref,
297 ),
298 "Failed to wrap native object of class `{}`",
299 type_name::<T>(),
300 )?;
301
302 Reference::<T>::add_ref(
303 env,
304 wrapped_value,
305 (wrapped_value, object_ref, finalize_callbacks_ptr),
306 );
307
308 #[cfg(all(feature = "napi8", not(target_family = "wasm")))]
312 unsafe {
313 crate::bindgen_runtime::tag_object(env, result, &T::type_tag())?;
314 }
315
316 Ok(result)
317}