use super::arg_num;
use crate::host::{with_host, JsObj};
use fusevm::Value;
use indexmap::IndexMap;
pub const TIMEOUT_METHODS: &[&str] = &[
"ref", "unref", "hasRef", "refresh", "close", "valueOf", "toString",
];
pub const IMMEDIATE_METHODS: &[&str] = &["ref", "unref", "hasRef", "close", "valueOf", "toString"];
pub fn new_handle(id: u64, tag: &'static str) -> Value {
with_host(|h| {
let mut m = IndexMap::new();
m.insert("@@native".into(), h.new_str(tag));
m.insert("@@timerId".into(), Value::Float(id as f64));
h.new_object(m)
})
}
pub fn handle_id(v: &Value) -> Option<u64> {
with_host(|h| match h.get(v) {
Some(JsObj::Object(p)) => p.get("@@timerId").map(|n| h.to_number(n) as u64),
_ => None,
})
}
pub fn instance_call(recv: &Value, method: &str, _args: &[Value]) -> Result<Value, String> {
let id = handle_id(recv).unwrap_or(0);
match method {
"ref" => {
with_host(|h| h.set_timer_refed(id, true));
Ok(recv.clone())
}
"unref" => {
with_host(|h| h.set_timer_refed(id, false));
Ok(recv.clone())
}
"hasRef" => Ok(Value::Bool(with_host(|h| h.timer_has_ref(id)))),
"refresh" => {
with_host(|h| h.refresh_timer(id));
Ok(recv.clone())
}
"close" => {
with_host(|h| h.cancel_timer(id));
Ok(recv.clone())
}
"valueOf" => Ok(Value::Float(id as f64)),
"toString" => Ok(with_host(|h| h.new_str(id.to_string()))),
_ => Err(crate::host::type_error(&format!(
"timeout.{method} is not a function"
))),
}
}
pub const METHODS: &[&str] = &[
"setTimeout",
"setInterval",
"setImmediate",
"clearTimeout",
"clearInterval",
"clearImmediate",
];
pub fn call(method: &str, args: &[Value]) -> Option<Result<Value, String>> {
let global = match method {
"setTimeout" | "setInterval" | "setImmediate" | "clearTimeout" | "clearInterval" => method,
"clearImmediate" => "clearTimeout",
_ => return None,
};
Some(crate::builtins::call_builtin_function(
global,
args.to_vec(),
))
}
pub const PROMISES_METHODS: &[&str] = &["setTimeout", "setImmediate"];
pub fn promises_call(method: &str, args: &[Value]) -> Option<Result<Value, String>> {
match method {
"setTimeout" => {
let delay = arg_num(args, 0);
let value = args.get(1).cloned().unwrap_or(Value::Undef);
Some(Ok(schedule_promise("setTimeout", Some(delay), value)))
}
"setImmediate" => {
let value = args.first().cloned().unwrap_or(Value::Undef);
Some(Ok(schedule_promise("setImmediate", None, value)))
}
_ => None,
}
}
fn schedule_promise(kind: &str, delay: Option<f64>, value: Value) -> Value {
let (promise, id) = with_host(|h| {
let p = h.new_promise();
let id = h.promise_id(&p).unwrap_or(0);
(p, id)
});
let resolver = with_host(|h| h.alloc(JsObj::Builtin(format!("@@presolve:{id}"))));
let timer_args = match delay {
Some(d) => vec![resolver, Value::Float(d), value],
None => vec![resolver, value],
};
let _ = crate::builtins::call_builtin_function(kind, timer_args);
promise
}