use cstring::cstr;
use once_cell::sync::OnceCell;
use std::{
collections::HashMap,
future::Future,
pin::Pin,
sync::{Arc, Mutex},
task::{Context, Poll, Waker},
};
type JSValue = f32;
extern "C" {
fn jsffiregister(function: i32, code: i32);
fn jsfficall0(obj: JSValue, function: i32);
fn jsfficall1(obj: JSValue, function: i32, a1_type: i32, a1: JSValue);
fn jsfficall2(
obj: JSValue,
function: i32,
a1_type: i32,
a1: JSValue,
a2_type: i32,
a2: JSValue,
);
fn jsfficall3(
obj: JSValue,
function: i32,
a1_type: i32,
a1: JSValue,
a2_type: i32,
a2: JSValue,
a3_type: i32,
a3: JSValue,
);
fn jsfficall4(
obj: JSValue,
function: i32,
a1_type: i32,
a1: JSValue,
a2_type: i32,
a2: JSValue,
a3_type: i32,
a3: JSValue,
a4_type: i32,
a4: JSValue,
);
fn jsfficall5(
obj: JSValue,
function: i32,
a1_type: i32,
a1: JSValue,
a2_type: i32,
a2: JSValue,
a3_type: i32,
a3: JSValue,
a4_type: i32,
a4: JSValue,
a5_type: i32,
a5: JSValue,
);
fn jsfficall6(
obj: JSValue,
function: i32,
a1_type: i32,
a1: JSValue,
a2_type: i32,
a2: JSValue,
a3_type: i32,
a3: JSValue,
a4_type: i32,
a4: JSValue,
a5_type: i32,
a5: JSValue,
a6_type: i32,
a6: JSValue,
);
fn jsfficall7(
obj: JSValue,
function: i32,
a1_type: i32,
a1: JSValue,
a2_type: i32,
a2: JSValue,
a3_type: i32,
a3: JSValue,
a4_type: i32,
a4: JSValue,
a5_type: i32,
a5: JSValue,
a6_type: i32,
a6: JSValue,
a7_type: i32,
a7: JSValue,
);
fn jsfficall8(
obj: JSValue,
function: i32,
a1_type: i32,
a1: JSValue,
a2_type: i32,
a2: JSValue,
a3_type: i32,
a3: JSValue,
a4_type: i32,
a4: JSValue,
a5_type: i32,
a5: JSValue,
a6_type: i32,
a6: JSValue,
a7_type: i32,
a7: JSValue,
a8_type: i32,
a8: JSValue,
);
fn jsfficall9(
obj: JSValue,
function: i32,
a1_type: i32,
a1: JSValue,
a2_type: i32,
a2: JSValue,
a3_type: i32,
a3: JSValue,
a4_type: i32,
a4: JSValue,
a5_type: i32,
a5: JSValue,
a6_type: i32,
a6: JSValue,
a7_type: i32,
a7: JSValue,
a8_type: i32,
a8: JSValue,
a9_type: i32,
a9: JSValue,
);
fn jsfficall10(
obj: JSValue,
function: i32,
a1_type: i32,
a1: JSValue,
a2_type: i32,
a2: JSValue,
a3_type: i32,
a3: JSValue,
a4_type: i32,
a4: JSValue,
a5_type: i32,
a5: JSValue,
a6_type: i32,
a6: JSValue,
a7_type: i32,
a7: JSValue,
a8_type: i32,
a8: JSValue,
a9_type: i32,
a9: JSValue,
a10_type: i32,
a10: JSValue,
);
}
pub const UNDEFINED: JSValue = 0.0;
pub const NULL: JSValue = 1.0;
pub const CONSOLE: JSValue = 2.0;
pub const WINDOW: JSValue = 3.0;
pub const FALSE: JSValue = 0.0;
pub const TRUE: JSValue = 1.0;
pub const TYPE_NOTHING: i32 = 0;
pub const TYPE_NUM: i32 = 1;
pub const TYPE_STRING: i32 = 2;
pub const TYPE_BOOL: i32 = 3;
pub const TYPE_FUNCTION: i32 = 4;
pub const TYPE_OBJ: i32 = 5;
pub fn register(function: i32, code: &str) {
unsafe { jsffiregister(function, cstr(code)) }
}
pub fn call_0(obj: JSValue, function: i32) {
unsafe { jsfficall0(obj, function) }
}
pub fn call_1(obj: JSValue, function: i32, a1_type: i32, a1: JSValue) {
unsafe { jsfficall1(obj, function, a1_type, a1) }
}
pub fn call_2(obj: JSValue, function: i32, a1_type: i32, a1: JSValue, a2_type: i32, a2: JSValue) {
unsafe { jsfficall2(obj, function, a1_type, a1, a2_type, a2) }
}
pub fn call_3(
obj: JSValue,
function: i32,
a1_type: i32,
a1: JSValue,
a2_type: i32,
a2: f32,
a3_type: i32,
a3: JSValue,
) {
unsafe { jsfficall3(obj, function, a1_type, a1, a2_type, a2, a3_type, a3) }
}
pub fn call_4(
obj: JSValue,
function: i32,
a1_type: i32,
a1: JSValue,
a2_type: i32,
a2: f32,
a3_type: i32,
a3: JSValue,
a4_type: i32,
a4: JSValue,
) {
unsafe {
jsfficall4(
obj, function, a1_type, a1, a2_type, a2, a3_type, a3, a4_type, a4,
)
}
}
pub fn call_5(
obj: JSValue,
function: i32,
a1_type: i32,
a1: JSValue,
a2_type: i32,
a2: f32,
a3_type: i32,
a3: JSValue,
a4_type: i32,
a4: JSValue,
a5_type: i32,
a5: JSValue,
) {
unsafe {
jsfficall5(
obj, function, a1_type, a1, a2_type, a2, a3_type, a3, a4_type, a4, a5_type, a5,
)
}
}
pub fn call_6(
obj: JSValue,
function: i32,
a1_type: i32,
a1: JSValue,
a2_type: i32,
a2: f32,
a3_type: i32,
a3: JSValue,
a4_type: i32,
a4: JSValue,
a5_type: i32,
a5: JSValue,
a6_type: i32,
a6: JSValue,
) {
unsafe {
jsfficall6(
obj, function, a1_type, a1, a2_type, a2, a3_type, a3, a4_type, a4, a5_type, a5,
a6_type, a6,
)
}
}
pub fn call_7(
obj: JSValue,
function: i32,
a1_type: i32,
a1: JSValue,
a2_type: i32,
a2: f32,
a3_type: i32,
a3: JSValue,
a4_type: i32,
a4: JSValue,
a5_type: i32,
a5: JSValue,
a6_type: i32,
a6: JSValue,
a7_type: i32,
a7: JSValue,
) {
unsafe {
jsfficall7(
obj, function, a1_type, a1, a2_type, a2, a3_type, a3, a4_type, a4, a5_type, a5,
a6_type, a6, a7_type, a7,
)
}
}
pub fn call_8(
obj: JSValue,
function: i32,
a1_type: i32,
a1: JSValue,
a2_type: i32,
a2: f32,
a3_type: i32,
a3: JSValue,
a4_type: i32,
a4: JSValue,
a5_type: i32,
a5: JSValue,
a6_type: i32,
a6: JSValue,
a7_type: i32,
a7: JSValue,
a8_type: i32,
a8: JSValue,
) {
unsafe {
jsfficall8(
obj, function, a1_type, a1, a2_type, a2, a3_type, a3, a4_type, a4, a5_type, a5,
a6_type, a6, a7_type, a7, a8_type, a8,
)
}
}
pub fn call_9(
obj: JSValue,
function: i32,
a1_type: i32,
a1: JSValue,
a2_type: i32,
a2: f32,
a3_type: i32,
a3: JSValue,
a4_type: i32,
a4: JSValue,
a5_type: i32,
a5: JSValue,
a6_type: i32,
a6: JSValue,
a7_type: i32,
a7: JSValue,
a8_type: i32,
a8: JSValue,
a9_type: i32,
a9: JSValue,
) {
unsafe {
jsfficall9(
obj, function, a1_type, a1, a2_type, a2, a3_type, a3, a4_type, a4, a5_type, a5,
a6_type, a6, a7_type, a7, a8_type, a8, a9_type, a9,
)
}
}
pub fn call_10(
obj: JSValue,
function: i32,
a1_type: i32,
a1: JSValue,
a2_type: i32,
a2: f32,
a3_type: i32,
a3: JSValue,
a4_type: i32,
a4: JSValue,
a5_type: i32,
a5: JSValue,
a6_type: i32,
a6: JSValue,
a7_type: i32,
a7: JSValue,
a8_type: i32,
a8: JSValue,
a9_type: i32,
a9: JSValue,
a10_type: i32,
a10: JSValue,
) {
unsafe {
jsfficall10(
obj, function, a1_type, a1, a2_type, a2, a3_type, a3, a4_type, a4, a5_type, a5,
a6_type, a6, a7_type, a7, a8_type, a8, a9_type, a9, a10_type, a10,
)
}
}
struct Callback {
cur_id: i32,
handlers: HashMap<i32, Arc<Mutex<Box<dyn Fn() -> () + Send + 'static>>>>,
}
pub fn to_string(s: &str) -> JSValue {
cstr(s) as f32
}
fn get_callbacks() -> &'static Mutex<Callback> {
static INSTANCE: OnceCell<Mutex<Callback>> = OnceCell::new();
INSTANCE.get_or_init(|| {
Mutex::new(Callback {
cur_id: 0,
handlers: HashMap::new(),
})
})
}
pub fn create_callback(cb: Box<dyn Fn() -> () + Send + 'static>) -> i32 {
let mut h = get_callbacks().lock().unwrap();
h.cur_id += 1;
let id = h.cur_id;
h.handlers.insert(id, Arc::new(Mutex::new(cb)));
return id;
}
pub struct CallbackFuture {
shared_state: Arc<Mutex<SharedState>>,
}
struct SharedState {
completed: bool,
waker: Option<Waker>,
}
impl Future for CallbackFuture {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut shared_state = self.shared_state.lock().unwrap();
if shared_state.completed {
Poll::Ready(())
} else {
shared_state.waker = Some(cx.waker().clone());
Poll::Pending
}
}
}
impl CallbackFuture {
pub fn create() -> (Self, f32) {
let shared_state = Arc::new(Mutex::new(SharedState {
completed: false,
waker: None,
}));
let thread_shared_state = shared_state.clone();
let id = create_callback(Box::new(move || {
let mut shared_state = thread_shared_state.lock().unwrap();
shared_state.completed = true;
if let Some(waker) = shared_state.waker.take() {
std::mem::drop(shared_state);
waker.wake()
}
}));
(CallbackFuture { shared_state }, id as f32)
}
}
#[no_mangle]
pub fn jsfficallback(id: i32) -> () {
let h = get_callbacks().lock().unwrap();
let handler_ref = h.handlers.get(&id).unwrap().clone();
std::mem::drop(h);
let handler = handler_ref.lock().unwrap();
handler()
}