use std::ops::ControlFlow;
use boa_gc::{Finalize, Gc, Trace};
use crate::{Context, JsError, JsResult, JsValue, vm::CompletionRecord};
pub(crate) trait CoroutineBranch<T> {
fn branch(self) -> ControlFlow<JsResult<()>, T>;
}
impl<T, E> CoroutineBranch<T> for Result<T, E>
where
E: Into<JsError>,
{
fn branch(self) -> ControlFlow<JsResult<()>, T> {
match self {
Ok(v) => ControlFlow::Continue(v),
Err(e) => ControlFlow::Break(Err(e.into())),
}
}
}
impl CoroutineBranch<JsValue> for CompletionRecord {
fn branch(self) -> ControlFlow<JsResult<()>, JsValue> {
match self {
CompletionRecord::Normal(val) => ControlFlow::Continue(val),
CompletionRecord::Return(_) => ControlFlow::Break(Ok(())),
CompletionRecord::Throw(err) => ControlFlow::Break(Err(err)),
}
}
}
pub(crate) type CoroutineState = ControlFlow<JsResult<()>, JsValue>;
trait TraceableCoroutine: Trace {
fn call(&self, completion: CompletionRecord, context: &mut Context) -> CoroutineState;
}
#[derive(Trace, Finalize)]
struct Coroutine<F, T>
where
F: Fn(CompletionRecord, &T, &mut Context) -> CoroutineState,
T: Trace,
{
#[unsafe_ignore_trace]
f: F,
captures: T,
}
impl<F, T> TraceableCoroutine for Coroutine<F, T>
where
F: Fn(CompletionRecord, &T, &mut Context) -> CoroutineState,
T: Trace,
{
fn call(&self, completion: CompletionRecord, context: &mut Context) -> CoroutineState {
(self.f)(completion, &self.captures, context)
}
}
#[derive(Clone, Trace, Finalize)]
pub(crate) struct NativeCoroutine {
inner: Gc<dyn TraceableCoroutine>,
}
impl std::fmt::Debug for NativeCoroutine {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("NativeCoroutine").finish_non_exhaustive()
}
}
impl NativeCoroutine {
pub(crate) fn from_copy_closure_with_captures<F, T>(closure: F, captures: T) -> Self
where
F: Fn(CompletionRecord, &T, &mut Context) -> CoroutineState + Copy + 'static,
T: Trace + 'static,
{
unsafe { Self::from_closure_with_captures(closure, captures) }
}
pub(crate) unsafe fn from_closure_with_captures<F, T>(closure: F, captures: T) -> Self
where
F: Fn(CompletionRecord, &T, &mut Context) -> CoroutineState + 'static,
T: Trace + 'static,
{
let ptr = Gc::into_raw(Gc::new(Coroutine {
f: closure,
captures,
}));
unsafe {
Self {
inner: Gc::from_raw(ptr),
}
}
}
#[inline]
pub(crate) fn call(
&self,
completion: CompletionRecord,
context: &mut Context,
) -> CoroutineState {
self.inner.call(completion, context)
}
}