use std::{
ffi::{CStr, CString},
os::raw::c_void,
vec,
};
use crate::bindgen::{
JSCFunctionEnum_JS_CFUNC_generic, JSContext, JSRuntime, JSValue, JS_Eval, JS_GetGlobalObject,
JS_NewCFunction2, JS_NewContext, JS_NewFloat64, JS_NewRuntime, JS_NewString, JS_SetOpaque,
JS_SetPropertyStr, JS_ToCStringLen2,
};
pub struct Runtime {
pub ptr: *mut JSRuntime,
}
pub struct Context {
pub ptr: *mut JSContext,
}
impl Runtime {
pub fn new() -> Self {
unsafe {
let ptr = JS_NewRuntime();
Self { ptr }
}
}
pub fn new_context(&self) -> Context {
unsafe {
let ptr = JS_NewContext(self.ptr);
Context { ptr }
}
}
}
pub trait FromJs {
fn from_js(v: JSValue) -> Self;
}
pub trait StaticJsFn<A = (), R = ()>: Sized {
fn apply( args: Vec<JSValue>) -> impl IntoJs;
}
pub trait IntoJs: Sized {
fn into_js(self, ctx: *mut JSContext) -> JSValue;
}
impl IntoJs for f64 {
fn into_js(self, ctx: *mut JSContext) -> JSValue {
unsafe { JS_NewFloat64(ctx, self) }
}
}
impl IntoJs for () {
fn into_js(self, ctx: *mut JSContext) -> JSValue {
unsafe { JS_NewFloat64(ctx, 0.) }
}
}
impl IntoJs for String {
fn into_js(self, ctx: *mut JSContext) -> JSValue {
println!("====IntoJs1");
let str = CString::new("a").unwrap();
println!("====IntoJs2");
let ptr = str.as_ptr();
println!("====IntoJs3");
std::mem::forget(str);
println!("====IntoJs4");
let v = unsafe { JS_NewString(ctx, ptr) };
println!("====IntoJs5");
v
}
}
impl<A, R, F> StaticJsFn<A, R> for F
where
F: Fn(A) -> R,
A: IntoJs,
R: IntoJs,
{
fn apply( args: Vec<JSValue>) -> impl IntoJs {
println!("=======");
1.
}
}
impl<A1, A2, R, F> StaticJsFn<(A1, A2), R> for F
where
F: Fn(A1, A2) -> R,
A1: IntoJs,
A2: IntoJs,
R: IntoJs,
{
fn apply( args: Vec<JSValue>) -> impl IntoJs {
println!("=======");
1.
}
}
impl Context {
pub fn new_function<A, B, F: StaticJsFn<A, B> + 'static>(&self, name: &str, f: F) -> JSValue {
let name = CString::new(name).unwrap().clone();
let name_ptr = name.as_ptr();
let length = 1;
unsafe extern "C" fn trampoline<A, B, F>(
ctx: *mut JSContext,
this_val: JSValue,
argc: ::std::os::raw::c_int,
argv: *mut JSValue,
) -> JSValue
where
F: StaticJsFn<A, B> + 'static,
{
println!("call {}", this_val.u.ptr as usize);
let closure_ptr = this_val.u.ptr;
let closure: &mut F = &mut *(closure_ptr as *mut F);
let r = F ::apply(vec![]);
println!("====1:");
let v = r.into_js(ctx);
println!("====2:");
return v;
}
let wrap: unsafe extern "C" fn(*mut JSContext, JSValue, i32, *mut JSValue) -> JSValue =
trampoline::<A, B, F>;
let func = Some(wrap);
unsafe {
let fun = JS_NewCFunction2(
self.ptr,
func,
name_ptr,
length,
JSCFunctionEnum_JS_CFUNC_generic,
0,
);
fun
}
}
pub fn eval(&self, code: &str, filename: &str, eval_flags: std::os::raw::c_int) {
let input = CString::new(code).unwrap();
let input_len = input.as_bytes().len();
let filename = CString::new(filename).unwrap();
unsafe {
let input_ptr = input.as_ptr();
let filename_ptr = filename.as_ptr();
let v = JS_Eval(self.ptr, input_ptr, input_len, filename_ptr, eval_flags);
let ptr = JS_ToCStringLen2(self.ptr, std::ptr::null_mut(), v, 0);
let output = CStr::from_ptr(ptr);
println!("output: {:?}", output);
}
}
pub fn set_property(&self, name: &str, this_obj: JSValue, value: JSValue) {
unsafe {
let name = CString::new(name).unwrap();
let name_ptr = name.as_ptr();
JS_SetPropertyStr(self.ptr, this_obj, name_ptr, value);
}
}
pub fn get_global_object(&self) -> JSValue {
unsafe { JS_GetGlobalObject(self.ptr) }
}
}