| 1 | use std::marker::PhantomData; |
| 2 | use std::os::raw::c_void; |
| 3 | use std::ptr; |
| 4 | |
| 5 | use crate::bindgen_runtime::ToNapiValue; |
| 6 | use crate::{check_status, JsObject, Value}; |
| 7 | use crate::{sys, Env, Error, Result}; |
| 8 | #[cfg (feature = "deferred_trace" )] |
| 9 | use crate::{NapiRaw, NapiValue}; |
| 10 | |
| 11 | #[cfg (feature = "deferred_trace" )] |
| 12 | /// A javascript error which keeps a stack trace |
| 13 | /// to the original caller in an asynchronous context. |
| 14 | /// This is required as the stack trace is lost when |
| 15 | /// an error is created in a different thread. |
| 16 | /// |
| 17 | /// See this issue for more details: |
| 18 | /// https://github.com/nodejs/node-addon-api/issues/595 |
| 19 | #[repr (transparent)] |
| 20 | #[derive (Clone)] |
| 21 | struct DeferredTrace(sys::napi_ref); |
| 22 | |
| 23 | #[cfg (feature = "deferred_trace" )] |
| 24 | impl DeferredTrace { |
| 25 | fn new(raw_env: sys::napi_env) -> Result<Self> { |
| 26 | let env = unsafe { Env::from_raw(raw_env) }; |
| 27 | let reason = env.create_string("none" ).unwrap(); |
| 28 | |
| 29 | let mut js_error = ptr::null_mut(); |
| 30 | check_status!( |
| 31 | unsafe { sys::napi_create_error(raw_env, ptr::null_mut(), reason.raw(), &mut js_error) }, |
| 32 | "Create error in DeferredTrace failed" |
| 33 | )?; |
| 34 | |
| 35 | let mut result = ptr::null_mut(); |
| 36 | check_status!( |
| 37 | unsafe { sys::napi_create_reference(raw_env, js_error, 1, &mut result) }, |
| 38 | "Create reference in DeferredTrace failed" |
| 39 | )?; |
| 40 | |
| 41 | Ok(Self(result)) |
| 42 | } |
| 43 | |
| 44 | fn into_rejected(self, raw_env: sys::napi_env, err: Error) -> Result<sys::napi_value> { |
| 45 | let env = unsafe { Env::from_raw(raw_env) }; |
| 46 | let mut raw = ptr::null_mut(); |
| 47 | check_status!( |
| 48 | unsafe { sys::napi_get_reference_value(raw_env, self.0, &mut raw) }, |
| 49 | "Failed to get referenced value in DeferredTrace" |
| 50 | )?; |
| 51 | |
| 52 | let mut obj = unsafe { JsObject::from_raw_unchecked(raw_env, raw) }; |
| 53 | let err_value = if !err.maybe_raw.is_null() { |
| 54 | let mut err_raw_value = std::ptr::null_mut(); |
| 55 | check_status!( |
| 56 | unsafe { sys::napi_get_reference_value(raw_env, err.maybe_raw, &mut err_raw_value) }, |
| 57 | "Get error reference in `to_napi_value` failed" |
| 58 | )?; |
| 59 | let err_obj = unsafe { JsObject::from_raw_unchecked(raw_env, err_raw_value) }; |
| 60 | |
| 61 | let err_value = if err_obj.has_named_property("message" )? { |
| 62 | // The error was already created inside the JS engine, just return it |
| 63 | Ok(unsafe { err_obj.raw() }) |
| 64 | } else { |
| 65 | obj.set_named_property("message" , "" )?; |
| 66 | obj.set_named_property("code" , "" )?; |
| 67 | Ok(raw) |
| 68 | }; |
| 69 | check_status!( |
| 70 | unsafe { sys::napi_delete_reference(raw_env, err.maybe_raw) }, |
| 71 | "Delete error reference in `to_napi_value` failed" |
| 72 | )?; |
| 73 | err_value |
| 74 | } else { |
| 75 | obj.set_named_property("message" , &err.reason)?; |
| 76 | obj.set_named_property( |
| 77 | "code" , |
| 78 | env.create_string_from_std(format!("{}" , err.status))?, |
| 79 | )?; |
| 80 | Ok(raw) |
| 81 | }; |
| 82 | check_status!( |
| 83 | unsafe { sys::napi_delete_reference(raw_env, self.0) }, |
| 84 | "Failed to get referenced value in DeferredTrace" |
| 85 | )?; |
| 86 | err_value |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | struct DeferredData<Data: ToNapiValue, Resolver: FnOnce(Env) -> Result<Data>> { |
| 91 | resolver: Result<Resolver>, |
| 92 | #[cfg (feature = "deferred_trace" )] |
| 93 | trace: DeferredTrace, |
| 94 | } |
| 95 | |
| 96 | pub struct JsDeferred<Data: ToNapiValue, Resolver: FnOnce(Env) -> Result<Data>> { |
| 97 | pub(crate) tsfn: sys::napi_threadsafe_function, |
| 98 | #[cfg (feature = "deferred_trace" )] |
| 99 | trace: DeferredTrace, |
| 100 | _data: PhantomData<Data>, |
| 101 | _resolver: PhantomData<Resolver>, |
| 102 | } |
| 103 | |
| 104 | // A trick to send the resolver into the `panic` handler |
| 105 | // Do not use clone in the other place besides the `fn execute_tokio_future` |
| 106 | impl<Data: ToNapiValue, Resolver: FnOnce(Env) -> Result<Data>> Clone |
| 107 | for JsDeferred<Data, Resolver> |
| 108 | { |
| 109 | fn clone(&self) -> Self { |
| 110 | Self { |
| 111 | tsfn: self.tsfn, |
| 112 | #[cfg (feature = "deferred_trace" )] |
| 113 | trace: self.trace.clone(), |
| 114 | _data: PhantomData, |
| 115 | _resolver: PhantomData, |
| 116 | } |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | unsafe impl<Data: ToNapiValue, Resolver: FnOnce(Env) -> Result<Data>> Send |
| 121 | for JsDeferred<Data, Resolver> |
| 122 | { |
| 123 | } |
| 124 | |
| 125 | impl<Data: ToNapiValue, Resolver: FnOnce(Env) -> Result<Data>> JsDeferred<Data, Resolver> { |
| 126 | pub(crate) fn new(env: sys::napi_env) -> Result<(Self, JsObject)> { |
| 127 | let (tsfn, promise) = js_deferred_new_raw(env, Some(napi_resolve_deferred::<Data, Resolver>))?; |
| 128 | |
| 129 | let deferred = Self { |
| 130 | tsfn, |
| 131 | #[cfg (feature = "deferred_trace" )] |
| 132 | trace: DeferredTrace::new(env)?, |
| 133 | _data: PhantomData, |
| 134 | _resolver: PhantomData, |
| 135 | }; |
| 136 | |
| 137 | Ok((deferred, promise)) |
| 138 | } |
| 139 | |
| 140 | /// Consumes the deferred, and resolves the promise. The provided function will be called |
| 141 | /// from the JavaScript thread, and should return the resolved value. |
| 142 | pub fn resolve(self, resolver: Resolver) { |
| 143 | self.call_tsfn(Ok(resolver)) |
| 144 | } |
| 145 | |
| 146 | /// Consumes the deferred, and rejects the promise with the provided error. |
| 147 | pub fn reject(self, error: Error) { |
| 148 | self.call_tsfn(Err(error)) |
| 149 | } |
| 150 | |
| 151 | fn call_tsfn(self, result: Result<Resolver>) { |
| 152 | let data = DeferredData { |
| 153 | resolver: result, |
| 154 | #[cfg (feature = "deferred_trace" )] |
| 155 | trace: self.trace, |
| 156 | }; |
| 157 | |
| 158 | // Call back into the JS thread via a threadsafe function. This results in napi_resolve_deferred being called. |
| 159 | let status = unsafe { |
| 160 | sys::napi_call_threadsafe_function( |
| 161 | self.tsfn, |
| 162 | Box::into_raw(Box::from(data)).cast(), |
| 163 | sys::ThreadsafeFunctionCallMode::blocking, |
| 164 | ) |
| 165 | }; |
| 166 | debug_assert!( |
| 167 | status == sys::Status::napi_ok, |
| 168 | "Call threadsafe function in JsDeferred failed" |
| 169 | ); |
| 170 | |
| 171 | let status = unsafe { |
| 172 | sys::napi_release_threadsafe_function(self.tsfn, sys::ThreadsafeFunctionReleaseMode::release) |
| 173 | }; |
| 174 | debug_assert!( |
| 175 | status == sys::Status::napi_ok, |
| 176 | "Release threadsafe function in JsDeferred failed" |
| 177 | ); |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | fn js_deferred_new_raw( |
| 182 | env: sys::napi_env, |
| 183 | resolve_deferred: sys::napi_threadsafe_function_call_js, |
| 184 | ) -> Result<(sys::napi_threadsafe_function, JsObject)> { |
| 185 | let mut raw_promise = ptr::null_mut(); |
| 186 | let mut raw_deferred = ptr::null_mut(); |
| 187 | check_status! { |
| 188 | unsafe { sys::napi_create_promise(env, &mut raw_deferred, &mut raw_promise) } |
| 189 | }?; |
| 190 | |
| 191 | // Create a threadsafe function so we can call back into the JS thread when we are done. |
| 192 | let mut async_resource_name = ptr::null_mut(); |
| 193 | check_status!( |
| 194 | unsafe { |
| 195 | sys::napi_create_string_utf8( |
| 196 | env, |
| 197 | "napi_resolve_deferred \0" .as_ptr().cast(), |
| 198 | 22, |
| 199 | &mut async_resource_name, |
| 200 | ) |
| 201 | }, |
| 202 | "Create async resource name in JsDeferred failed" |
| 203 | )?; |
| 204 | |
| 205 | let mut tsfn = ptr::null_mut(); |
| 206 | check_status!( |
| 207 | unsafe { |
| 208 | sys::napi_create_threadsafe_function( |
| 209 | env, |
| 210 | ptr::null_mut(), |
| 211 | ptr::null_mut(), |
| 212 | async_resource_name, |
| 213 | 0, |
| 214 | 1, |
| 215 | ptr::null_mut(), |
| 216 | None, |
| 217 | raw_deferred.cast(), |
| 218 | resolve_deferred, |
| 219 | &mut tsfn, |
| 220 | ) |
| 221 | }, |
| 222 | "Create threadsafe function in JsDeferred failed" |
| 223 | )?; |
| 224 | |
| 225 | let promise = JsObject(Value { |
| 226 | env, |
| 227 | value: raw_promise, |
| 228 | value_type: crate::ValueType::Object, |
| 229 | }); |
| 230 | |
| 231 | Ok((tsfn, promise)) |
| 232 | } |
| 233 | |
| 234 | extern "C" fn napi_resolve_deferred<Data: ToNapiValue, Resolver: FnOnce(Env) -> Result<Data>>( |
| 235 | env: sys::napi_env, |
| 236 | _js_callback: sys::napi_value, |
| 237 | context: *mut c_void, |
| 238 | data: *mut c_void, |
| 239 | ) { |
| 240 | let deferred = context.cast(); |
| 241 | let deferred_data: Box<DeferredData<Data, Resolver>> = unsafe { Box::from_raw(data.cast()) }; |
| 242 | let result = deferred_data |
| 243 | .resolver |
| 244 | .and_then(|resolver| resolver(unsafe { Env::from_raw(env) })) |
| 245 | .and_then(|res| unsafe { ToNapiValue::to_napi_value(env, res) }); |
| 246 | |
| 247 | if let Err(e) = result.and_then(|res| { |
| 248 | check_status!( |
| 249 | unsafe { sys::napi_resolve_deferred(env, deferred, res) }, |
| 250 | "Resolve deferred value failed" |
| 251 | ) |
| 252 | }) { |
| 253 | #[cfg (feature = "deferred_trace" )] |
| 254 | let error = deferred_data.trace.into_rejected(env, e); |
| 255 | #[cfg (not(feature = "deferred_trace" ))] |
| 256 | let error = Ok::<sys::napi_value, Error>(unsafe { crate::JsError::from(e).into_value(env) }); |
| 257 | |
| 258 | match error { |
| 259 | Ok(error) => { |
| 260 | unsafe { sys::napi_reject_deferred(env, deferred, error) }; |
| 261 | } |
| 262 | Err(err) => { |
| 263 | if cfg!(debug_assertions) { |
| 264 | println!("Failed to reject deferred: {:?}" , err); |
| 265 | let mut err = ptr::null_mut(); |
| 266 | let mut err_msg = ptr::null_mut(); |
| 267 | unsafe { |
| 268 | sys::napi_create_string_utf8( |
| 269 | env, |
| 270 | "Rejection failed \0" .as_ptr().cast(), |
| 271 | 0, |
| 272 | &mut err_msg, |
| 273 | ); |
| 274 | sys::napi_create_error(env, ptr::null_mut(), err_msg, &mut err); |
| 275 | sys::napi_reject_deferred(env, deferred, err); |
| 276 | } |
| 277 | } |
| 278 | } |
| 279 | } |
| 280 | } |
| 281 | } |
| 282 | |