@crossbind/core-embind-rust 2.0.0-beta.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/LICENSE +21 -0
- package/README.md +344 -0
- package/adapters/jsi.cpp +433 -0
- package/adapters/web.cpp +268 -0
- package/crate/Cargo.lock +7 -0
- package/crate/Cargo.toml +7 -0
- package/crate/src/lib.rs +1186 -0
- package/include/crossbind_embind.h +53 -0
- package/package.json +25 -0
package/crate/src/lib.rs
ADDED
|
@@ -0,0 +1,1186 @@
|
|
|
1
|
+
//! embind-rs: a Rust producer for the emscripten embind registration protocol (spike).
|
|
2
|
+
//! Consumers are untouched: libembind.js on web, jsi-embind on mobile read the same calls.
|
|
3
|
+
use std::any::TypeId;
|
|
4
|
+
use std::collections::HashMap;
|
|
5
|
+
use std::ffi::{c_char, c_void, CString};
|
|
6
|
+
use std::sync::{Mutex, OnceLock};
|
|
7
|
+
|
|
8
|
+
extern "C" {
|
|
9
|
+
fn crossbind_embind_register_class(
|
|
10
|
+
cls: *const c_void, ptr_ty: *const c_void, const_ptr_ty: *const c_void, base: *const c_void,
|
|
11
|
+
gat_sig: *const c_char, get_actual_type: usize,
|
|
12
|
+
upcast_sig: *const c_char, upcast: usize,
|
|
13
|
+
downcast_sig: *const c_char, downcast: usize,
|
|
14
|
+
name: *const c_char, dtor_sig: *const c_char, dtor: usize,
|
|
15
|
+
);
|
|
16
|
+
fn crossbind_embind_register_class_constructor(
|
|
17
|
+
cls: *const c_void, argc: u32, arg_types: *const *const c_void,
|
|
18
|
+
sig: *const c_char, invoker: usize, ctor: usize,
|
|
19
|
+
);
|
|
20
|
+
fn crossbind_embind_register_class_function(
|
|
21
|
+
cls: *const c_void, name: *const c_char, argc: u32, arg_types: *const *const c_void,
|
|
22
|
+
sig: *const c_char, invoker: usize, ctx: usize,
|
|
23
|
+
is_pure_virtual: u32, is_async: bool, is_nonnull_return: bool,
|
|
24
|
+
);
|
|
25
|
+
fn crossbind_embind_register_class_class_function(
|
|
26
|
+
cls: *const c_void, name: *const c_char, argc: u32, arg_types: *const *const c_void,
|
|
27
|
+
sig: *const c_char, invoker: usize, method: usize,
|
|
28
|
+
is_async: bool, is_nonnull_return: bool,
|
|
29
|
+
);
|
|
30
|
+
fn crossbind_embind_register_smart_ptr(
|
|
31
|
+
ptr_ty: *const c_void, pointee_ty: *const c_void, name: *const c_char, sharing_policy: i32,
|
|
32
|
+
gp_sig: *const c_char, get_pointee: usize,
|
|
33
|
+
ctor_sig: *const c_char, constructor: usize,
|
|
34
|
+
share_sig: *const c_char, share: usize,
|
|
35
|
+
dtor_sig: *const c_char, destructor: usize,
|
|
36
|
+
);
|
|
37
|
+
fn crossbind_embind_register_enum(enum_ty: *const c_void, name: *const c_char, size: usize, is_signed: bool, policy: i32);
|
|
38
|
+
fn crossbind_embind_register_enum_value(enum_ty: *const c_void, name: *const c_char, value: i32);
|
|
39
|
+
fn crossbind_embind_register_value_object(
|
|
40
|
+
struct_ty: *const c_void, name: *const c_char,
|
|
41
|
+
ctor_sig: *const c_char, ctor: usize, dtor_sig: *const c_char, dtor: usize,
|
|
42
|
+
);
|
|
43
|
+
fn crossbind_embind_register_value_object_field(
|
|
44
|
+
struct_ty: *const c_void, name: *const c_char,
|
|
45
|
+
getter_ret: *const c_void, getter_sig: *const c_char, getter: usize, getter_ctx: usize,
|
|
46
|
+
setter_arg: *const c_void, setter_sig: *const c_char, setter: usize, setter_ctx: usize,
|
|
47
|
+
);
|
|
48
|
+
fn crossbind_embind_finalize_value_object(struct_ty: *const c_void);
|
|
49
|
+
// Registers an init function with the host's binding-init list. The host runs it at the
|
|
50
|
+
// right time: immediately on web, after the JSI runtime is installed on mobile.
|
|
51
|
+
fn crossbind_embind_register_bindings(init: extern "C" fn());
|
|
52
|
+
fn crossbind_embind_register_function(
|
|
53
|
+
name: *const c_char, argc: u32, arg_types: *const *const c_void,
|
|
54
|
+
sig: *const c_char, invoker: usize, function: usize,
|
|
55
|
+
is_async: bool, is_nonnull_return: bool,
|
|
56
|
+
);
|
|
57
|
+
fn crossbind_tid_int() -> *const c_void;
|
|
58
|
+
fn crossbind_tid_double() -> *const c_void;
|
|
59
|
+
fn crossbind_tid_bool() -> *const c_void;
|
|
60
|
+
fn crossbind_tid_void() -> *const c_void;
|
|
61
|
+
fn crossbind_tid_std_string() -> *const c_void;
|
|
62
|
+
fn free(ptr: *mut u8);
|
|
63
|
+
fn crossbind_tid_int64() -> *const c_void;
|
|
64
|
+
fn crossbind_tid_uint64() -> *const c_void;
|
|
65
|
+
fn crossbind_embind_register_optional(optional_ty: *const c_void, inner_ty: *const c_void);
|
|
66
|
+
fn crossbind_tid_optional_int() -> *const c_void;
|
|
67
|
+
fn crossbind_tid_optional_double() -> *const c_void;
|
|
68
|
+
fn crossbind_tid_optional_bool() -> *const c_void;
|
|
69
|
+
fn crossbind_tid_optional_string() -> *const c_void;
|
|
70
|
+
fn malloc(size: usize) -> *mut u8;
|
|
71
|
+
// Adapter-owned error escape ([u32 len][bytes] wire): web throws a JS Error immediately,
|
|
72
|
+
// native stores the message and throws after the invoker returns.
|
|
73
|
+
fn crossbind_embind_raise_error(wire_msg: *mut u8);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Optional wire helpers exist only on wasm (native optionals are nullable heap cells the jsi
|
|
77
|
+
// adapter converts itself). The opt_* readers consume the incoming handle's reference.
|
|
78
|
+
#[cfg(target_family = "wasm")]
|
|
79
|
+
extern "C" {
|
|
80
|
+
fn crossbind_emval_take_value(inner_ty: *const c_void, argv: *const c_void) -> usize;
|
|
81
|
+
fn crossbind_emval_undefined() -> usize;
|
|
82
|
+
fn crossbind_emval_opt_i32(handle: usize, out: *mut i32) -> i32;
|
|
83
|
+
fn crossbind_emval_opt_f64(handle: usize, out: *mut f64) -> i32;
|
|
84
|
+
fn crossbind_emval_opt_bool(handle: usize, out: *mut u8) -> i32;
|
|
85
|
+
fn crossbind_emval_opt_string(handle: usize, out: *mut *mut u8) -> i32;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// embind holds pointers to the registration data (typeids, argType arrays, signature strings)
|
|
89
|
+
// for the lifetime of the program, so they must be 'static. Instead of scattering `Box::leak`,
|
|
90
|
+
// one arena owns them: the containers move on growth but the heap buffers they point at stay
|
|
91
|
+
// put, so the pointers handed out remain valid for as long as the arena (i.e. the process).
|
|
92
|
+
struct Registry {
|
|
93
|
+
cstrings: Vec<CString>,
|
|
94
|
+
tids: Vec<Box<u8>>,
|
|
95
|
+
argtypes: Vec<Box<[*const c_void]>>,
|
|
96
|
+
class_tid: HashMap<TypeId, *const c_void>,
|
|
97
|
+
shared_ptr_tid: HashMap<TypeId, *const c_void>,
|
|
98
|
+
}
|
|
99
|
+
// Raw pointers are not Send/Sync; the arena is only ever touched under its Mutex.
|
|
100
|
+
unsafe impl Send for Registry {}
|
|
101
|
+
|
|
102
|
+
fn registry() -> &'static Mutex<Registry> {
|
|
103
|
+
static REG: OnceLock<Mutex<Registry>> = OnceLock::new();
|
|
104
|
+
REG.get_or_init(|| Mutex::new(Registry {
|
|
105
|
+
cstrings: Vec::new(), tids: Vec::new(), argtypes: Vec::new(), class_tid: HashMap::new(),
|
|
106
|
+
shared_ptr_tid: HashMap::new(),
|
|
107
|
+
}))
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
impl Registry {
|
|
111
|
+
fn cstr(&mut self, s: &str) -> *const c_char {
|
|
112
|
+
let c = CString::new(s).expect("no interior NUL in binding name");
|
|
113
|
+
let ptr = c.as_ptr();
|
|
114
|
+
self.cstrings.push(c);
|
|
115
|
+
ptr
|
|
116
|
+
}
|
|
117
|
+
fn tid(&mut self) -> *const c_void {
|
|
118
|
+
let b = Box::new(0u8);
|
|
119
|
+
let ptr = &*b as *const u8 as *const c_void;
|
|
120
|
+
self.tids.push(b);
|
|
121
|
+
ptr
|
|
122
|
+
}
|
|
123
|
+
fn argtypes(&mut self, v: Vec<*const c_void>) -> *const *const c_void {
|
|
124
|
+
let boxed = v.into_boxed_slice();
|
|
125
|
+
let ptr = boxed.as_ptr();
|
|
126
|
+
self.argtypes.push(boxed);
|
|
127
|
+
ptr
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
#[doc(hidden)]
|
|
132
|
+
pub fn __register_bindings(init: extern "C" fn()) {
|
|
133
|
+
unsafe { crossbind_embind_register_bindings(init); }
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/// Registers the bindings with the host - the Rust equivalent of C++'s `EMSCRIPTEN_BINDINGS`.
|
|
137
|
+
/// A platform init-array constructor hands the binding function to the host's init list, which
|
|
138
|
+
/// runs it at the right time (immediately on web; after the JSI runtime is installed on mobile).
|
|
139
|
+
/// No C++ trigger is needed. The consumer staticlib must be whole-archived so the constructor is
|
|
140
|
+
/// pulled into the link.
|
|
141
|
+
#[macro_export]
|
|
142
|
+
macro_rules! bindings {
|
|
143
|
+
($($body:tt)*) => {
|
|
144
|
+
const _: () = {
|
|
145
|
+
extern "C" fn __crossbind_embind_init() {
|
|
146
|
+
// Run at most once: the init-array constructor can be pulled in more than once
|
|
147
|
+
// (force_load + a normal link of the same archive), which would otherwise register
|
|
148
|
+
// every public name twice and abort in embind.
|
|
149
|
+
use ::core::sync::atomic::{AtomicBool, Ordering};
|
|
150
|
+
static DONE: AtomicBool = AtomicBool::new(false);
|
|
151
|
+
if DONE.swap(true, Ordering::SeqCst) { return; }
|
|
152
|
+
$($body)*
|
|
153
|
+
}
|
|
154
|
+
extern "C" fn __crossbind_embind_ctor() {
|
|
155
|
+
$crate::__register_bindings(__crossbind_embind_init);
|
|
156
|
+
}
|
|
157
|
+
#[used]
|
|
158
|
+
#[cfg_attr(target_vendor = "apple", link_section = "__DATA,__mod_init_func")]
|
|
159
|
+
#[cfg_attr(not(target_vendor = "apple"), link_section = ".init_array")]
|
|
160
|
+
static __CROSSBIND_EMBIND_CTOR: extern "C" fn() = __crossbind_embind_ctor;
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/// Types that cross the bridge: wire representation + signature letter + embind typeid.
|
|
166
|
+
pub trait WireType: 'static {
|
|
167
|
+
type Wire;
|
|
168
|
+
const SIG: char;
|
|
169
|
+
fn tid() -> *const c_void;
|
|
170
|
+
fn from_wire(w: Self::Wire) -> Self;
|
|
171
|
+
fn to_wire(self) -> Self::Wire;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
impl WireType for i32 {
|
|
175
|
+
type Wire = i32;
|
|
176
|
+
const SIG: char = 'i';
|
|
177
|
+
fn tid() -> *const c_void { unsafe { crossbind_tid_int() } }
|
|
178
|
+
fn from_wire(w: i32) -> i32 { w }
|
|
179
|
+
fn to_wire(self) -> i32 { self }
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// On wasm the invoker is dyncalled by embind-js, so its C signature must carry a real f64.
|
|
183
|
+
#[cfg(target_family = "wasm")]
|
|
184
|
+
impl WireType for f64 {
|
|
185
|
+
type Wire = f64;
|
|
186
|
+
const SIG: char = 'd';
|
|
187
|
+
fn tid() -> *const c_void { unsafe { crossbind_tid_double() } }
|
|
188
|
+
fn from_wire(w: f64) -> f64 { w }
|
|
189
|
+
fn to_wire(self) -> f64 { self }
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// On native the invoker is called only by the host adapter, whose bounded dispatch is
|
|
193
|
+
// integer-slot based: a real f64 parameter would land in FP registers and read garbage.
|
|
194
|
+
// Carry the double as its bit pattern in a u64 instead - the adapter converts number<->bits,
|
|
195
|
+
// so the invoker's C signature stays fully integer-class and every ABI dispatches correctly.
|
|
196
|
+
#[cfg(not(target_family = "wasm"))]
|
|
197
|
+
impl WireType for f64 {
|
|
198
|
+
type Wire = u64;
|
|
199
|
+
const SIG: char = 'd';
|
|
200
|
+
fn tid() -> *const c_void { unsafe { crossbind_tid_double() } }
|
|
201
|
+
fn from_wire(w: u64) -> f64 { f64::from_bits(w) }
|
|
202
|
+
fn to_wire(self) -> u64 { self.to_bits() }
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// i64/u64 cross as JS BigInt: 'j'/'u' slots are 64-bit integer-class in the invoker's C
|
|
206
|
+
// signature, so the native bounded dispatch stays valid; on wasm the dynCall char is 'j'
|
|
207
|
+
// for both (the web adapter maps 'u' -> 'j', signedness lives in the registered typeid).
|
|
208
|
+
impl WireType for i64 {
|
|
209
|
+
type Wire = i64;
|
|
210
|
+
const SIG: char = 'j';
|
|
211
|
+
fn tid() -> *const c_void { unsafe { crossbind_tid_int64() } }
|
|
212
|
+
fn from_wire(w: i64) -> i64 { w }
|
|
213
|
+
fn to_wire(self) -> i64 { self }
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
impl WireType for u64 {
|
|
217
|
+
type Wire = u64;
|
|
218
|
+
const SIG: char = 'u';
|
|
219
|
+
fn tid() -> *const c_void { unsafe { crossbind_tid_uint64() } }
|
|
220
|
+
fn from_wire(w: u64) -> u64 { w }
|
|
221
|
+
fn to_wire(self) -> u64 { self }
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// bool crosses as an int-sized wire (0/1), the way emscripten's ABI passes it.
|
|
225
|
+
impl WireType for bool {
|
|
226
|
+
type Wire = i32;
|
|
227
|
+
const SIG: char = 'i';
|
|
228
|
+
fn tid() -> *const c_void { unsafe { crossbind_tid_bool() } }
|
|
229
|
+
fn from_wire(w: i32) -> bool { w != 0 }
|
|
230
|
+
fn to_wire(self) -> i32 { self as i32 }
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// The void return type: methods that mutate without producing a value.
|
|
234
|
+
impl WireType for () {
|
|
235
|
+
type Wire = ();
|
|
236
|
+
const SIG: char = 'v';
|
|
237
|
+
fn tid() -> *const c_void { unsafe { crossbind_tid_void() } }
|
|
238
|
+
fn from_wire(_: ()) {}
|
|
239
|
+
fn to_wire(self) {}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/// std::string wire: [u32 len][bytes] (malloc'd). The consumer frees on return, and also
|
|
243
|
+
/// frees an incoming argument after the call - Rust only COPIES, never owns the wire buffer.
|
|
244
|
+
impl WireType for String {
|
|
245
|
+
type Wire = *mut u8;
|
|
246
|
+
// 's' (not 'p'): embind-jsi passes std::string as a jsi::String, not a raw pointer, so the host
|
|
247
|
+
// adapter must marshal it - the Rust side still sees the `[u32 len][bytes]` wire below.
|
|
248
|
+
const SIG: char = 's';
|
|
249
|
+
fn tid() -> *const c_void { unsafe { crossbind_tid_std_string() } }
|
|
250
|
+
fn from_wire(w: *mut u8) -> String {
|
|
251
|
+
unsafe {
|
|
252
|
+
let len = *(w as *const u32) as usize;
|
|
253
|
+
let bytes = std::slice::from_raw_parts(w.add(4), len);
|
|
254
|
+
String::from_utf8_lossy(bytes).into_owned()
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
fn to_wire(self) -> *mut u8 {
|
|
258
|
+
unsafe {
|
|
259
|
+
let bytes = self.as_bytes();
|
|
260
|
+
let base = malloc(4 + bytes.len());
|
|
261
|
+
*(base as *mut u32) = bytes.len() as u32;
|
|
262
|
+
std::ptr::copy_nonoverlapping(bytes.as_ptr(), base.add(4), bytes.len());
|
|
263
|
+
base
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/// Option<T> both ways: on wasm the wire is an EM_VAL handle (None = undefined; incoming
|
|
269
|
+
/// undefined/null both read as None); on native it is a nullable heap cell (0 = None) the jsi
|
|
270
|
+
/// adapter converts, tagged 'I'/'D'/'B'/'S'. Incoming cells stay adapter-owned (read, no free).
|
|
271
|
+
macro_rules! option_wire {
|
|
272
|
+
($( $inner:ty, $opt_tid:ident, $native_sig:literal ; )*) => {
|
|
273
|
+
$(
|
|
274
|
+
#[cfg(target_family = "wasm")]
|
|
275
|
+
impl WireType for Option<$inner> {
|
|
276
|
+
type Wire = usize;
|
|
277
|
+
const SIG: char = 'i';
|
|
278
|
+
fn tid() -> *const c_void { unsafe { $opt_tid() } }
|
|
279
|
+
fn from_wire(w: usize) -> Self { <$inner as EmvalOptRead>::emval_opt_read(w) }
|
|
280
|
+
fn to_wire(self) -> usize {
|
|
281
|
+
match self {
|
|
282
|
+
None => unsafe { crossbind_emval_undefined() },
|
|
283
|
+
Some(v) => v.emval_take(),
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
#[cfg(not(target_family = "wasm"))]
|
|
288
|
+
impl WireType for Option<$inner> {
|
|
289
|
+
type Wire = *mut u8;
|
|
290
|
+
const SIG: char = $native_sig;
|
|
291
|
+
fn tid() -> *const c_void { unsafe { $opt_tid() } }
|
|
292
|
+
fn from_wire(w: *mut u8) -> Self {
|
|
293
|
+
if w.is_null() { None } else { Some(<$inner as OptionCellRead>::option_cell_read(w)) }
|
|
294
|
+
}
|
|
295
|
+
fn to_wire(self) -> *mut u8 {
|
|
296
|
+
match self {
|
|
297
|
+
None => std::ptr::null_mut(),
|
|
298
|
+
Some(v) => v.option_cell(),
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
)*
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
option_wire! {
|
|
306
|
+
i32, crossbind_tid_optional_int, 'I';
|
|
307
|
+
f64, crossbind_tid_optional_double, 'D';
|
|
308
|
+
bool, crossbind_tid_optional_bool, 'B';
|
|
309
|
+
String, crossbind_tid_optional_string, 'S';
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// wasm Some-path: hand the inner's in-memory repr to _emval_take_value. JS copies the value
|
|
313
|
+
// synchronously, so stack temporaries suffice; the string wire is freed by embind itself.
|
|
314
|
+
#[cfg(target_family = "wasm")]
|
|
315
|
+
trait EmvalTake { fn emval_take(self) -> usize; }
|
|
316
|
+
#[cfg(target_family = "wasm")]
|
|
317
|
+
impl EmvalTake for i32 {
|
|
318
|
+
fn emval_take(self) -> usize { unsafe { crossbind_emval_take_value(crossbind_tid_int(), &self as *const i32 as *const c_void) } }
|
|
319
|
+
}
|
|
320
|
+
#[cfg(target_family = "wasm")]
|
|
321
|
+
impl EmvalTake for f64 {
|
|
322
|
+
fn emval_take(self) -> usize { unsafe { crossbind_emval_take_value(crossbind_tid_double(), &self as *const f64 as *const c_void) } }
|
|
323
|
+
}
|
|
324
|
+
#[cfg(target_family = "wasm")]
|
|
325
|
+
impl EmvalTake for bool {
|
|
326
|
+
fn emval_take(self) -> usize {
|
|
327
|
+
let b: u8 = self as u8;
|
|
328
|
+
unsafe { crossbind_emval_take_value(crossbind_tid_bool(), &b as *const u8 as *const c_void) }
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
#[cfg(target_family = "wasm")]
|
|
332
|
+
impl EmvalTake for String {
|
|
333
|
+
fn emval_take(self) -> usize {
|
|
334
|
+
let w = self.to_wire();
|
|
335
|
+
unsafe { crossbind_emval_take_value(crossbind_tid_std_string(), &w as *const *mut u8 as *const c_void) }
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// wasm incoming: the opt_* reader consumes the handle and yields the native repr (1) or
|
|
340
|
+
// nothing (0, for undefined/null). The string reader hands over a producer-owned wire buffer.
|
|
341
|
+
#[cfg(target_family = "wasm")]
|
|
342
|
+
trait EmvalOptRead: Sized { fn emval_opt_read(handle: usize) -> Option<Self>; }
|
|
343
|
+
#[cfg(target_family = "wasm")]
|
|
344
|
+
impl EmvalOptRead for i32 {
|
|
345
|
+
fn emval_opt_read(h: usize) -> Option<i32> {
|
|
346
|
+
let mut v: i32 = 0;
|
|
347
|
+
if unsafe { crossbind_emval_opt_i32(h, &mut v) } != 0 { Some(v) } else { None }
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
#[cfg(target_family = "wasm")]
|
|
351
|
+
impl EmvalOptRead for f64 {
|
|
352
|
+
fn emval_opt_read(h: usize) -> Option<f64> {
|
|
353
|
+
let mut v: f64 = 0.0;
|
|
354
|
+
if unsafe { crossbind_emval_opt_f64(h, &mut v) } != 0 { Some(v) } else { None }
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
#[cfg(target_family = "wasm")]
|
|
358
|
+
impl EmvalOptRead for bool {
|
|
359
|
+
fn emval_opt_read(h: usize) -> Option<bool> {
|
|
360
|
+
let mut v: u8 = 0;
|
|
361
|
+
if unsafe { crossbind_emval_opt_bool(h, &mut v) } != 0 { Some(v != 0) } else { None }
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
#[cfg(target_family = "wasm")]
|
|
365
|
+
impl EmvalOptRead for String {
|
|
366
|
+
fn emval_opt_read(h: usize) -> Option<String> {
|
|
367
|
+
let mut w: *mut u8 = std::ptr::null_mut();
|
|
368
|
+
if unsafe { crossbind_emval_opt_string(h, &mut w) } == 0 { return None; }
|
|
369
|
+
let s = String::from_wire(w);
|
|
370
|
+
unsafe { free(w); }
|
|
371
|
+
Some(s)
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Native incoming: read the adapter-owned cell (the adapter frees it after the call).
|
|
376
|
+
#[cfg(not(target_family = "wasm"))]
|
|
377
|
+
trait OptionCellRead: Sized { fn option_cell_read(cell: *mut u8) -> Self; }
|
|
378
|
+
#[cfg(not(target_family = "wasm"))]
|
|
379
|
+
impl OptionCellRead for i32 {
|
|
380
|
+
fn option_cell_read(cell: *mut u8) -> i32 { unsafe { *(cell as *const i32) } }
|
|
381
|
+
}
|
|
382
|
+
#[cfg(not(target_family = "wasm"))]
|
|
383
|
+
impl OptionCellRead for f64 {
|
|
384
|
+
fn option_cell_read(cell: *mut u8) -> f64 { unsafe { *(cell as *const f64) } }
|
|
385
|
+
}
|
|
386
|
+
#[cfg(not(target_family = "wasm"))]
|
|
387
|
+
impl OptionCellRead for bool {
|
|
388
|
+
fn option_cell_read(cell: *mut u8) -> bool { unsafe { *cell != 0 } }
|
|
389
|
+
}
|
|
390
|
+
#[cfg(not(target_family = "wasm"))]
|
|
391
|
+
impl OptionCellRead for String {
|
|
392
|
+
fn option_cell_read(cell: *mut u8) -> String { String::from_wire(cell) }
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Native Some-path: one malloc'd cell per kind; the jsi adapter reads and frees it.
|
|
396
|
+
#[cfg(not(target_family = "wasm"))]
|
|
397
|
+
trait OptionCell { fn option_cell(self) -> *mut u8; }
|
|
398
|
+
#[cfg(not(target_family = "wasm"))]
|
|
399
|
+
impl OptionCell for i32 {
|
|
400
|
+
fn option_cell(self) -> *mut u8 { unsafe { let p = malloc(4); *(p as *mut i32) = self; p } }
|
|
401
|
+
}
|
|
402
|
+
#[cfg(not(target_family = "wasm"))]
|
|
403
|
+
impl OptionCell for f64 {
|
|
404
|
+
fn option_cell(self) -> *mut u8 { unsafe { let p = malloc(8); *(p as *mut f64) = self; p } }
|
|
405
|
+
}
|
|
406
|
+
#[cfg(not(target_family = "wasm"))]
|
|
407
|
+
impl OptionCell for bool {
|
|
408
|
+
fn option_cell(self) -> *mut u8 { unsafe { let p = malloc(1); *p = self as u8; p } }
|
|
409
|
+
}
|
|
410
|
+
#[cfg(not(target_family = "wasm"))]
|
|
411
|
+
impl OptionCell for String {
|
|
412
|
+
fn option_cell(self) -> *mut u8 { self.to_wire() }
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/// Registers std::optional<T> for the inners a bridge actually returns (adapter-idempotent).
|
|
416
|
+
pub fn register_optional_i32() { unsafe { crossbind_embind_register_optional(crossbind_tid_optional_int(), crossbind_tid_int()); } }
|
|
417
|
+
pub fn register_optional_f64() { unsafe { crossbind_embind_register_optional(crossbind_tid_optional_double(), crossbind_tid_double()); } }
|
|
418
|
+
pub fn register_optional_bool() { unsafe { crossbind_embind_register_optional(crossbind_tid_optional_bool(), crossbind_tid_bool()); } }
|
|
419
|
+
pub fn register_optional_string() { unsafe { crossbind_embind_register_optional(crossbind_tid_optional_string(), crossbind_tid_std_string()); } }
|
|
420
|
+
|
|
421
|
+
/// Post-raise sentinel returns: only native ever observes them (the adapter throws right after
|
|
422
|
+
/// the invoker returns); on web `crossbind_embind_raise_error` throws into JS and never comes back.
|
|
423
|
+
pub trait ErrSentinel {
|
|
424
|
+
fn err_sentinel() -> Self;
|
|
425
|
+
}
|
|
426
|
+
impl ErrSentinel for i32 { fn err_sentinel() -> Self { 0 } }
|
|
427
|
+
impl ErrSentinel for i64 { fn err_sentinel() -> Self { 0 } }
|
|
428
|
+
impl ErrSentinel for u64 { fn err_sentinel() -> Self { 0 } }
|
|
429
|
+
impl ErrSentinel for f64 { fn err_sentinel() -> Self { 0.0 } }
|
|
430
|
+
impl ErrSentinel for bool { fn err_sentinel() -> Self { false } }
|
|
431
|
+
impl ErrSentinel for () { fn err_sentinel() -> Self {} }
|
|
432
|
+
impl ErrSentinel for String { fn err_sentinel() -> Self { String::new() } }
|
|
433
|
+
impl<T> ErrSentinel for *mut T { fn err_sentinel() -> Self { std::ptr::null_mut() } }
|
|
434
|
+
|
|
435
|
+
/// Routes a shim's `Err` to JS as an exception, then returns a dead sentinel value.
|
|
436
|
+
pub fn raise_err<W: ErrSentinel>(msg: String) -> W {
|
|
437
|
+
unsafe { crossbind_embind_raise_error(msg.to_wire()); }
|
|
438
|
+
W::err_sentinel()
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
extern "C" fn dtor_thunk<T>(p: *mut T) {
|
|
442
|
+
if !p.is_null() {
|
|
443
|
+
drop(unsafe { Box::from_raw(p) });
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
extern "C" fn get_actual_type_thunk<T: 'static>(_p: *const c_void) -> *const c_void {
|
|
448
|
+
registry().lock().unwrap().class_tid[&TypeId::of::<T>()]
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/// The registered typeid of an enum, for a user `WireType` impl's `tid()`. The enum must be
|
|
452
|
+
/// registered with `enum_::<E>(..)` before any binding that uses it.
|
|
453
|
+
pub fn enum_tid<E: 'static>() -> *const c_void {
|
|
454
|
+
registry().lock().unwrap().class_tid[&TypeId::of::<E>()]
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/// The registered typeid of a class, for reference-parameter wrappers (`&OtherClass` params).
|
|
458
|
+
/// The class must be registered before any binding that takes it - declare the struct earlier.
|
|
459
|
+
pub fn class_tid<T: 'static>() -> *const c_void {
|
|
460
|
+
registry().lock().unwrap().class_tid.get(&TypeId::of::<T>()).copied()
|
|
461
|
+
.expect("class parameter type must be registered before the binding that uses it")
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/// The registered smart-pointer typeid of a SHARED class (`.smart_ptr_shared(..)`), for the
|
|
465
|
+
/// generated `Arc<T>` return/parameter wrappers.
|
|
466
|
+
pub fn shared_tid<T: 'static>() -> *const c_void {
|
|
467
|
+
registry().lock().unwrap().shared_ptr_tid.get(&TypeId::of::<T>()).copied()
|
|
468
|
+
.expect("Arc<T> bindings need the class registered with .smart_ptr_shared(..) first")
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// Arc-as-intrusive smart pointer: the wire IS the Arc::into_raw pointer, every JS handle owns
|
|
472
|
+
// one strong count. share bumps the count (embind's INTRUSIVE path), the destructor drops one.
|
|
473
|
+
extern "C" fn arc_share_thunk<T>(p: *mut T) -> *mut T {
|
|
474
|
+
if !p.is_null() {
|
|
475
|
+
unsafe { std::sync::Arc::increment_strong_count(p as *const T) };
|
|
476
|
+
}
|
|
477
|
+
p
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
extern "C" fn arc_dtor_thunk<T>(p: *mut T) {
|
|
481
|
+
if !p.is_null() {
|
|
482
|
+
drop(unsafe { std::sync::Arc::from_raw(p as *const T) });
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// ---- live JS values (JsValue / JsFunction) ----
|
|
487
|
+
// An owned emval handle: the host keeps the real value in its handle table and this side only
|
|
488
|
+
// refcounts. Params arrive owned (Drop releases), returns hand the count to JS. Handles are
|
|
489
|
+
// thread-affine: use them only on the JS thread that produced them.
|
|
490
|
+
extern "C" {
|
|
491
|
+
fn crossbind_tid_emval() -> *const c_void;
|
|
492
|
+
fn crossbind_v_ref(h: usize);
|
|
493
|
+
fn crossbind_v_unref(h: usize);
|
|
494
|
+
fn crossbind_v_undef() -> usize;
|
|
495
|
+
fn crossbind_v_from_f64(v: f64) -> usize;
|
|
496
|
+
fn crossbind_v_from_bool(v: i32) -> usize;
|
|
497
|
+
fn crossbind_v_from_str(w: *mut u8) -> usize;
|
|
498
|
+
fn crossbind_v_get_prop(h: usize, key: *mut u8) -> usize;
|
|
499
|
+
fn crossbind_v_set_prop(h: usize, key: *mut u8, v: usize);
|
|
500
|
+
fn crossbind_v_kind(h: usize) -> i32;
|
|
501
|
+
fn crossbind_v_as_f64(h: usize) -> f64;
|
|
502
|
+
fn crossbind_v_as_bool(h: usize) -> i32;
|
|
503
|
+
fn crossbind_v_as_str(h: usize) -> *mut u8;
|
|
504
|
+
fn crossbind_v_call(f: usize, argc: u32, argv: *const usize) -> usize;
|
|
505
|
+
fn crossbind_v_cb_err_take() -> *mut u8;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
fn str_wire(s: &str) -> *mut u8 {
|
|
509
|
+
unsafe {
|
|
510
|
+
let bytes = s.as_bytes();
|
|
511
|
+
let base = malloc(4 + bytes.len());
|
|
512
|
+
*(base as *mut u32) = bytes.len() as u32;
|
|
513
|
+
std::ptr::copy_nonoverlapping(bytes.as_ptr(), base.add(4), bytes.len());
|
|
514
|
+
base
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
fn wire_string(w: *mut u8) -> String {
|
|
519
|
+
unsafe {
|
|
520
|
+
let len = *(w as *const u32) as usize;
|
|
521
|
+
let bytes = std::slice::from_raw_parts(w.add(4), len);
|
|
522
|
+
let s = String::from_utf8_lossy(bytes).into_owned();
|
|
523
|
+
free(w);
|
|
524
|
+
s
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
pub struct JsValue {
|
|
529
|
+
handle: usize,
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
impl JsValue {
|
|
533
|
+
fn own(handle: usize) -> Self {
|
|
534
|
+
JsValue { handle }
|
|
535
|
+
}
|
|
536
|
+
fn release(self) -> usize {
|
|
537
|
+
let h = self.handle;
|
|
538
|
+
core::mem::forget(self);
|
|
539
|
+
h
|
|
540
|
+
}
|
|
541
|
+
pub fn undefined() -> Self {
|
|
542
|
+
JsValue::own(unsafe { crossbind_v_undef() })
|
|
543
|
+
}
|
|
544
|
+
pub fn from_f64(v: f64) -> Self {
|
|
545
|
+
JsValue::own(unsafe { crossbind_v_from_f64(v) })
|
|
546
|
+
}
|
|
547
|
+
pub fn from_bool(v: bool) -> Self {
|
|
548
|
+
JsValue::own(unsafe { crossbind_v_from_bool(v as i32) })
|
|
549
|
+
}
|
|
550
|
+
pub fn from_str(v: &str) -> Self {
|
|
551
|
+
JsValue::own(unsafe { crossbind_v_from_str(str_wire(v)) })
|
|
552
|
+
}
|
|
553
|
+
pub fn get(&self, key: &str) -> JsValue {
|
|
554
|
+
JsValue::own(unsafe { crossbind_v_get_prop(self.handle, str_wire(key)) })
|
|
555
|
+
}
|
|
556
|
+
pub fn set(&self, key: &str, value: &JsValue) {
|
|
557
|
+
unsafe { crossbind_v_set_prop(self.handle, str_wire(key), value.handle) };
|
|
558
|
+
}
|
|
559
|
+
// 0 nullish, 1 bool, 2 number, 3 string, 4 function, 5 anything else.
|
|
560
|
+
fn kind(&self) -> i32 {
|
|
561
|
+
unsafe { crossbind_v_kind(self.handle) }
|
|
562
|
+
}
|
|
563
|
+
pub fn is_nullish(&self) -> bool {
|
|
564
|
+
self.kind() == 0
|
|
565
|
+
}
|
|
566
|
+
pub fn as_f64(&self) -> Option<f64> {
|
|
567
|
+
if self.kind() == 2 { Some(unsafe { crossbind_v_as_f64(self.handle) }) } else { None }
|
|
568
|
+
}
|
|
569
|
+
pub fn as_bool(&self) -> Option<bool> {
|
|
570
|
+
if self.kind() == 1 { Some(unsafe { crossbind_v_as_bool(self.handle) } != 0) } else { None }
|
|
571
|
+
}
|
|
572
|
+
pub fn as_string(&self) -> Option<String> {
|
|
573
|
+
if self.kind() == 3 { Some(wire_string(unsafe { crossbind_v_as_str(self.handle) })) } else { None }
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
impl Clone for JsValue {
|
|
578
|
+
fn clone(&self) -> Self {
|
|
579
|
+
unsafe { crossbind_v_ref(self.handle) };
|
|
580
|
+
JsValue { handle: self.handle }
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
impl Drop for JsValue {
|
|
585
|
+
fn drop(&mut self) {
|
|
586
|
+
unsafe { crossbind_v_unref(self.handle) };
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/// A callable JS value; a JS throw inside the callback surfaces as `Err(message)`.
|
|
591
|
+
pub struct JsFunction {
|
|
592
|
+
value: JsValue,
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
impl JsFunction {
|
|
596
|
+
fn call_n(&self, args: &[usize]) -> Result<JsValue, String> {
|
|
597
|
+
let h = unsafe { crossbind_v_call(self.value.handle, args.len() as u32, args.as_ptr()) };
|
|
598
|
+
if h == 0 {
|
|
599
|
+
let w = unsafe { crossbind_v_cb_err_take() };
|
|
600
|
+
return Err(if w.is_null() { String::from("callback failed") } else { wire_string(w) });
|
|
601
|
+
}
|
|
602
|
+
Ok(JsValue::own(h))
|
|
603
|
+
}
|
|
604
|
+
pub fn call0(&self) -> Result<JsValue, String> {
|
|
605
|
+
self.call_n(&[])
|
|
606
|
+
}
|
|
607
|
+
pub fn call1(&self, a0: &JsValue) -> Result<JsValue, String> {
|
|
608
|
+
self.call_n(&[a0.handle])
|
|
609
|
+
}
|
|
610
|
+
pub fn call2(&self, a0: &JsValue, a1: &JsValue) -> Result<JsValue, String> {
|
|
611
|
+
self.call_n(&[a0.handle, a1.handle])
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// The emval handle slot: i32 table index on wasm dynCall, BigInt-marshalled pointer slot on
|
|
616
|
+
// the native bounded dispatch (same split as the generated JSON wire).
|
|
617
|
+
impl WireType for JsValue {
|
|
618
|
+
type Wire = usize;
|
|
619
|
+
#[cfg(target_family = "wasm")]
|
|
620
|
+
const SIG: char = 'i';
|
|
621
|
+
#[cfg(not(target_family = "wasm"))]
|
|
622
|
+
const SIG: char = 'p';
|
|
623
|
+
fn tid() -> *const c_void {
|
|
624
|
+
unsafe { crossbind_tid_emval() }
|
|
625
|
+
}
|
|
626
|
+
fn from_wire(w: usize) -> Self {
|
|
627
|
+
JsValue::own(w)
|
|
628
|
+
}
|
|
629
|
+
fn to_wire(self) -> usize {
|
|
630
|
+
self.release()
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
impl WireType for JsFunction {
|
|
635
|
+
type Wire = usize;
|
|
636
|
+
#[cfg(target_family = "wasm")]
|
|
637
|
+
const SIG: char = 'i';
|
|
638
|
+
#[cfg(not(target_family = "wasm"))]
|
|
639
|
+
const SIG: char = 'p';
|
|
640
|
+
fn tid() -> *const c_void {
|
|
641
|
+
unsafe { crossbind_tid_emval() }
|
|
642
|
+
}
|
|
643
|
+
fn from_wire(w: usize) -> Self {
|
|
644
|
+
JsFunction { value: JsValue::own(w) }
|
|
645
|
+
}
|
|
646
|
+
fn to_wire(self) -> usize {
|
|
647
|
+
self.value.release()
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
impl ErrSentinel for JsValue {
|
|
652
|
+
fn err_sentinel() -> Self {
|
|
653
|
+
JsValue::undefined()
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// Host test builds (plain `cargo test` on a package that depends on embind-rs) have no adapter
|
|
658
|
+
// to satisfy the emval hooks; this dev-only feature links panicking stand-ins instead. Never
|
|
659
|
+
// enable it for a real bridge build - the adapter provides the genuine symbols there.
|
|
660
|
+
#[cfg(all(feature = "host-stubs", not(target_family = "wasm")))]
|
|
661
|
+
mod host_stubs {
|
|
662
|
+
#[no_mangle] extern "C" fn crossbind_v_ref(_h: usize) {}
|
|
663
|
+
#[no_mangle] extern "C" fn crossbind_v_unref(_h: usize) {}
|
|
664
|
+
#[no_mangle] extern "C" fn crossbind_v_undef() -> usize { panic!("emval hooks need a crossbind host") }
|
|
665
|
+
#[no_mangle] extern "C" fn crossbind_v_from_f64(_v: f64) -> usize { panic!("emval hooks need a crossbind host") }
|
|
666
|
+
#[no_mangle] extern "C" fn crossbind_v_from_bool(_v: i32) -> usize { panic!("emval hooks need a crossbind host") }
|
|
667
|
+
#[no_mangle] extern "C" fn crossbind_v_from_str(_w: *mut u8) -> usize { panic!("emval hooks need a crossbind host") }
|
|
668
|
+
#[no_mangle] extern "C" fn crossbind_v_get_prop(_h: usize, _k: *mut u8) -> usize { panic!("emval hooks need a crossbind host") }
|
|
669
|
+
#[no_mangle] extern "C" fn crossbind_v_set_prop(_h: usize, _k: *mut u8, _v: usize) {}
|
|
670
|
+
#[no_mangle] extern "C" fn crossbind_v_kind(_h: usize) -> i32 { 0 }
|
|
671
|
+
#[no_mangle] extern "C" fn crossbind_v_as_f64(_h: usize) -> f64 { 0.0 }
|
|
672
|
+
#[no_mangle] extern "C" fn crossbind_v_as_bool(_h: usize) -> i32 { 0 }
|
|
673
|
+
#[no_mangle] extern "C" fn crossbind_v_as_str(_h: usize) -> *mut u8 { core::ptr::null_mut() }
|
|
674
|
+
#[no_mangle] extern "C" fn crossbind_v_call(_f: usize, _c: u32, _a: *const usize) -> usize { 0 }
|
|
675
|
+
#[no_mangle] extern "C" fn crossbind_v_cb_err_take() -> *mut u8 { core::ptr::null_mut() }
|
|
676
|
+
#[no_mangle] extern "C" fn crossbind_tid_emval() -> *const core::ffi::c_void { core::ptr::null() }
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
pub struct EnumBuilder<E: 'static> {
|
|
680
|
+
ty: *const c_void,
|
|
681
|
+
_e: std::marker::PhantomData<E>,
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/// Registers a C-like enum (i32 discriminants). Follow with `.value(name, discriminant)`.
|
|
685
|
+
pub fn enum_<E: 'static>(name: &str) -> EnumBuilder<E> {
|
|
686
|
+
let (ty, name_ptr);
|
|
687
|
+
{
|
|
688
|
+
let mut r = registry().lock().unwrap();
|
|
689
|
+
ty = r.tid();
|
|
690
|
+
name_ptr = r.cstr(name);
|
|
691
|
+
r.class_tid.insert(TypeId::of::<E>(), ty);
|
|
692
|
+
}
|
|
693
|
+
unsafe { crossbind_embind_register_enum(ty, name_ptr, 4, true, 0); }
|
|
694
|
+
EnumBuilder { ty, _e: std::marker::PhantomData }
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
impl<E: 'static> EnumBuilder<E> {
|
|
698
|
+
pub fn value(self, name: &str, discriminant: i32) -> Self {
|
|
699
|
+
let name_ptr = registry().lock().unwrap().cstr(name);
|
|
700
|
+
unsafe { crossbind_embind_register_enum_value(self.ty, name_ptr, discriminant); }
|
|
701
|
+
self
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/// The registered typeid of a value object, for a user `WireType` impl's `tid()`.
|
|
706
|
+
pub fn value_object_tid<T: 'static>() -> *const c_void {
|
|
707
|
+
registry().lock().unwrap().class_tid[&TypeId::of::<T>()]
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
extern "C" fn value_ctor<T: Default>() -> *mut T {
|
|
711
|
+
Box::into_raw(Box::new(T::default()))
|
|
712
|
+
}
|
|
713
|
+
extern "C" fn field_getter<F: WireType + Copy>(offset: usize, s: *const u8) -> <F as WireType>::Wire {
|
|
714
|
+
unsafe { (*(s.add(offset) as *const F)).to_wire() }
|
|
715
|
+
}
|
|
716
|
+
extern "C" fn field_setter<F: WireType>(offset: usize, s: *mut u8, v: <F as WireType>::Wire) {
|
|
717
|
+
unsafe { *(s.add(offset) as *mut F) = F::from_wire(v); }
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
pub struct ValueObjectBuilder<T: 'static> {
|
|
721
|
+
ty: *const c_void,
|
|
722
|
+
_t: std::marker::PhantomData<T>,
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/// Registers a by-value data struct (a plain `{field: ...}` object in JS). Add fields with
|
|
726
|
+
/// `.field::<F>(name, offset_of!(T, field))`, then call `.finalize()`. `T` must be `Default`.
|
|
727
|
+
pub fn value_object_<T: Default + 'static>(name: &str) -> ValueObjectBuilder<T> {
|
|
728
|
+
let (ty, name_ptr, ctor_sig, dtor_sig);
|
|
729
|
+
{
|
|
730
|
+
let mut r = registry().lock().unwrap();
|
|
731
|
+
ty = r.tid();
|
|
732
|
+
name_ptr = r.cstr(name);
|
|
733
|
+
ctor_sig = r.cstr("p");
|
|
734
|
+
dtor_sig = r.cstr("vp");
|
|
735
|
+
r.class_tid.insert(TypeId::of::<T>(), ty);
|
|
736
|
+
}
|
|
737
|
+
unsafe {
|
|
738
|
+
crossbind_embind_register_value_object(
|
|
739
|
+
ty, name_ptr, ctor_sig, value_ctor::<T> as *const () as usize, dtor_sig, dtor_thunk::<T> as *const () as usize,
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
ValueObjectBuilder { ty, _t: std::marker::PhantomData }
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
impl<T: 'static> ValueObjectBuilder<T> {
|
|
746
|
+
pub fn field<F: WireType + Copy>(self, name: &str, offset: usize) -> Self {
|
|
747
|
+
let (name_ptr, getter_sig, setter_sig);
|
|
748
|
+
{
|
|
749
|
+
let mut r = registry().lock().unwrap();
|
|
750
|
+
name_ptr = r.cstr(name);
|
|
751
|
+
getter_sig = r.cstr(&format!("{}pp", F::SIG));
|
|
752
|
+
setter_sig = r.cstr(&format!("vpp{}", F::SIG));
|
|
753
|
+
}
|
|
754
|
+
unsafe {
|
|
755
|
+
crossbind_embind_register_value_object_field(
|
|
756
|
+
self.ty, name_ptr,
|
|
757
|
+
F::tid(), getter_sig, field_getter::<F> as *const () as usize, offset,
|
|
758
|
+
F::tid(), setter_sig, field_setter::<F> as *const () as usize, offset,
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
self
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
pub fn finalize(self) {
|
|
765
|
+
unsafe { crossbind_embind_finalize_value_object(self.ty); }
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
pub struct ClassBuilder<T: 'static> {
|
|
770
|
+
cls: *const c_void,
|
|
771
|
+
ptr_ty: *const c_void,
|
|
772
|
+
smart_ptr_ty: Option<*const c_void>,
|
|
773
|
+
_t: std::marker::PhantomData<T>,
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// Identity thunks for the smart pointer: our "smart pointer value" IS the raw Box'd pointee,
|
|
777
|
+
// so getPointee/construct/share are identity and the destructor drops the Box. Registered with
|
|
778
|
+
// sharing_policy NONE, so share is never actually called and the pointee stays uniquely owned
|
|
779
|
+
// (making `&mut *this` in method invokers sound).
|
|
780
|
+
extern "C" fn smart_identity(p: *mut c_void) -> *mut c_void { p }
|
|
781
|
+
|
|
782
|
+
pub fn class_<T: 'static>(name: &str) -> ClassBuilder<T> {
|
|
783
|
+
let (cls, ptr_ty, const_ptr_ty, gat, name_ptr, dtor_sig);
|
|
784
|
+
{
|
|
785
|
+
let mut r = registry().lock().unwrap();
|
|
786
|
+
cls = r.tid();
|
|
787
|
+
ptr_ty = r.tid();
|
|
788
|
+
const_ptr_ty = r.tid();
|
|
789
|
+
gat = r.cstr("pp");
|
|
790
|
+
name_ptr = r.cstr(name);
|
|
791
|
+
dtor_sig = r.cstr("vp");
|
|
792
|
+
r.class_tid.insert(TypeId::of::<T>(), cls);
|
|
793
|
+
}
|
|
794
|
+
unsafe {
|
|
795
|
+
crossbind_embind_register_class(
|
|
796
|
+
cls, ptr_ty, const_ptr_ty, std::ptr::null(),
|
|
797
|
+
gat, get_actual_type_thunk::<T> as *const () as usize,
|
|
798
|
+
std::ptr::null(), 0,
|
|
799
|
+
std::ptr::null(), 0,
|
|
800
|
+
name_ptr, dtor_sig, dtor_thunk::<T> as *const () as usize,
|
|
801
|
+
);
|
|
802
|
+
}
|
|
803
|
+
ClassBuilder { cls, ptr_ty, smart_ptr_ty: None, _t: std::marker::PhantomData }
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
impl<T: 'static> ClassBuilder<T> {
|
|
807
|
+
/// Registers a smart-pointer type for this class so factory functions can return an
|
|
808
|
+
/// object that JS owns and frees on delete(). Call before `.createN`.
|
|
809
|
+
pub fn smart_ptr(mut self, name: &str) -> Self {
|
|
810
|
+
let (ptr_ty, name_ptr, gp_sig, ctor_sig, share_sig, dtor_sig);
|
|
811
|
+
{
|
|
812
|
+
let mut r = registry().lock().unwrap();
|
|
813
|
+
ptr_ty = r.tid();
|
|
814
|
+
name_ptr = r.cstr(name);
|
|
815
|
+
gp_sig = r.cstr("pp");
|
|
816
|
+
ctor_sig = r.cstr("pp");
|
|
817
|
+
share_sig = r.cstr("pp");
|
|
818
|
+
dtor_sig = r.cstr("vp");
|
|
819
|
+
}
|
|
820
|
+
unsafe {
|
|
821
|
+
crossbind_embind_register_smart_ptr(
|
|
822
|
+
ptr_ty, self.cls, name_ptr, 0, // sharing_policy::NONE
|
|
823
|
+
gp_sig, smart_identity as *const () as usize,
|
|
824
|
+
ctor_sig, smart_identity as *const () as usize,
|
|
825
|
+
share_sig, smart_identity as *const () as usize,
|
|
826
|
+
dtor_sig, dtor_thunk::<T> as *const () as usize,
|
|
827
|
+
);
|
|
828
|
+
}
|
|
829
|
+
self.smart_ptr_ty = Some(ptr_ty);
|
|
830
|
+
self
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
/// Shared-ownership variant (`Arc<T>` surfaces): same identity wire, but registered with
|
|
834
|
+
/// embind's INTRUSIVE sharing - share bumps the Arc strong count and delete() drops one,
|
|
835
|
+
/// so several JS handles may co-own one Rust object. Every producing path of such a class
|
|
836
|
+
/// must allocate via Arc (constructor_arcN / create_arcN / Arc-allocating _ptr shims).
|
|
837
|
+
pub fn smart_ptr_shared(mut self, name: &str) -> Self {
|
|
838
|
+
let (ptr_ty, name_ptr, gp_sig, ctor_sig, share_sig, dtor_sig);
|
|
839
|
+
{
|
|
840
|
+
let mut r = registry().lock().unwrap();
|
|
841
|
+
ptr_ty = r.tid();
|
|
842
|
+
name_ptr = r.cstr(name);
|
|
843
|
+
gp_sig = r.cstr("pp");
|
|
844
|
+
ctor_sig = r.cstr("pp");
|
|
845
|
+
share_sig = r.cstr("pp");
|
|
846
|
+
dtor_sig = r.cstr("vp");
|
|
847
|
+
r.shared_ptr_tid.insert(TypeId::of::<T>(), ptr_ty);
|
|
848
|
+
}
|
|
849
|
+
unsafe {
|
|
850
|
+
crossbind_embind_register_smart_ptr(
|
|
851
|
+
ptr_ty, self.cls, name_ptr, 1, // sharing_policy::INTRUSIVE
|
|
852
|
+
gp_sig, smart_identity as *const () as usize,
|
|
853
|
+
ctor_sig, smart_identity as *const () as usize,
|
|
854
|
+
share_sig, arc_share_thunk::<T> as *const () as usize,
|
|
855
|
+
dtor_sig, arc_dtor_thunk::<T> as *const () as usize,
|
|
856
|
+
);
|
|
857
|
+
}
|
|
858
|
+
self.smart_ptr_ty = Some(ptr_ty);
|
|
859
|
+
self
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// embind models a vector as a class with size/get/push_back methods, so this is pure
|
|
864
|
+
// composition over class_/constructor0/function1 - no new registration ABI.
|
|
865
|
+
fn vec_new<T: 'static>() -> Vec<T> { Vec::new() }
|
|
866
|
+
fn vec_size<T: 'static>(v: &mut Vec<T>) -> i32 { v.len() as i32 }
|
|
867
|
+
fn vec_get<T: Copy + 'static>(v: &mut Vec<T>, i: i32) -> T { v[i as usize] }
|
|
868
|
+
fn vec_push<T: 'static>(v: &mut Vec<T>, x: T) { v.push(x); } // void return
|
|
869
|
+
|
|
870
|
+
/// Registers `Vec<T>` as a JS class named `name` with `size()`, `get(i)`, `push_back(v)`.
|
|
871
|
+
pub fn register_vector<T: WireType + Copy>(name: &str) {
|
|
872
|
+
class_::<Vec<T>>(name)
|
|
873
|
+
.constructor0(vec_new::<T>)
|
|
874
|
+
.function0("size", vec_size::<T>)
|
|
875
|
+
.function1("get", vec_get::<T>)
|
|
876
|
+
.function1("push_back", vec_push::<T>);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
// N-arity static factories: a class function that returns the smart-pointer-owned object.
|
|
880
|
+
macro_rules! factories {
|
|
881
|
+
($( $method:ident / $invoker:ident : $($arg:ident),* ; )*) => {
|
|
882
|
+
$(
|
|
883
|
+
extern "C" fn $invoker<T: 'static $(, $arg: WireType)*>(
|
|
884
|
+
method: usize $(, $arg: <$arg as WireType>::Wire)*
|
|
885
|
+
) -> *mut T {
|
|
886
|
+
let f: fn($($arg),*) -> T = unsafe { core::mem::transmute(method) };
|
|
887
|
+
Box::into_raw(Box::new(f($(<$arg as WireType>::from_wire($arg)),*)))
|
|
888
|
+
}
|
|
889
|
+
)*
|
|
890
|
+
impl<T: 'static> ClassBuilder<T> {
|
|
891
|
+
$(
|
|
892
|
+
pub fn $method<$($arg: WireType),*>(self, name: &str, f: fn($($arg),*) -> T) -> Self {
|
|
893
|
+
let ret = self.smart_ptr_ty.expect("call .smart_ptr(name) before a factory");
|
|
894
|
+
let mut sig = String::from("pp");
|
|
895
|
+
let mut args = vec![ret];
|
|
896
|
+
$( sig.push(<$arg as WireType>::SIG); args.push(<$arg as WireType>::tid()); )*
|
|
897
|
+
let (argc, arg_ptr, name_ptr, sig_ptr);
|
|
898
|
+
{
|
|
899
|
+
let mut r = registry().lock().unwrap();
|
|
900
|
+
argc = args.len() as u32;
|
|
901
|
+
arg_ptr = r.argtypes(args);
|
|
902
|
+
name_ptr = r.cstr(name);
|
|
903
|
+
sig_ptr = r.cstr(&sig);
|
|
904
|
+
}
|
|
905
|
+
unsafe {
|
|
906
|
+
crossbind_embind_register_class_class_function(
|
|
907
|
+
self.cls, name_ptr, argc, arg_ptr, sig_ptr,
|
|
908
|
+
$invoker::<T $(, $arg)*> as *const () as usize, f as *const () as usize, false, false,
|
|
909
|
+
);
|
|
910
|
+
}
|
|
911
|
+
self
|
|
912
|
+
}
|
|
913
|
+
)*
|
|
914
|
+
}
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
factories! {
|
|
918
|
+
create0 / factory_invoker0 : ;
|
|
919
|
+
create1 / factory_invoker1 : A0;
|
|
920
|
+
create2 / factory_invoker2 : A0, A1;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
// Arc-allocating factories for shared classes: identical registration shape, but the object
|
|
924
|
+
// is stored as an Arc::into_raw pointer so delete()/share stay strong-count based.
|
|
925
|
+
macro_rules! arc_factories {
|
|
926
|
+
($( $method:ident / $invoker:ident : $($arg:ident),* ; )*) => {
|
|
927
|
+
$(
|
|
928
|
+
extern "C" fn $invoker<T: 'static $(, $arg: WireType)*>(
|
|
929
|
+
method: usize $(, $arg: <$arg as WireType>::Wire)*
|
|
930
|
+
) -> *mut T {
|
|
931
|
+
let f: fn($($arg),*) -> std::sync::Arc<T> = unsafe { core::mem::transmute(method) };
|
|
932
|
+
std::sync::Arc::into_raw(f($(<$arg as WireType>::from_wire($arg)),*)) as *mut T
|
|
933
|
+
}
|
|
934
|
+
)*
|
|
935
|
+
impl<T: 'static> ClassBuilder<T> {
|
|
936
|
+
$(
|
|
937
|
+
pub fn $method<$($arg: WireType),*>(self, name: &str, f: fn($($arg),*) -> std::sync::Arc<T>) -> Self {
|
|
938
|
+
let ret = self.smart_ptr_ty.expect("call .smart_ptr_shared(name) before an arc factory");
|
|
939
|
+
let mut sig = String::from("pp");
|
|
940
|
+
let mut args = vec![ret];
|
|
941
|
+
$( sig.push(<$arg as WireType>::SIG); args.push(<$arg as WireType>::tid()); )*
|
|
942
|
+
let (argc, arg_ptr, name_ptr, sig_ptr);
|
|
943
|
+
{
|
|
944
|
+
let mut r = registry().lock().unwrap();
|
|
945
|
+
argc = args.len() as u32;
|
|
946
|
+
arg_ptr = r.argtypes(args);
|
|
947
|
+
name_ptr = r.cstr(name);
|
|
948
|
+
sig_ptr = r.cstr(&sig);
|
|
949
|
+
}
|
|
950
|
+
unsafe {
|
|
951
|
+
crossbind_embind_register_class_class_function(
|
|
952
|
+
self.cls, name_ptr, argc, arg_ptr, sig_ptr,
|
|
953
|
+
$invoker::<T $(, $arg)*> as *const () as usize, f as *const () as usize, false, false,
|
|
954
|
+
);
|
|
955
|
+
}
|
|
956
|
+
self
|
|
957
|
+
}
|
|
958
|
+
)*
|
|
959
|
+
}
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
arc_factories! {
|
|
963
|
+
create_arc0 / arc_factory_invoker0 : ;
|
|
964
|
+
create_arc1 / arc_factory_invoker1 : A0;
|
|
965
|
+
create_arc2 / arc_factory_invoker2 : A0, A1;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
// N-arity constructors: one monomorphized invoker + builder method per argument count.
|
|
969
|
+
macro_rules! constructors {
|
|
970
|
+
($( $method:ident / $invoker:ident : $($arg:ident),* ; )*) => {
|
|
971
|
+
$(
|
|
972
|
+
extern "C" fn $invoker<T: 'static $(, $arg: WireType)*>(
|
|
973
|
+
ctor: usize $(, $arg: <$arg as WireType>::Wire)*
|
|
974
|
+
) -> *mut T {
|
|
975
|
+
let f: fn($($arg),*) -> T = unsafe { core::mem::transmute(ctor) };
|
|
976
|
+
Box::into_raw(Box::new(f($(<$arg as WireType>::from_wire($arg)),*)))
|
|
977
|
+
}
|
|
978
|
+
)*
|
|
979
|
+
impl<T: 'static> ClassBuilder<T> {
|
|
980
|
+
$(
|
|
981
|
+
pub fn $method<$($arg: WireType),*>(self, f: fn($($arg),*) -> T) -> Self {
|
|
982
|
+
let mut sig = String::from("pp");
|
|
983
|
+
let mut args = vec![self.ptr_ty];
|
|
984
|
+
$( sig.push(<$arg as WireType>::SIG); args.push(<$arg as WireType>::tid()); )*
|
|
985
|
+
let (argc, arg_ptr, sig_ptr);
|
|
986
|
+
{
|
|
987
|
+
let mut r = registry().lock().unwrap();
|
|
988
|
+
argc = args.len() as u32;
|
|
989
|
+
arg_ptr = r.argtypes(args);
|
|
990
|
+
sig_ptr = r.cstr(&sig);
|
|
991
|
+
}
|
|
992
|
+
unsafe {
|
|
993
|
+
crossbind_embind_register_class_constructor(
|
|
994
|
+
self.cls, argc, arg_ptr, sig_ptr,
|
|
995
|
+
$invoker::<T $(, $arg)*> as *const () as usize, f as *const () as usize,
|
|
996
|
+
);
|
|
997
|
+
}
|
|
998
|
+
self
|
|
999
|
+
}
|
|
1000
|
+
)*
|
|
1001
|
+
}
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
constructors! {
|
|
1005
|
+
constructor0 / ctor_invoker0 : ;
|
|
1006
|
+
constructor1 / ctor_invoker1 : A0;
|
|
1007
|
+
constructor2 / ctor_invoker2 : A0, A1;
|
|
1008
|
+
constructor3 / ctor_invoker3 : A0, A1, A2;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
// Raw-pointer ctor variants for fallible `new`: the shim boxes the value itself (or raises and
|
|
1012
|
+
// returns null), so `Result<Self, E>` rides the same registration ABI as a plain constructor.
|
|
1013
|
+
macro_rules! constructors_ptr {
|
|
1014
|
+
($( $method:ident / $invoker:ident : $($arg:ident),* ; )*) => {
|
|
1015
|
+
$(
|
|
1016
|
+
extern "C" fn $invoker<T: 'static $(, $arg: WireType)*>(
|
|
1017
|
+
ctor: usize $(, $arg: <$arg as WireType>::Wire)*
|
|
1018
|
+
) -> *mut T {
|
|
1019
|
+
let f: fn($($arg),*) -> *mut T = unsafe { core::mem::transmute(ctor) };
|
|
1020
|
+
f($(<$arg as WireType>::from_wire($arg)),*)
|
|
1021
|
+
}
|
|
1022
|
+
)*
|
|
1023
|
+
impl<T: 'static> ClassBuilder<T> {
|
|
1024
|
+
$(
|
|
1025
|
+
pub fn $method<$($arg: WireType),*>(self, f: fn($($arg),*) -> *mut T) -> Self {
|
|
1026
|
+
let mut sig = String::from("pp");
|
|
1027
|
+
let mut args = vec![self.ptr_ty];
|
|
1028
|
+
$( sig.push(<$arg as WireType>::SIG); args.push(<$arg as WireType>::tid()); )*
|
|
1029
|
+
let (argc, arg_ptr, sig_ptr);
|
|
1030
|
+
{
|
|
1031
|
+
let mut r = registry().lock().unwrap();
|
|
1032
|
+
argc = args.len() as u32;
|
|
1033
|
+
arg_ptr = r.argtypes(args);
|
|
1034
|
+
sig_ptr = r.cstr(&sig);
|
|
1035
|
+
}
|
|
1036
|
+
unsafe {
|
|
1037
|
+
crossbind_embind_register_class_constructor(
|
|
1038
|
+
self.cls, argc, arg_ptr, sig_ptr,
|
|
1039
|
+
$invoker::<T $(, $arg)*> as *const () as usize, f as *const () as usize,
|
|
1040
|
+
);
|
|
1041
|
+
}
|
|
1042
|
+
self
|
|
1043
|
+
}
|
|
1044
|
+
)*
|
|
1045
|
+
}
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
constructors_ptr! {
|
|
1049
|
+
constructor_ptr0 / ctor_ptr_invoker0 : ;
|
|
1050
|
+
constructor_ptr1 / ctor_ptr_invoker1 : A0;
|
|
1051
|
+
constructor_ptr2 / ctor_ptr_invoker2 : A0, A1;
|
|
1052
|
+
constructor_ptr3 / ctor_ptr_invoker3 : A0, A1, A2;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
// Raw-pointer factory variants: `Result<Self, E>` raises on Err, `Option<Self>` returns null
|
|
1056
|
+
// (the consumer maps a null smart pointer to JS null).
|
|
1057
|
+
macro_rules! factories_ptr {
|
|
1058
|
+
($( $method:ident / $invoker:ident : $($arg:ident),* ; )*) => {
|
|
1059
|
+
$(
|
|
1060
|
+
extern "C" fn $invoker<T: 'static $(, $arg: WireType)*>(
|
|
1061
|
+
method: usize $(, $arg: <$arg as WireType>::Wire)*
|
|
1062
|
+
) -> *mut T {
|
|
1063
|
+
let f: fn($($arg),*) -> *mut T = unsafe { core::mem::transmute(method) };
|
|
1064
|
+
f($(<$arg as WireType>::from_wire($arg)),*)
|
|
1065
|
+
}
|
|
1066
|
+
)*
|
|
1067
|
+
impl<T: 'static> ClassBuilder<T> {
|
|
1068
|
+
$(
|
|
1069
|
+
pub fn $method<$($arg: WireType),*>(self, name: &str, f: fn($($arg),*) -> *mut T) -> Self {
|
|
1070
|
+
let ret = self.smart_ptr_ty.expect("call .smart_ptr(name) before a factory");
|
|
1071
|
+
let mut sig = String::from("pp");
|
|
1072
|
+
let mut args = vec![ret];
|
|
1073
|
+
$( sig.push(<$arg as WireType>::SIG); args.push(<$arg as WireType>::tid()); )*
|
|
1074
|
+
let (argc, arg_ptr, name_ptr, sig_ptr);
|
|
1075
|
+
{
|
|
1076
|
+
let mut r = registry().lock().unwrap();
|
|
1077
|
+
argc = args.len() as u32;
|
|
1078
|
+
arg_ptr = r.argtypes(args);
|
|
1079
|
+
name_ptr = r.cstr(name);
|
|
1080
|
+
sig_ptr = r.cstr(&sig);
|
|
1081
|
+
}
|
|
1082
|
+
unsafe {
|
|
1083
|
+
crossbind_embind_register_class_class_function(
|
|
1084
|
+
self.cls, name_ptr, argc, arg_ptr, sig_ptr,
|
|
1085
|
+
$invoker::<T $(, $arg)*> as *const () as usize, f as *const () as usize, false, false,
|
|
1086
|
+
);
|
|
1087
|
+
}
|
|
1088
|
+
self
|
|
1089
|
+
}
|
|
1090
|
+
)*
|
|
1091
|
+
}
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
factories_ptr! {
|
|
1095
|
+
create_ptr0 / factory_ptr_invoker0 : ;
|
|
1096
|
+
create_ptr1 / factory_ptr_invoker1 : A0;
|
|
1097
|
+
create_ptr2 / factory_ptr_invoker2 : A0, A1;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
// N-arity instance methods.
|
|
1101
|
+
macro_rules! functions {
|
|
1102
|
+
($( $method:ident / $invoker:ident : $($arg:ident),* ; )*) => {
|
|
1103
|
+
$(
|
|
1104
|
+
extern "C" fn $invoker<T: 'static $(, $arg: WireType)*, R: WireType>(
|
|
1105
|
+
ctx: usize, this: *mut T $(, $arg: <$arg as WireType>::Wire)*
|
|
1106
|
+
) -> <R as WireType>::Wire {
|
|
1107
|
+
let f: fn(&mut T $(, $arg)*) -> R = unsafe { core::mem::transmute(ctx) };
|
|
1108
|
+
f(unsafe { &mut *this } $(, <$arg as WireType>::from_wire($arg))*).to_wire()
|
|
1109
|
+
}
|
|
1110
|
+
)*
|
|
1111
|
+
impl<T: 'static> ClassBuilder<T> {
|
|
1112
|
+
$(
|
|
1113
|
+
pub fn $method<$($arg: WireType,)* R: WireType>(self, name: &str, f: fn(&mut T $(, $arg)*) -> R) -> Self {
|
|
1114
|
+
let mut sig = String::new();
|
|
1115
|
+
sig.push(R::SIG); sig.push('p'); sig.push('p');
|
|
1116
|
+
let mut args = vec![R::tid(), self.ptr_ty];
|
|
1117
|
+
$( sig.push(<$arg as WireType>::SIG); args.push(<$arg as WireType>::tid()); )*
|
|
1118
|
+
let (argc, arg_ptr, name_ptr, sig_ptr);
|
|
1119
|
+
{
|
|
1120
|
+
let mut r = registry().lock().unwrap();
|
|
1121
|
+
argc = args.len() as u32;
|
|
1122
|
+
arg_ptr = r.argtypes(args);
|
|
1123
|
+
name_ptr = r.cstr(name);
|
|
1124
|
+
sig_ptr = r.cstr(&sig);
|
|
1125
|
+
}
|
|
1126
|
+
unsafe {
|
|
1127
|
+
crossbind_embind_register_class_function(
|
|
1128
|
+
self.cls, name_ptr, argc, arg_ptr, sig_ptr,
|
|
1129
|
+
$invoker::<T $(, $arg)*, R> as *const () as usize, f as *const () as usize, 0, false, false,
|
|
1130
|
+
);
|
|
1131
|
+
}
|
|
1132
|
+
self
|
|
1133
|
+
}
|
|
1134
|
+
)*
|
|
1135
|
+
}
|
|
1136
|
+
};
|
|
1137
|
+
}
|
|
1138
|
+
functions! {
|
|
1139
|
+
function0 / method_invoker0 : ;
|
|
1140
|
+
function1 / method_invoker1 : A0;
|
|
1141
|
+
function2 / method_invoker2 : A0, A1;
|
|
1142
|
+
function3 / method_invoker3 : A0, A1, A2;
|
|
1143
|
+
function4 / method_invoker4 : A0, A1, A2, A3;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
// N-arity FREE functions (`Module.name(..)` in JS, no class). embind slices the target off
|
|
1147
|
+
// exactly like methods, so the adapter bakes + prepends the fn pointer; argTypes = [ret, args..].
|
|
1148
|
+
macro_rules! free_functions {
|
|
1149
|
+
($( $fname:ident / $invoker:ident : $($arg:ident),* ; )*) => {
|
|
1150
|
+
$(
|
|
1151
|
+
extern "C" fn $invoker<$($arg: WireType,)* R: WireType>(
|
|
1152
|
+
ctx: usize $(, $arg: <$arg as WireType>::Wire)*
|
|
1153
|
+
) -> <R as WireType>::Wire {
|
|
1154
|
+
let f: fn($($arg),*) -> R = unsafe { core::mem::transmute(ctx) };
|
|
1155
|
+
f($(<$arg as WireType>::from_wire($arg)),*).to_wire()
|
|
1156
|
+
}
|
|
1157
|
+
pub fn $fname<$($arg: WireType,)* R: WireType>(name: &str, f: fn($($arg),*) -> R) {
|
|
1158
|
+
let mut sig = String::new();
|
|
1159
|
+
sig.push(R::SIG); sig.push('p');
|
|
1160
|
+
let mut args = vec![R::tid()];
|
|
1161
|
+
$( sig.push(<$arg as WireType>::SIG); args.push(<$arg as WireType>::tid()); )*
|
|
1162
|
+
let (argc, arg_ptr, name_ptr, sig_ptr);
|
|
1163
|
+
{
|
|
1164
|
+
let mut r = registry().lock().unwrap();
|
|
1165
|
+
argc = args.len() as u32;
|
|
1166
|
+
arg_ptr = r.argtypes(args);
|
|
1167
|
+
name_ptr = r.cstr(name);
|
|
1168
|
+
sig_ptr = r.cstr(&sig);
|
|
1169
|
+
}
|
|
1170
|
+
unsafe {
|
|
1171
|
+
crossbind_embind_register_function(
|
|
1172
|
+
name_ptr, argc, arg_ptr, sig_ptr,
|
|
1173
|
+
$invoker::<$($arg,)* R> as *const () as usize, f as *const () as usize, false, false,
|
|
1174
|
+
);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
)*
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
free_functions! {
|
|
1181
|
+
fn0 / free_invoker0 : ;
|
|
1182
|
+
fn1 / free_invoker1 : A0;
|
|
1183
|
+
fn2 / free_invoker2 : A0, A1;
|
|
1184
|
+
fn3 / free_invoker3 : A0, A1, A2;
|
|
1185
|
+
fn4 / free_invoker4 : A0, A1, A2, A3;
|
|
1186
|
+
}
|