feat: init
This commit is contained in:
149
nix-js/src/runtime.rs
Normal file
149
nix-js/src/runtime.rs
Normal file
@@ -0,0 +1,149 @@
|
||||
use std::cell::RefCell;
|
||||
use std::sync::Once;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::value::{AttrSet, Const, List, Symbol, Value};
|
||||
|
||||
static INIT: Once = Once::new();
|
||||
|
||||
thread_local! {
|
||||
static ISOLATE: RefCell<v8::OwnedIsolate> =
|
||||
RefCell::new(v8::Isolate::new(Default::default()));
|
||||
}
|
||||
|
||||
pub fn run(script: &str) -> Result<Value> {
|
||||
INIT.call_once(|| {
|
||||
v8::V8::initialize_platform(v8::new_default_platform(0, false).make_shared());
|
||||
v8::V8::initialize();
|
||||
});
|
||||
|
||||
ISOLATE.with_borrow_mut(|isolate| run_impl(script, isolate))
|
||||
}
|
||||
|
||||
fn run_impl(script: &str, isolate: &mut v8::Isolate) -> Result<Value> {
|
||||
let handle_scope = std::pin::pin!(v8::HandleScope::new(isolate));
|
||||
let handle_scope = &mut handle_scope.init();
|
||||
let context = v8::Context::new(handle_scope, v8::ContextOptions::default());
|
||||
let scope = &mut v8::ContextScope::new(handle_scope, context);
|
||||
|
||||
let runtime_code = include_str!("./runtime/runtime.js");
|
||||
let runtime_source = v8::String::new(scope, runtime_code).unwrap();
|
||||
let runtime_script = v8::Script::compile(scope, runtime_source, None).unwrap();
|
||||
|
||||
if runtime_script.run(scope).is_none() {
|
||||
return Err(Error::eval_error("Failed to initialize runtime".to_string()));
|
||||
}
|
||||
|
||||
let source = v8::String::new(scope, script).unwrap();
|
||||
|
||||
// Use TryCatch to capture JavaScript exceptions
|
||||
let try_catch = std::pin::pin!(v8::TryCatch::new(scope));
|
||||
let try_catch = &mut try_catch.init();
|
||||
let script = match v8::Script::compile(try_catch, source, None) {
|
||||
Some(script) => script,
|
||||
None => {
|
||||
if let Some(exception) = try_catch.exception() {
|
||||
let exception_string = exception
|
||||
.to_string(try_catch)
|
||||
.unwrap()
|
||||
.to_rust_string_lossy(try_catch);
|
||||
return Err(Error::eval_error(format!("Compilation error: {}", exception_string)));
|
||||
} else {
|
||||
return Err(Error::eval_error("Unknown compilation error".to_string()));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match script.run(try_catch) {
|
||||
Some(result) => Ok(to_value(result, try_catch)),
|
||||
None => {
|
||||
if let Some(exception) = try_catch.exception() {
|
||||
let exception_string = exception
|
||||
.to_string(try_catch)
|
||||
.unwrap()
|
||||
.to_rust_string_lossy(try_catch);
|
||||
Err(Error::eval_error(format!("Runtime error: {}", exception_string)))
|
||||
} else {
|
||||
Err(Error::eval_error("Unknown runtime error".to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn to_value<'a>(
|
||||
val: v8::Local<'a, v8::Value>,
|
||||
scope: &v8::PinnedRef<'a, v8::HandleScope>,
|
||||
) -> Value {
|
||||
match () {
|
||||
_ if val.is_int32() => {
|
||||
let val = val.to_int32(scope).unwrap().value();
|
||||
Value::Const(Const::Int(val as i64))
|
||||
}
|
||||
_ if val.is_big_int() => {
|
||||
let (val, true) = val.to_big_int(scope).unwrap().i64_value() else {
|
||||
todo!()
|
||||
};
|
||||
Value::Const(Const::Int(val))
|
||||
}
|
||||
_ if val.is_number() => {
|
||||
let val = val.to_number(scope).unwrap().value();
|
||||
Value::Const(Const::Float(val))
|
||||
}
|
||||
_ if val.is_true() => Value::Const(Const::Bool(true)),
|
||||
_ if val.is_false() => Value::Const(Const::Bool(false)),
|
||||
_ if val.is_null() => Value::Const(Const::Bool(true)),
|
||||
_ if val.is_string() => {
|
||||
let val = val.to_string(scope).unwrap();
|
||||
Value::String(val.to_rust_string_lossy(scope))
|
||||
}
|
||||
_ if val.is_array() => {
|
||||
let val = val.try_cast::<v8::Array>().unwrap();
|
||||
let len = val.length();
|
||||
let list = (0..len)
|
||||
.map(|i| {
|
||||
let val = val.get_index(scope, i).unwrap();
|
||||
to_value(val, scope)
|
||||
})
|
||||
.collect();
|
||||
Value::List(List::new(list))
|
||||
}
|
||||
_ if val.is_object() => {
|
||||
let val = val.to_object(scope).unwrap();
|
||||
let keys = val
|
||||
.get_own_property_names(scope, v8::GetPropertyNamesArgsBuilder::new().build())
|
||||
.unwrap();
|
||||
let len = keys.length();
|
||||
let attrs = (0..len)
|
||||
.map(|i| {
|
||||
let key = keys.get_index(scope, i).unwrap();
|
||||
let val = val.get(scope, key).unwrap();
|
||||
let key = key.to_rust_string_lossy(scope);
|
||||
(Symbol::new(key), to_value(val, scope))
|
||||
})
|
||||
.collect();
|
||||
Value::AttrSet(AttrSet::new(attrs))
|
||||
}
|
||||
_ if val.is_function_template() => Value::PrimOp,
|
||||
_ if val.is_function() => Value::Func,
|
||||
_ => todo!("{}", val.type_repr()),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_value_working() {
|
||||
assert_eq!(
|
||||
run("({
|
||||
test: [1, 9223372036854775807n, true, false, 'hello world!']
|
||||
})").unwrap(),
|
||||
Value::AttrSet(AttrSet::new(std::collections::BTreeMap::from([(
|
||||
Symbol::from("test"),
|
||||
Value::List(List::new(vec![
|
||||
Value::Const(Const::Int(1)),
|
||||
Value::Const(Const::Int(9223372036854775807)),
|
||||
Value::Const(Const::Bool(true)),
|
||||
Value::Const(Const::Bool(false)),
|
||||
Value::String("hello world!".to_string())
|
||||
]))
|
||||
)])))
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user