1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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 _register(function: i32, code: i32);
    fn _call(obj: JSValue, function: i32, a1_type: i32, a1: JSValue, a2_type: i32, a2: JSValue);
}

pub const UNDEFINED: JSValue = 0.0;

pub const FALSE: JSValue = 0.0;
pub const TRUE: JSValue = 1.0;

pub const JS_TYPE_NOTHING: i32 = 0;
pub const JS_TYPE_NUM: i32 = 1;
pub const JS_TYPE_STRING: i32 = 2;
pub const JS_TYPE_BOOL: i32 = 3;
pub const JS_TYPE_FUNCTION: i32 = 4;

pub fn register_js_function(function: i32, code: &str) {
    unsafe { _register(function, cstr(code)) }
}

pub fn js_call_1(obj: f32, function: i32, a1_type: i32, a1: f32) {
    unsafe { _call(obj, function, a1_type, a1, JS_TYPE_NOTHING, UNDEFINED) }
}

pub fn js_call_2(obj: f32, function: i32, a1_type: i32, a1: f32, a2_type: i32, a2: f32) {
    unsafe { _call(obj, function, a1_type, a1, a2_type, a2) }
}

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(),
        })
    })
}

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>>,
}

/// Shared state between the future and the waiting thread
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 callback(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()
}