use crate::host::{with_host, JsObj};
use fusevm::Value;
use indexmap::IndexMap;
pub const METHODS: &[&str] = &[
"runInThisContext",
"runInNewContext",
"runInContext",
"createContext",
"createScript",
"compileFunction",
"isContext",
];
pub const SCRIPT_METHODS: &[&str] = &["runInThisContext", "runInNewContext"];
pub fn call(method: &str, args: &[Value]) -> Option<Result<Value, String>> {
Some(match method {
"runInThisContext" => run_code(&super::arg_str(args, 0)),
"runInNewContext" => run_in_context(&super::arg_str(args, 0), args.get(1)),
"runInContext" => run_in_context(&super::arg_str(args, 0), args.get(1)),
"createScript" => construct(args),
"compileFunction" => compile_function(args),
"createContext" => Ok(match args.first() {
Some(o) if with_host(|h| matches!(h.get(o), Some(JsObj::Object(_)))) => o.clone(),
_ => with_host(|h| h.new_object(IndexMap::new())),
}),
"isContext" => Ok(Value::Bool(with_host(|h| {
matches!(
h.get(args.first().unwrap_or(&Value::Undef)),
Some(JsObj::Object(_))
)
}))),
_ => return None,
})
}
pub fn construct(args: &[Value]) -> Result<Value, String> {
let code = super::arg_str(args, 0);
Ok(with_host(|h| {
let code_val = h.new_str(code);
let mut m = IndexMap::new();
m.insert("@@native".into(), h.new_str("Script"));
m.insert("@@code".into(), code_val);
h.new_object(m)
}))
}
pub fn instance_call(recv: &Value, method: &str, args: Vec<Value>) -> Result<Value, String> {
let code = with_host(|h| match h.get(recv) {
Some(JsObj::Object(p)) => p.get("@@code").map(|v| h.str_of(v)).unwrap_or_default(),
_ => String::new(),
});
match method {
"runInThisContext" => run_code(&code),
"runInNewContext" | "runInContext" => run_in_context(&code, args.first()),
_ => Err(crate::host::type_error(&format!(
"{method} is not a function"
))),
}
}
fn run_code(code: &str) -> Result<Value, String> {
let prog = crate::compile_completion(code)?;
let main = crate::load_merged(prog);
crate::host::run_chunk_on(main)
}
fn compile_function(args: &[Value]) -> Result<Value, String> {
let code = super::arg_str(args, 0);
let params = match args.get(1) {
Some(v) => with_host(|h| match h.get(v) {
Some(JsObj::Array(items)) => items
.iter()
.map(|it| h.str_of(it))
.collect::<Vec<_>>()
.join(", "),
_ => String::new(),
}),
None => String::new(),
};
let src = format!("(function anonymous({params}\n) {{\n{code}\n}})");
run_code(&src)
}
fn run_in_context(code: &str, sandbox: Option<&Value>) -> Result<Value, String> {
let keys: Vec<(String, Value)> = match sandbox {
Some(s) => with_host(|h| match h.get(s) {
Some(JsObj::Object(p)) => p
.iter()
.filter(|(k, _)| !k.starts_with("@@") && !k.starts_with('#'))
.map(|(k, v)| (k.clone(), v.clone()))
.collect(),
_ => Vec::new(),
}),
None => Vec::new(),
};
with_host(|h| {
for (k, v) in &keys {
h.set_global(k, v.clone());
}
});
let r = run_code(code);
if let Some(s) = sandbox {
for (k, _) in &keys {
if let Some(nv) = with_host(|h| h.read_global(k)) {
with_host(|h| {
if let Some(JsObj::Object(p)) = h.get_mut(s) {
p.insert(k.clone(), nv);
}
});
}
}
}
r
}