use crate::host::{invoke, with_host};
use fusevm::Value;
use indexmap::IndexMap;
use std::cell::RefCell;
use std::collections::HashMap;
thread_local! {
static STORES: RefCell<HashMap<u32, Vec<Value>>> = RefCell::new(HashMap::new());
}
pub const METHODS: &[&str] = &["executionAsyncId", "triggerAsyncId", "createHook"];
pub const ALS_METHODS: &[&str] = &["getStore", "run", "enterWith", "exit", "disable"];
pub const HOOK_METHODS: &[&str] = &["enable", "disable"];
pub fn call(method: &str, _args: &[Value]) -> Option<Result<Value, String>> {
Some(match method {
"executionAsyncId" => Ok(Value::Float(1.0)),
"triggerAsyncId" => Ok(Value::Float(0.0)),
"createHook" => Ok(new_hook()),
_ => return None,
})
}
pub fn construct(name: &str, _args: &[Value]) -> Option<Result<Value, String>> {
match name {
"AsyncLocalStorage" => Some(Ok(new_native("AsyncLocalStorage"))),
_ => None,
}
}
fn new_native(tag: &'static str) -> Value {
with_host(|h| {
let mut m = IndexMap::new();
m.insert("@@native".into(), h.new_str(tag));
h.new_object(m)
})
}
fn new_hook() -> Value {
new_native("AsyncHook")
}
pub fn instance_call(
tag: &str,
recv: &Value,
method: &str,
args: Vec<Value>,
) -> Result<Value, String> {
match tag {
"AsyncHook" => match method {
"enable" | "disable" => Ok(recv.clone()),
_ => Err(crate::host::type_error(&format!(
"{method} is not a function"
))),
},
"AsyncLocalStorage" => als_call(recv, method, args),
_ => Err(crate::host::type_error(&format!(
"{method} is not a function"
))),
}
}
fn key(recv: &Value) -> u32 {
match recv {
Value::Obj(i) => *i,
_ => 0,
}
}
fn als_call(recv: &Value, method: &str, args: Vec<Value>) -> Result<Value, String> {
let id = key(recv);
match method {
"getStore" => Ok(STORES.with(|s| {
s.borrow()
.get(&id)
.and_then(|v| v.last().cloned())
.unwrap_or(Value::Undef)
})),
"run" => {
let store = args.first().cloned().unwrap_or(Value::Undef);
let cb = args.get(1).cloned().unwrap_or(Value::Undef);
let rest = args.get(2..).map(|s| s.to_vec()).unwrap_or_default();
with_store(id, store, cb, rest)
}
"exit" => {
let cb = args.first().cloned().unwrap_or(Value::Undef);
let rest = args.get(1..).map(|s| s.to_vec()).unwrap_or_default();
with_store(id, Value::Undef, cb, rest)
}
"enterWith" => {
let store = args.first().cloned().unwrap_or(Value::Undef);
STORES.with(|s| s.borrow_mut().entry(id).or_default().push(store));
Ok(Value::Undef)
}
"disable" => {
STORES.with(|s| {
s.borrow_mut().remove(&id);
});
Ok(Value::Undef)
}
_ => Err(crate::host::type_error(&format!(
"{method} is not a function"
))),
}
}
fn with_store(id: u32, store: Value, cb: Value, rest: Vec<Value>) -> Result<Value, String> {
STORES.with(|s| s.borrow_mut().entry(id).or_default().push(store));
let r = invoke(&cb, rest, None);
STORES.with(|s| {
if let Some(v) = s.borrow_mut().get_mut(&id) {
v.pop();
}
});
r
}