use std::{cell::RefCell, collections::VecDeque, fmt::Debug, future::Future, pin::Pin};
use crate::{
object::{JsFunction, NativeObject},
realm::Realm,
Context, JsResult, JsValue,
};
use boa_gc::{Finalize, Trace};
pub type FutureJob = Pin<Box<dyn Future<Output = NativeJob> + 'static>>;
pub struct NativeJob {
#[allow(clippy::type_complexity)]
f: Box<dyn FnOnce(&mut Context) -> JsResult<JsValue>>,
realm: Option<Realm>,
}
impl Debug for NativeJob {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("NativeJob").field("f", &"Closure").finish()
}
}
impl NativeJob {
pub fn new<F>(f: F) -> Self
where
F: FnOnce(&mut Context) -> JsResult<JsValue> + 'static,
{
Self {
f: Box::new(f),
realm: None,
}
}
pub fn with_realm<F>(f: F, realm: Realm, _context: &mut Context) -> Self
where
F: FnOnce(&mut Context) -> JsResult<JsValue> + 'static,
{
Self {
f: Box::new(f),
realm: Some(realm),
}
}
#[must_use]
pub const fn realm(&self) -> Option<&Realm> {
self.realm.as_ref()
}
pub fn call(self, context: &mut Context) -> JsResult<JsValue> {
if let Some(realm) = self.realm {
let old_realm = context.enter_realm(realm);
let result = (self.f)(context);
context.enter_realm(old_realm);
result
} else {
(self.f)(context)
}
}
}
#[derive(Trace, Finalize)]
pub struct JobCallback {
callback: JsFunction,
host_defined: Box<dyn NativeObject>,
}
impl Debug for JobCallback {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("JobCallback")
.field("callback", &self.callback)
.field("host_defined", &"dyn NativeObject")
.finish()
}
}
impl JobCallback {
#[inline]
pub fn new<T: NativeObject>(callback: JsFunction, host_defined: T) -> Self {
Self {
callback,
host_defined: Box::new(host_defined),
}
}
#[inline]
#[must_use]
pub const fn callback(&self) -> &JsFunction {
&self.callback
}
#[inline]
#[must_use]
pub fn host_defined(&self) -> &dyn NativeObject {
&*self.host_defined
}
#[inline]
pub fn host_defined_mut(&mut self) -> &mut dyn NativeObject {
&mut *self.host_defined
}
}
pub trait JobQueue {
fn enqueue_promise_job(&self, job: NativeJob, context: &mut Context);
fn run_jobs(&self, context: &mut Context);
fn enqueue_future_job(&self, future: FutureJob, context: &mut Context);
fn run_jobs_async<'a, 'ctx, 'fut>(
&'a self,
context: &'ctx mut Context,
) -> Pin<Box<dyn Future<Output = ()> + 'fut>>
where
'a: 'fut,
'ctx: 'fut,
{
Box::pin(async { self.run_jobs(context) })
}
}
#[derive(Debug, Clone, Copy)]
pub struct IdleJobQueue;
impl JobQueue for IdleJobQueue {
fn enqueue_promise_job(&self, _: NativeJob, _: &mut Context) {}
fn run_jobs(&self, _: &mut Context) {}
fn enqueue_future_job(&self, _: FutureJob, _: &mut Context) {}
}
#[derive(Default)]
pub struct SimpleJobQueue(RefCell<VecDeque<NativeJob>>);
impl Debug for SimpleJobQueue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("SimpleQueue").field(&"..").finish()
}
}
impl SimpleJobQueue {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
impl JobQueue for SimpleJobQueue {
fn enqueue_promise_job(&self, job: NativeJob, _: &mut Context) {
self.0.borrow_mut().push_back(job);
}
fn run_jobs(&self, context: &mut Context) {
let mut next_job = self.0.borrow_mut().pop_front();
while let Some(job) = next_job {
if job.call(context).is_err() {
self.0.borrow_mut().clear();
return;
};
next_job = self.0.borrow_mut().pop_front();
}
}
fn enqueue_future_job(&self, future: FutureJob, context: &mut Context) {
let job = pollster::block_on(future);
self.enqueue_promise_job(job, context);
}
}