use std::ptr;
#[cfg(feature = "napi-6")]
use std::sync::Arc;
use neon_runtime::no_panic::FailureBoundary;
#[cfg(feature = "napi-6")]
use neon_runtime::tsfn::ThreadsafeFunction;
use neon_runtime::{napi, raw};
use crate::context::{internal::Env, Context};
use crate::handle::{internal::TransparentNoCopyWrapper, Managed};
#[cfg(feature = "napi-6")]
use crate::lifecycle::{DropData, InstanceData};
use crate::result::JsResult;
use crate::types::{private::ValueInternal, Handle, Object, Value};
#[cfg(feature = "channel-api")]
use crate::{
context::TaskContext,
event::{Channel, JoinHandle, SendError},
};
const BOUNDARY: FailureBoundary = FailureBoundary {
both: "A panic and exception occurred while resolving a `neon::types::Deferred`",
exception: "An exception occurred while resolving a `neon::types::Deferred`",
panic: "A panic occurred while resolving a `neon::types::Deferred`",
};
#[cfg_attr(docsrs, doc(cfg(feature = "promise-api")))]
#[derive(Debug)]
#[repr(transparent)]
pub struct JsPromise(raw::Local);
impl JsPromise {
pub(crate) fn new<'a, C: Context<'a>>(cx: &mut C) -> (Deferred, Handle<'a, Self>) {
let (deferred, promise) = unsafe { napi::promise::create(cx.env().to_raw()) };
let deferred = Deferred {
internal: Some(NodeApiDeferred(deferred)),
#[cfg(feature = "napi-6")]
drop_queue: InstanceData::drop_queue(cx),
};
(deferred, Handle::new_internal(JsPromise(promise)))
}
}
unsafe impl TransparentNoCopyWrapper for JsPromise {
type Inner = raw::Local;
fn into_inner(self) -> Self::Inner {
self.0
}
}
impl Managed for JsPromise {
fn to_raw(&self) -> raw::Local {
self.0
}
fn from_raw(_env: Env, h: raw::Local) -> Self {
Self(h)
}
}
impl ValueInternal for JsPromise {
fn name() -> String {
"Promise".to_string()
}
fn is_typeof<Other: Value>(env: Env, other: &Other) -> bool {
unsafe { neon_runtime::tag::is_promise(env.to_raw(), other.to_raw()) }
}
}
impl Value for JsPromise {}
impl Object for JsPromise {}
#[cfg_attr(docsrs, doc(cfg(feature = "promise-api")))]
pub struct Deferred {
internal: Option<NodeApiDeferred>,
#[cfg(feature = "napi-6")]
drop_queue: Arc<ThreadsafeFunction<DropData>>,
}
impl Deferred {
pub fn resolve<'a, V, C>(self, cx: &mut C, value: Handle<V>)
where
V: Value,
C: Context<'a>,
{
unsafe {
napi::promise::resolve(cx.env().to_raw(), self.into_inner(), value.to_raw());
}
}
pub fn reject<'a, V, C>(self, cx: &mut C, value: Handle<V>)
where
V: Value,
C: Context<'a>,
{
unsafe {
napi::promise::reject(cx.env().to_raw(), self.into_inner(), value.to_raw());
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "channel-api")))]
#[cfg(feature = "channel-api")]
pub fn try_settle_with<V, F>(
self,
channel: &Channel,
complete: F,
) -> Result<JoinHandle<()>, SendError>
where
V: Value,
F: FnOnce(TaskContext) -> JsResult<V> + Send + 'static,
{
channel.try_send(move |cx| {
self.try_catch_settle(cx, move |cx| complete(cx));
Ok(())
})
}
#[cfg_attr(docsrs, doc(cfg(feature = "channel-api")))]
#[cfg(feature = "channel-api")]
pub fn settle_with<V, F>(self, channel: &Channel, complete: F) -> JoinHandle<()>
where
V: Value,
F: FnOnce(TaskContext) -> JsResult<V> + Send + 'static,
{
self.try_settle_with(channel, complete).unwrap()
}
pub(crate) fn try_catch_settle<'a, C, V, F>(self, cx: C, f: F)
where
C: Context<'a>,
V: Value,
F: FnOnce(C) -> JsResult<'a, V>,
{
unsafe {
BOUNDARY.catch_failure(
cx.env().to_raw(),
Some(self.into_inner()),
move |_| match f(cx) {
Ok(value) => value.to_raw(),
Err(_) => ptr::null_mut(),
},
);
}
}
pub(crate) fn into_inner(mut self) -> napi::Deferred {
self.internal.take().unwrap().0
}
}
#[repr(transparent)]
pub(crate) struct NodeApiDeferred(napi::Deferred);
unsafe impl Send for NodeApiDeferred {}
#[cfg(feature = "napi-6")]
impl NodeApiDeferred {
pub(crate) unsafe fn leaked(self, env: raw::Env) {
napi::promise::reject_err_message(
env,
self.0,
"`neon::types::Deferred` was dropped without being settled",
);
}
}
impl Drop for Deferred {
#[cfg(not(feature = "napi-6"))]
fn drop(&mut self) {
if self.internal.is_none() {
return;
}
if std::thread::panicking() {
eprintln!("Warning: neon::types::JsPromise leaked during a panic");
return;
}
if let Ok(true) = crate::context::internal::IS_RUNNING.try_with(|v| *v.borrow()) {
panic!("Must settle a `neon::types::JsPromise` with `neon::types::Deferred`");
}
}
#[cfg(feature = "napi-6")]
fn drop(&mut self) {
if let Some(internal) = self.internal.take() {
let _ = self.drop_queue.call(DropData::Deferred(internal), None);
}
}
}