use crate::host::{with_host, JsObj};
use fusevm::Value;
use indexmap::IndexMap;
pub const METHODS: &[&str] = &[
"getHeapStatistics",
"getHeapSpaceStatistics",
"getHeapCodeStatistics",
"serialize",
"deserialize",
"setFlagsFromString",
"getHeapSnapshot",
"cachedDataVersionTag",
];
const CACHED_DATA_VERSION_TAG: f64 = 3_527_742_766.0;
pub const SERIALIZER_METHODS: &[&str] = &["writeHeader", "writeValue", "releaseBuffer"];
pub const DESERIALIZER_METHODS: &[&str] = &["readHeader", "readValue"];
pub fn call(method: &str, args: &[Value]) -> Option<Result<Value, String>> {
Some(match method {
"getHeapStatistics" => Ok(heap_statistics()),
"getHeapSpaceStatistics" => Ok(with_host(|h| h.new_array(Vec::new()))),
"getHeapCodeStatistics" => Ok(heap_code_statistics()),
"serialize" => serialize(args),
"deserialize" => deserialize(args),
"setFlagsFromString" => Ok(Value::Undef),
"getHeapSnapshot" => Err(crate::host::type_error(
"v8.getHeapSnapshot is not supported: node-js does not run on V8",
)),
"cachedDataVersionTag" => Ok(Value::Float(CACHED_DATA_VERSION_TAG)),
_ => return None,
})
}
pub fn constant(name: &str) -> Option<Value> {
match name {
"Serializer" | "Deserializer" | "DefaultSerializer" | "DefaultDeserializer" => {
Some(with_host(|h| h.alloc(JsObj::Builtin(name.into()))))
}
_ => None,
}
}
pub fn construct(name: &str, args: &[Value]) -> Result<Value, String> {
match name {
"Serializer" | "DefaultSerializer" => Ok(with_host(|h| {
let mut m = IndexMap::new();
m.insert("@@native".into(), h.new_str("Serializer"));
m.insert("@@json".into(), Value::Undef);
h.new_object(m)
})),
"Deserializer" | "DefaultDeserializer" => {
let json = with_host(|h| h.str_of(&args.first().cloned().unwrap_or(Value::Undef)));
Ok(with_host(|h| {
let jv = h.new_str(json);
let mut m = IndexMap::new();
m.insert("@@native".into(), h.new_str("Deserializer"));
m.insert("@@json".into(), jv);
h.new_object(m)
}))
}
_ => Err(crate::host::type_error(&format!(
"v8.{name} is not a constructor"
))),
}
}
pub fn instance_call(
tag: &str,
recv: &Value,
method: &str,
args: Vec<Value>,
) -> Result<Value, String> {
match (tag, method) {
("Serializer", "writeHeader") => Ok(Value::Undef),
("Serializer", "writeValue") => {
let json = crate::builtins::call_builtin_function(
"JSON.stringify",
vec![args.first().cloned().unwrap_or(Value::Undef)],
)?;
let s = with_host(|h| h.str_of(&json));
with_host(|h| {
let sv = h.new_str(s);
if let Some(JsObj::Object(p)) = h.get_mut(recv) {
p.insert("@@json".into(), sv);
}
});
Ok(Value::Bool(true))
}
("Serializer", "releaseBuffer") => {
let s = with_host(|h| match h.get(recv) {
Some(JsObj::Object(p)) => match p.get("@@json") {
Some(Value::Undef) | None => String::new(),
Some(v) => h.str_of(v),
},
_ => String::new(),
});
Ok(super::buffer::from_bytes(s.as_bytes()))
}
("Deserializer", "readHeader") => Ok(Value::Undef),
("Deserializer", "readValue") => {
let sv = with_host(|h| match h.get(recv) {
Some(JsObj::Object(p)) => p.get("@@json").cloned().unwrap_or(Value::Undef),
_ => Value::Undef,
});
crate::builtins::call_builtin_function("JSON.parse", vec![sv])
}
_ => Err(crate::host::type_error(&format!(
"{method} is not a function"
))),
}
}
fn heap_statistics() -> Value {
zeros_object(&[
"total_heap_size",
"total_heap_size_executable",
"total_physical_size",
"total_available_size",
"used_heap_size",
"heap_size_limit",
"malloced_memory",
"peak_malloced_memory",
"does_zap_garbage",
"number_of_native_contexts",
"number_of_detached_contexts",
"total_global_handles_size",
"used_global_handles_size",
"external_memory",
])
}
fn heap_code_statistics() -> Value {
zeros_object(&[
"code_and_metadata_size",
"bytecode_and_metadata_size",
"external_script_source_size",
"cpu_profiler_metadata_size",
])
}
fn zeros_object(keys: &[&str]) -> Value {
with_host(|h| {
let mut m = IndexMap::new();
for k in keys {
m.insert((*k).to_string(), Value::Float(0.0));
}
h.new_object(m)
})
}
fn serialize(args: &[Value]) -> Result<Value, String> {
let v = args.first().cloned().unwrap_or(Value::Undef);
let json = crate::builtins::call_builtin_function("JSON.stringify", vec![v])?;
let s = with_host(|h| h.str_of(&json));
let sval = with_host(|h| h.new_str(s));
super::buffer::static_call("from", std::slice::from_ref(&sval)).unwrap_or(Ok(Value::Undef))
}
fn deserialize(args: &[Value]) -> Result<Value, String> {
let s = with_host(|h| h.str_of(&args.first().cloned().unwrap_or(Value::Undef)));
let sval = with_host(|h| h.new_str(s));
crate::builtins::call_builtin_function("JSON.parse", vec![sval])
}