Skip to main content

grower_jsni/
lib.rs

1use wasm_bindgen::prelude::wasm_bindgen;
2use std::vec;
3
4#[allow(unused_imports)]
5use wasm_bindgen::JsValue;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum JSNIKind {
9    I8,
10    I16,
11    I32,
12    I64,
13    U8,
14    U16,
15    U32,
16    U64,
17    F32,
18    F64,
19    Bool,
20    Char,
21    String,
22    VecU8,
23    Null,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub struct JSNIValue {
28    pub kind: JSNIKind,
29    pub value: u64,
30}
31
32macro_rules! impl_from_primitive {
33    ($ty:ty, $kind:expr) => {
34        impl From<$ty> for JSNIValue {
35            fn from(value: $ty) -> Self {
36                JSNIValue {
37                    kind: $kind,
38                    value: value as u64,
39                }
40            }
41        }
42    };
43}
44
45impl_from_primitive!(i8, JSNIKind::I8);
46impl_from_primitive!(i16, JSNIKind::I16);
47impl_from_primitive!(i32, JSNIKind::I32);
48impl_from_primitive!(i64, JSNIKind::I64);
49impl_from_primitive!(u8, JSNIKind::U8);
50impl_from_primitive!(u16, JSNIKind::U16);
51impl_from_primitive!(u32, JSNIKind::U32);
52impl_from_primitive!(u64, JSNIKind::U64);
53impl_from_primitive!(bool, JSNIKind::Bool);
54impl_from_primitive!(char, JSNIKind::Char);
55
56impl From<f32> for JSNIValue {
57    fn from(value: f32) -> Self {
58        let bytes = value.to_le_bytes();
59        JSNIValue {
60            kind: JSNIKind::F32,
61            value: ((bytes[0] as u64) << 0)
62                | ((bytes[1] as u64) << 8)
63                | ((bytes[2] as u64) << 16)
64                | ((bytes[3] as u64) << 24),
65        }
66    }
67}
68
69impl From<f64> for JSNIValue {
70    fn from(value: f64) -> Self {
71        let bytes = value.to_le_bytes();
72        JSNIValue {
73            kind: JSNIKind::F64,
74            value: ((bytes[0] as u64) << 0)
75                | ((bytes[1] as u64) << 8)
76                | ((bytes[2] as u64) << 16)
77                | ((bytes[3] as u64) << 24)
78                | ((bytes[4] as u64) << 32)
79                | ((bytes[5] as u64) << 40)
80                | ((bytes[6] as u64) << 48)
81                | ((bytes[7] as u64) << 56),
82        }
83    }
84}
85
86impl From<Vec<u8>> for JSNIValue {
87    fn from(value: Vec<u8>) -> Self {
88        let len = value.len();
89        let ptr = value.as_ptr() as *mut u8;
90        std::mem::forget(value); // Prevent Rust from freeing the memory
91        JSNIValue {
92            kind: JSNIKind::VecU8,
93            // high: len 32bit, low: ptr 64bit
94            value: (len as u64) << 32 | ptr as u64,
95        }
96    }
97}
98
99impl From<String> for JSNIValue {
100    fn from(value: String) -> Self {
101        let len = value.len();
102        let bytes = value.into_bytes();
103        let ptr = bytes.as_ptr() as *mut u8;
104        std::mem::forget(bytes); // Prevent Rust from freeing the memory
105        JSNIValue {
106            kind: JSNIKind::String,
107            value: (len as u64) << 32 | ptr as u64,
108        }
109    }
110}
111
112impl JSNIValue {
113    pub fn null() -> Self {
114        JSNIValue {
115            kind: JSNIKind::Null,
116            value: 0,
117        }
118    }
119
120    pub fn to_vec(&self) ->Vec<u8> {
121        if self.kind != JSNIKind::VecU8 {
122            panic!("JSNIValue is not a Vec<u8>");
123        }
124        let ptr = self.value & 0xFFFFFFFF;
125        unsafe { *Box::from_raw(ptr as *mut Vec<u8>) }
126    }
127
128    pub fn to_string(&self) -> String {
129        if self.kind != JSNIKind::String {
130            panic!("JSNIValue is not a String");
131        }
132        let ptr = self.value & 0xFFFFFFFF;
133        let vec = unsafe { *Box::from_raw(ptr as *mut Vec<u8>) };
134        String::from_utf8(vec).unwrap()
135    }
136}
137
138pub struct JavaScriptNativeInterface {
139}
140
141#[wasm_bindgen]
142extern "C" {
143    async fn jsni_call(js_func_name: *const u8, args: *const u8, args_count: usize) -> JsValue;
144}
145
146fn vec_onto_box<T>(vec: Vec<T>) -> *mut Vec<T> {
147    Box::into_raw(Box::new(vec))
148}
149
150/// Allocates a JSNIValue array in the heap and returns a fat pointer.
151#[wasm_bindgen]
152pub fn alloc_jsni_value(size: usize) -> u64 {
153    let mut vec = vec![JSNIValue::null(); size];
154    let ptr = vec.as_mut_ptr() as *mut u8;
155    let vec_ptr = vec_onto_box(vec);
156    (vec_ptr as u64) << 32 | ptr as u64
157}
158
159/// Deallocates a JSNIValue array allocated by `alloc_jsni_value.
160#[wasm_bindgen]
161pub fn alloc(size: usize) -> u64 {
162    let mut vec = vec![0u8; size];
163    let ptr = vec.as_mut_ptr() as *mut u8;
164    let vec_ptr = vec_onto_box(vec);
165    (vec_ptr as u64) << 32 | ptr as u64
166}
167
168impl JavaScriptNativeInterface {
169    pub fn new() -> Self {
170        JavaScriptNativeInterface {}
171    }
172
173    fn free_args(&self, args: Vec<JSNIValue>) {
174        for arg in args {
175            match arg.kind {
176                JSNIKind::VecU8 => {
177                    let len = (arg.value >> 32) as usize;
178                    let ptr = (arg.value & 0xFFFFFFFF) as *mut u8;
179                    unsafe { Vec::from_raw_parts(ptr, len, len) };
180                }
181                JSNIKind::String => {
182                    let len = (arg.value >> 32) as usize;
183                    let ptr = (arg.value & 0xFFFFFFFF) as *mut u8;
184                    unsafe { String::from_raw_parts(ptr, len, len) };
185                }
186                _ => {}
187            }
188        }
189    }
190
191    /// Calls the JavaScript function.
192    /// Must be set registers with uarguments to pass to the JavaScript function before calling this function.
193    /// Returns a vector of results.
194    /// The first register is the count of results, followed by the results themselves.
195    pub async fn call(&mut self, js_func_name: String, args: Vec<JSNIValue>) -> Vec<JSNIValue> {
196        let js_func_name = JSNIValue::from(js_func_name);
197        let js_func_name_ptr = &js_func_name as *const JSNIValue as *const u8;
198
199        let return_values_ptr_raw = jsni_call(js_func_name_ptr, args.as_ptr() as *mut u8, args.len()).await.as_f64().unwrap();
200        self.free_args(args);
201
202        if return_values_ptr_raw < 0.0 {
203            // none returned
204            return Vec::new();
205        }
206
207        let return_values = unsafe { Box::from_raw(return_values_ptr_raw as u64 as *mut Vec<JSNIValue>) };
208        *return_values
209    }
210}