@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.
@@ -0,0 +1,433 @@
1
+ // jsi ADAPTER (mobile): stable flat C-ABI -> @crossbind/core-embind-jsi.
2
+ //
3
+ // Wraps each raw Rust invoker into a jsi::Function via bounded-dispatch: because every wire kind
4
+ // is integer-class in the invoker's C signature (doubles travel as bit patterns in u64 - see the
5
+ // crate's WireType for f64), uint64-slot casts dispatch correctly on every native ABI with no
6
+ // libffi and no per-arch assembly. (Apple ships libffi as private API on iOS - no header - so
7
+ // staying libffi-free is also the App-Store-safe choice.) The only bound is the arity cap below.
8
+ //
9
+ // KEY FINDING (embind.js craftInvokerFunction + getDynCaller): embind builds each invoker call as
10
+ // `invoker(targetFn, [thisWired,] argsWired...)`, then getDynCaller wraps our jsi::Function with
11
+ // slice=true for constructors/methods/static/free functions and DROPS the leading targetFn before
12
+ // calling us. So our jsi::Function receives ONLY `([thisWired,] argsWired...)` - the target pointer
13
+ // never arrives. But our Rust invoker takes that target as its first C parameter, so the adapter
14
+ // BAKES the target (known at registration) and prepends it when dispatching. Direct functions
15
+ // (getActualType, destructor, smart-ptr ops, value-object ctor/dtor/fields) are registered with
16
+ // slice=false and called with their real args, so those map 1:1 with no prepend.
17
+ //
18
+ // Wire kinds (embind.js marshals before calling us): int/bool/enum -> jsi number, pointer
19
+ // (this/handle/value-object wire) -> jsi::BigInt(uint64), std::string -> jsi::String (SIG 's',
20
+ // marshalled to/from the crate's [u32 len][bytes] buffer in readArg / wireStringToJsi).
21
+ //
22
+ // Compiled ONLY inside the RN native module (jsi + embind-jsi headers exist there via build_ios.js).
23
+
24
+ #include <jsi/jsi.h>
25
+ #include <cstdint>
26
+ #include <cstdlib>
27
+ #include <cstring>
28
+ #include <optional>
29
+ #include <string>
30
+ #include <typeinfo>
31
+ #include <unordered_set>
32
+ // Real embind-jsi declarations (correct namespaces/linkage/signatures): jsRuntime lives in
33
+ // `emscripten`, the _embind_register_* functions and InitFunc in `emscripten::internal` with C++
34
+ // linkage and jsi::Function& slots. Including its header avoids any hand-declaration mismatch.
35
+ #include <emscripten/bind.h>
36
+ #include "../include/crossbind_embind.h"
37
+
38
+ namespace jsi = facebook::jsi;
39
+ using emscripten::jsRuntime;
40
+ using namespace emscripten::internal; // InitFunc + the _embind_register_* functions
41
+
42
+ // Defined in embind-jsi's bind.cpp but not declared in its header: the identity value type.
43
+ // Optional returns register through it because THIS adapter already converts the nullable
44
+ // heap cell to the final jsi value (undefined or the inner) before embind-jsi sees it.
45
+ namespace emscripten { namespace internal {
46
+ void _embind_register_jsiValue(TYPEID jsiValueType, const char* name);
47
+ } }
48
+ // embind-jsi's TYPEID is `const void*` (wire.h) - identical to our CrossbindTid, so no conversion.
49
+
50
+ extern "C" {
51
+ const void* crossbind_tid_int() { return &typeid(int); }
52
+ const void* crossbind_tid_double() { return &typeid(double); }
53
+ const void* crossbind_tid_bool() { return &typeid(bool); }
54
+ const void* crossbind_tid_void() { return &typeid(void); }
55
+ const void* crossbind_tid_std_string() { return &typeid(std::string); }
56
+ const void* crossbind_tid_int64() { return &typeid(int64_t); }
57
+ const void* crossbind_tid_uint64() { return &typeid(uint64_t); }
58
+ // Adapter-local identities, NOT &typeid(std::optional<T>): linked C++ bindings may register
59
+ // the real optional typeids themselves, and these types never interop with C++ signatures.
60
+ const void* crossbind_tid_optional_int() { static const char t = 0; return &t; }
61
+ const void* crossbind_tid_optional_double() { static const char t = 0; return &t; }
62
+ const void* crossbind_tid_optional_bool() { static const char t = 0; return &t; }
63
+ const void* crossbind_tid_optional_string() { static const char t = 0; return &t; }
64
+
65
+ // Idempotent: every Rust archive in the app may ask for the same optional type.
66
+ void crossbind_embind_register_optional(const void* opt, const void* /*inner: adapter converts*/) {
67
+ static std::unordered_set<const void*> registered;
68
+ if (!registered.insert(opt).second) return;
69
+ _embind_register_jsiValue(opt, "std::optional");
70
+ }
71
+
72
+ // JSON-value bridge (serde_json::Value): raw JSI into two handle-based fork helpers - the
73
+ // fork's emval STRING plumbing reads wasm-heap pointers (none exist natively), so val::global
74
+ // style calls die with "Property 'WebAssembly' doesn't exist" on device. Handle refcounts:
75
+ // to_handle returns an owned +1; handle_to_json consumes its argument (the JS helper decrefs).
76
+ const void* crossbind_tid_emval() { return &typeid(emscripten::val); }
77
+
78
+ void* crossbind_emval_json_to_handle(uint8_t* w) {
79
+ uint32_t len;
80
+ std::memcpy(&len, w, 4);
81
+ std::string s((const char*)(w + 4), len);
82
+ std::free(w);
83
+ auto r = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__crossbind_json_to_handle")
84
+ .call(*jsRuntime, jsi::String::createFromUtf8(*jsRuntime, s));
85
+ // Fork emval handles are BigInt end-to-end (Emval.toHandle returns 1n/2n/allocator BigInts).
86
+ return (void*)(uintptr_t)r.asBigInt(*jsRuntime).asUint64(*jsRuntime);
87
+ }
88
+
89
+ uint8_t* crossbind_emval_handle_to_json(void* h) {
90
+ auto r = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__crossbind_handle_to_json")
91
+ .call(*jsRuntime, jsi::Value(jsi::BigInt::fromUint64(*jsRuntime, (uint64_t)(uintptr_t)h)));
92
+ std::string s = r.asString(*jsRuntime).utf8(*jsRuntime);
93
+ uint8_t* w = (uint8_t*)std::malloc(4 + s.size());
94
+ uint32_t len = (uint32_t)s.size();
95
+ std::memcpy(w, &len, 4);
96
+ std::memcpy(w + 4, s.data(), s.size());
97
+ return w;
98
+ }
99
+ }
100
+
101
+ // ---- live JS value hooks (JsValue / JsFunction) ----
102
+ // Fork helpers over the emval handle table (BigInt handles end-to-end); strings cross as jsi
103
+ // strings, never through the fork's wasm-heap emval string plumbing. Called directly from the
104
+ // Rust crate (not through bounded dispatch), so real f64 parameters are fine here.
105
+ static jsi::Value crossbindVHandle(uint64_t h) {
106
+ return jsi::Value(jsi::BigInt::fromUint64(*jsRuntime, h));
107
+ }
108
+ static uint64_t crossbindHandleOf(const jsi::Value& v) {
109
+ return v.asBigInt(*jsRuntime).asUint64(*jsRuntime);
110
+ }
111
+ static jsi::String crossbindKeyFromWire(uint8_t* w) {
112
+ uint32_t len;
113
+ std::memcpy(&len, w, 4);
114
+ std::string k((const char*)(w + 4), len);
115
+ std::free(w);
116
+ return jsi::String::createFromUtf8(*jsRuntime, k);
117
+ }
118
+ static uint8_t* crossbindWireFromString(const std::string& s) {
119
+ uint8_t* w = (uint8_t*)std::malloc(4 + s.size());
120
+ uint32_t len = (uint32_t)s.size();
121
+ std::memcpy(w, &len, 4);
122
+ std::memcpy(w + 4, s.data(), s.size());
123
+ return w;
124
+ }
125
+ static jsi::Function crossbindHelper(const char* name) {
126
+ return jsRuntime->global().getPropertyAsFunction(*jsRuntime, name);
127
+ }
128
+ static thread_local std::string g_cbError;
129
+ static thread_local bool g_hasCbError = false;
130
+
131
+ extern "C" {
132
+ void crossbind_v_ref(size_t h) {
133
+ crossbindHelper("__emval_incref").call(*jsRuntime, crossbindVHandle(h));
134
+ }
135
+ void crossbind_v_unref(size_t h) {
136
+ crossbindHelper("__emval_decref").call(*jsRuntime, crossbindVHandle(h));
137
+ }
138
+ // Fork emval constants: undefined = 1n (init_emval seeds 1..4, reserved = 5).
139
+ size_t crossbind_v_undef() { return 1; }
140
+ size_t crossbind_v_from_f64(double v) {
141
+ return (size_t)crossbindHandleOf(crossbindHelper("__crossbind_v_from_num").call(*jsRuntime, jsi::Value(v)));
142
+ }
143
+ size_t crossbind_v_from_bool(int v) {
144
+ return (size_t)crossbindHandleOf(crossbindHelper("__crossbind_v_from_bool").call(*jsRuntime, jsi::Value(v)));
145
+ }
146
+ size_t crossbind_v_from_str(uint8_t* w) {
147
+ return (size_t)crossbindHandleOf(crossbindHelper("__crossbind_v_from_str").call(*jsRuntime, crossbindKeyFromWire(w)));
148
+ }
149
+ size_t crossbind_v_get_prop(size_t h, uint8_t* w) {
150
+ return (size_t)crossbindHandleOf(crossbindHelper("__crossbind_v_get").call(*jsRuntime, crossbindVHandle(h), crossbindKeyFromWire(w)));
151
+ }
152
+ void crossbind_v_set_prop(size_t h, uint8_t* w, size_t v) {
153
+ crossbindHelper("__crossbind_v_set").call(*jsRuntime, crossbindVHandle(h), crossbindKeyFromWire(w), crossbindVHandle(v));
154
+ }
155
+ int crossbind_v_kind(size_t h) {
156
+ return (int)crossbindHelper("__crossbind_v_kind").call(*jsRuntime, crossbindVHandle(h)).getNumber();
157
+ }
158
+ double crossbind_v_as_f64(size_t h) {
159
+ return crossbindHelper("__crossbind_v_as_num").call(*jsRuntime, crossbindVHandle(h)).getNumber();
160
+ }
161
+ int crossbind_v_as_bool(size_t h) {
162
+ return (int)crossbindHelper("__crossbind_v_as_bool").call(*jsRuntime, crossbindVHandle(h)).getNumber();
163
+ }
164
+ uint8_t* crossbind_v_as_str(size_t h) {
165
+ auto r = crossbindHelper("__crossbind_v_as_str").call(*jsRuntime, crossbindVHandle(h));
166
+ return crossbindWireFromString(r.asString(*jsRuntime).utf8(*jsRuntime));
167
+ }
168
+ size_t crossbind_v_call(size_t f, unsigned argc, const size_t* argv) {
169
+ try {
170
+ jsi::Array args(*jsRuntime, argc);
171
+ for (unsigned i = 0; i < argc; i += 1) args.setValueAtIndex(*jsRuntime, i, crossbindVHandle(argv[i]));
172
+ auto r = crossbindHelper("__crossbind_v_call").call(*jsRuntime, crossbindVHandle(f), args);
173
+ return (size_t)crossbindHandleOf(r);
174
+ } catch (const jsi::JSError& e) {
175
+ g_cbError = e.getMessage();
176
+ g_hasCbError = true;
177
+ return 0;
178
+ } catch (const std::exception& e) {
179
+ g_cbError = e.what();
180
+ g_hasCbError = true;
181
+ return 0;
182
+ }
183
+ }
184
+ uint8_t* crossbind_v_cb_err_take() {
185
+ if (!g_hasCbError) return nullptr;
186
+ g_hasCbError = false;
187
+ return crossbindWireFromString(g_cbError);
188
+ }
189
+ }
190
+
191
+ // Error raised by the producer (embind_rs::raise_err): Rust cannot unwind across the C-ABI, so
192
+ // it parks the message here and returns a sentinel; callInvoker below turns it into a JSError.
193
+ static thread_local std::string g_pendingError;
194
+ static thread_local bool g_hasPendingError = false;
195
+
196
+ extern "C" void crossbind_embind_raise_error(uint8_t* w) {
197
+ uint32_t len;
198
+ std::memcpy(&len, w, 4);
199
+ g_pendingError.assign((const char*)(w + 4), (size_t)len);
200
+ g_hasPendingError = true;
201
+ std::free(w);
202
+ }
203
+
204
+ // embind calls the invoker as `invoker(ctx, [this,] argsWired...)` (craftInvokerFunction:
205
+ // `invoker(fn, thisWired, arg0Wired, ...)`), so the incoming jsi args ARE the invoker's full C
206
+ // arg list, described 1:1 by sig[1..] (sig[0] is the return). We marshal each and forward.
207
+ // Reads the crate's `[u32 len][bytes]` string wire at `w`, builds a jsi::String, and frees the
208
+ // buffer (the consumer owns it). embind-jsi's std::string fromWireType passes a jsi::String through.
209
+ static jsi::Value wireStringToJsi(jsi::Runtime& rt, uint64_t w) {
210
+ if (!w) return jsi::Value(jsi::String::createFromUtf8(rt, ""));
211
+ uint8_t* buf = (uint8_t*)(uintptr_t)w;
212
+ uint32_t len;
213
+ std::memcpy(&len, buf, 4);
214
+ std::string str((const char*)(buf + 4), (size_t)len);
215
+ std::free(buf);
216
+ return jsi::Value(jsi::String::createFromUtf8(rt, str));
217
+ }
218
+
219
+ static uint64_t readArg(jsi::Runtime& rt, const jsi::Value& v, char c) {
220
+ if (c == 's') {
221
+ // embind-jsi passes std::string args as a jsi::String; build the crate's [u32 len][bytes]
222
+ // wire buffer here (freed by the caller after dispatch - the crate's from_wire only copies).
223
+ if (!v.isString()) return 0;
224
+ std::string str = v.getString(rt).utf8(rt);
225
+ uint32_t len = (uint32_t)str.size();
226
+ uint8_t* buf = (uint8_t*)std::malloc(4 + (size_t)len);
227
+ std::memcpy(buf, &len, 4);
228
+ std::memcpy(buf + 4, str.data(), len);
229
+ return (uint64_t)(uintptr_t)buf;
230
+ }
231
+ if (c == 'd') {
232
+ // The native crate carries a double as its bit pattern in a u64 slot (see WireType for f64),
233
+ // keeping the invoker's C signature integer-class; convert the jsi number to bits here.
234
+ double x = v.getNumber();
235
+ uint64_t bits;
236
+ std::memcpy(&bits, &x, 8);
237
+ return bits;
238
+ }
239
+ // i64/u64 ('j'/'u') arrive as JS BigInt (embind bigint types), Number accepted as a courtesy;
240
+ // both directions are raw 64-bit slots, so the unsigned bit copy is sign-correct either way.
241
+ if (c == 'j' || c == 'u') return v.isBigInt() ? v.getBigInt(rt).getUint64(rt) : (uint64_t)(int64_t)v.getNumber();
242
+ // Optional args ('I'/'D'/'B'/'S'): undefined/null -> 0 (None); otherwise a heap cell this
243
+ // adapter owns and frees after dispatch ('S' carries a plain string wire).
244
+ if (c == 'I' || c == 'D' || c == 'B' || c == 'S') {
245
+ if (v.isUndefined() || v.isNull()) return 0;
246
+ if (c == 'S') return readArg(rt, v, 's');
247
+ if (c == 'I') { int32_t x = (int32_t)v.getNumber(); void* p = std::malloc(4); std::memcpy(p, &x, 4); return (uint64_t)(uintptr_t)p; }
248
+ if (c == 'D') { double x = v.getNumber(); void* p = std::malloc(8); std::memcpy(p, &x, 8); return (uint64_t)(uintptr_t)p; }
249
+ uint8_t b = v.getBool() ? 1 : 0; void* p = std::malloc(1); std::memcpy(p, &b, 1); return (uint64_t)(uintptr_t)p;
250
+ }
251
+ // Pointers (this/handle/value-object wire) arrive as BigInt, int/bool/enum as Number; accept
252
+ // either kind defensively so a host that boxed a pointer as a Number still dispatches correctly.
253
+ if (c == 'p') return v.isBigInt() ? v.getBigInt(rt).getUint64(rt) : (uint64_t)(int64_t)v.getNumber();
254
+ return v.isNumber() ? (uint64_t)(int64_t)v.getNumber() : v.getBigInt(rt).getUint64(rt); // 'i'
255
+ }
256
+
257
+ // Bounded-dispatch: every wire kind is integer-class in the invoker's C signature (pointers and
258
+ // ints natively; doubles as bit patterns in u64 - see WireType for f64), so uint64-slot casts are
259
+ // ABI-correct on every native ABI. No FP registers, no libffi; the only bound is the arity cap.
260
+ static jsi::Value callInvoker(jsi::Runtime& rt, const std::string& sig, void* invoker,
261
+ const jsi::Value* a, uint64_t ctx, bool prepend) {
262
+ const char ret = sig[0];
263
+ const int total = (int)sig.size() - 1; // C args the raw invoker takes (sig[1..])
264
+
265
+ // s[0..total-1] are the raw invoker's C args. When prepend, sig[1] is the baked target pointer
266
+ // (embind sliced it off), so the jsi args a[0..] fill sig[2..]; otherwise a[0..] fill sig[1..].
267
+ uint64_t s[6] = {0, 0, 0, 0, 0, 0};
268
+ int base = 0;
269
+ if (prepend) { s[0] = ctx; base = 1; }
270
+ for (int j = base; j < total && j < 6; ++j) s[j] = readArg(rt, a[j - base], sig[1 + j]);
271
+
272
+ uint64_t r = 0;
273
+ if (ret == 'v') {
274
+ switch (total) {
275
+ case 0: ((void(*)())invoker)(); break;
276
+ case 1: ((void(*)(uint64_t))invoker)(s[0]); break;
277
+ case 2: ((void(*)(uint64_t, uint64_t))invoker)(s[0], s[1]); break;
278
+ case 3: ((void(*)(uint64_t, uint64_t, uint64_t))invoker)(s[0], s[1], s[2]); break;
279
+ case 4: ((void(*)(uint64_t, uint64_t, uint64_t, uint64_t))invoker)(s[0], s[1], s[2], s[3]); break;
280
+ case 5: ((void(*)(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t))invoker)(s[0], s[1], s[2], s[3], s[4]); break;
281
+ default: ((void(*)(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t))invoker)(s[0], s[1], s[2], s[3], s[4], s[5]); break;
282
+ }
283
+ } else {
284
+ switch (total) {
285
+ case 0: r = ((uint64_t(*)())invoker)(); break;
286
+ case 1: r = ((uint64_t(*)(uint64_t))invoker)(s[0]); break;
287
+ case 2: r = ((uint64_t(*)(uint64_t, uint64_t))invoker)(s[0], s[1]); break;
288
+ case 3: r = ((uint64_t(*)(uint64_t, uint64_t, uint64_t))invoker)(s[0], s[1], s[2]); break;
289
+ case 4: r = ((uint64_t(*)(uint64_t, uint64_t, uint64_t, uint64_t))invoker)(s[0], s[1], s[2], s[3]); break;
290
+ case 5: r = ((uint64_t(*)(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t))invoker)(s[0], s[1], s[2], s[3], s[4]); break;
291
+ default: r = ((uint64_t(*)(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t))invoker)(s[0], s[1], s[2], s[3], s[4], s[5]); break;
292
+ }
293
+ }
294
+
295
+ // The consumer owns each incoming std::string arg buffer and optional arg cell; free them
296
+ // now that the crate has copied.
297
+ for (int j = base; j < total && j < 6; ++j) {
298
+ const char cj = sig[1 + j];
299
+ if (cj == 's' || cj == 'S' || cj == 'I' || cj == 'D' || cj == 'B') std::free((void*)(uintptr_t)s[j]);
300
+ }
301
+
302
+ // A shim raised (Result::Err): drop the sentinel return and surface a real JS exception.
303
+ if (g_hasPendingError) {
304
+ g_hasPendingError = false;
305
+ if (ret == 's' && r) std::free((void*)(uintptr_t)r);
306
+ throw jsi::JSError(rt, std::move(g_pendingError));
307
+ }
308
+
309
+ if (ret == 'v') return jsi::Value::undefined();
310
+ if (ret == 's') return wireStringToJsi(rt, r); // the crate returned a [u32 len][bytes] buffer
311
+ // Optional returns ('I'/'D'/'B'/'S'): nullable heap cell - 0 is None (undefined), otherwise
312
+ // the malloc'd inner value ('S' points at a plain string wire); freed here after reading.
313
+ if (ret == 'I' || ret == 'D' || ret == 'B' || ret == 'S') {
314
+ if (!r) return jsi::Value::undefined();
315
+ if (ret == 'S') return wireStringToJsi(rt, r);
316
+ uint8_t* cell = (uint8_t*)(uintptr_t)r;
317
+ jsi::Value out = jsi::Value::undefined();
318
+ if (ret == 'I') { int32_t v; std::memcpy(&v, cell, 4); out = jsi::Value((double)v); }
319
+ else if (ret == 'D') { double v; std::memcpy(&v, cell, 8); out = jsi::Value(v); }
320
+ else { out = jsi::Value(cell[0] != 0); }
321
+ std::free(cell);
322
+ return out;
323
+ }
324
+ if (ret == 'p') return jsi::Value(jsi::BigInt::fromUint64(rt, r));
325
+ if (ret == 'j') return jsi::Value(jsi::BigInt::fromInt64(rt, (int64_t)r));
326
+ if (ret == 'u') return jsi::Value(jsi::BigInt::fromUint64(rt, r));
327
+ if (ret == 'd') { double x; std::memcpy(&x, &r, 8); return jsi::Value(x); } // bits -> number
328
+ return jsi::Value((double)(int32_t)r); // 'i'
329
+ }
330
+
331
+ // prepend=true bakes `ctx` (the target fn pointer, sliced off by embind) as the invoker's first C
332
+ // arg; the jsi args then fill the rest. prepend=false maps the jsi args to sig[1..] directly.
333
+ static jsi::Function wrapInvoker(const std::string& name, const char* sigC, void* invoker,
334
+ uint64_t ctx = 0, bool prepend = false) {
335
+ std::string sig = sigC;
336
+ auto& rt = *jsRuntime;
337
+ std::string dbg = name + "_" + sig;
338
+ unsigned jsArgc = (unsigned)(sig.size() - 1 - (prepend ? 1 : 0)); // args embind actually passes
339
+ return jsi::Function::createFromHostFunction(
340
+ rt, jsi::PropNameID::forAscii(rt, dbg), jsArgc,
341
+ [sig, invoker, ctx, prepend](jsi::Runtime& rt, const jsi::Value&, const jsi::Value* a, size_t) -> jsi::Value {
342
+ return callInvoker(rt, sig, invoker, a, ctx, prepend);
343
+ });
344
+ }
345
+
346
+ extern "C" {
347
+
348
+ void crossbind_embind_register_bindings(void (*init)()) {
349
+ new InitFunc(init); // embind-jsi defers this until the JSI runtime is installed
350
+ }
351
+
352
+ void crossbind_embind_register_class(
353
+ CrossbindTid cls, CrossbindTid ptr_ty, CrossbindTid const_ptr_ty, CrossbindTid base,
354
+ const char* gat_sig, CrossbindFn get_actual_type,
355
+ const char* up_sig, CrossbindFn upcast, const char* down_sig, CrossbindFn downcast,
356
+ const char* name, const char* dtor_sig, CrossbindFn dtor) {
357
+ // Our producer has no base class, so upcast/downcast are never used at runtime; embind-jsi
358
+ // still requires valid jsi::Function& slots with valid signatures. Reuse the getActualType
359
+ // function + its signature for all three (the crate passes null up/down sigs, which would
360
+ // abort in readLatin1String). base stays null.
361
+ (void)up_sig; (void)down_sig; (void)upcast; (void)downcast;
362
+ auto gat = wrapInvoker(std::string(name) + "_gat", gat_sig, get_actual_type);
363
+ auto up = wrapInvoker(std::string(name) + "_up", gat_sig, get_actual_type);
364
+ auto dn = wrapInvoker(std::string(name) + "_dn", gat_sig, get_actual_type);
365
+ auto dt = wrapInvoker(std::string(name) + "_dtor", dtor_sig, dtor);
366
+ _embind_register_class(cls, ptr_ty, const_ptr_ty, base, gat_sig, gat, gat_sig, up, gat_sig, dn, name, dtor_sig, dt);
367
+ }
368
+
369
+ void crossbind_embind_register_class_constructor(
370
+ CrossbindTid cls, unsigned argc, const CrossbindTid* argTypes,
371
+ const char* sig, CrossbindFn invoker, CrossbindFn ctor) {
372
+ auto fn = wrapInvoker("ctor", sig, invoker, (uint64_t)ctor, true); // bake the sliced-off constructor target
373
+ _embind_register_class_constructor(cls, argc, argTypes, sig, fn, ctor);
374
+ }
375
+
376
+ void crossbind_embind_register_class_function(
377
+ CrossbindTid cls, const char* name, unsigned argc, const CrossbindTid* argTypes,
378
+ const char* sig, CrossbindFn invoker, void* ctx,
379
+ unsigned isPureVirtual, bool isAsync, bool /*isNonnullReturn dropped*/) {
380
+ auto fn = wrapInvoker(name, sig, invoker, (uint64_t)ctx, true); // bake the sliced-off method target
381
+ _embind_register_class_function(cls, name, argc, argTypes, sig, fn, ctx, isPureVirtual, isAsync);
382
+ }
383
+
384
+ void crossbind_embind_register_class_class_function(
385
+ CrossbindTid cls, const char* name, unsigned argc, const CrossbindTid* argTypes,
386
+ const char* sig, CrossbindFn invoker, CrossbindFn method, bool isAsync, bool /*isNonnullReturn dropped*/) {
387
+ auto fn = wrapInvoker(name, sig, invoker, (uint64_t)method, true); // bake the sliced-off static target
388
+ _embind_register_class_class_function(cls, name, argc, argTypes, sig, fn, method, isAsync);
389
+ }
390
+
391
+ void crossbind_embind_register_function(
392
+ const char* name, unsigned argc, const CrossbindTid* argTypes,
393
+ const char* sig, CrossbindFn invoker, CrossbindFn function, bool isAsync, bool /*isNonnullReturn dropped*/) {
394
+ auto fn = wrapInvoker(name, sig, invoker, (uint64_t)function, true); // free fns are sliced too
395
+ _embind_register_function(name, argc, argTypes, sig, fn, function, isAsync);
396
+ }
397
+
398
+ void crossbind_embind_register_smart_ptr(
399
+ CrossbindTid ptr_ty, CrossbindTid pointee_ty, const char* name, int sharing,
400
+ const char* gp_sig, CrossbindFn get_pointee, const char* ctor_sig, CrossbindFn constructor,
401
+ const char* share_sig, CrossbindFn share, const char* dtor_sig, CrossbindFn destructor) {
402
+ auto gp = wrapInvoker(std::string(name) + "_gp", gp_sig, get_pointee);
403
+ auto ct = wrapInvoker(std::string(name) + "_ctor", ctor_sig, constructor);
404
+ auto sh = wrapInvoker(std::string(name) + "_share", share_sig, share);
405
+ auto dt = wrapInvoker(std::string(name) + "_dtor", dtor_sig, destructor);
406
+ _embind_register_smart_ptr(ptr_ty, pointee_ty, name, (emscripten::sharing_policy)sharing, gp_sig, gp, ctor_sig, ct, share_sig, sh, dtor_sig, dt);
407
+ }
408
+
409
+ void crossbind_embind_register_enum(CrossbindTid enum_ty, const char* name, size_t size, bool is_signed, int /*policy dropped*/) {
410
+ _embind_register_enum(enum_ty, name, size, is_signed);
411
+ }
412
+ void crossbind_embind_register_enum_value(CrossbindTid enum_ty, const char* value_name, int value) {
413
+ _embind_register_enum_value(enum_ty, value_name, value);
414
+ }
415
+
416
+ void crossbind_embind_register_value_object(CrossbindTid struct_ty, const char* name,
417
+ const char* ctor_sig, CrossbindFn ctor, const char* dtor_sig, CrossbindFn dtor) {
418
+ auto ct = wrapInvoker(std::string(name) + "_ctor", ctor_sig, ctor);
419
+ auto dt = wrapInvoker(std::string(name) + "_dtor", dtor_sig, dtor);
420
+ _embind_register_value_object(struct_ty, name, ctor_sig, ct, dtor_sig, dt);
421
+ }
422
+ void crossbind_embind_register_value_object_field(CrossbindTid struct_ty, const char* field_name,
423
+ CrossbindTid getter_ret, const char* getter_sig, CrossbindFn getter, void* getter_ctx,
424
+ CrossbindTid setter_arg, const char* setter_sig, CrossbindFn setter, void* setter_ctx) {
425
+ auto g = wrapInvoker(std::string(field_name) + "_get", getter_sig, getter);
426
+ auto s = wrapInvoker(std::string(field_name) + "_set", setter_sig, setter);
427
+ _embind_register_value_object_field(struct_ty, field_name, getter_ret, getter_sig, g, getter_ctx, setter_arg, setter_sig, s, setter_ctx);
428
+ }
429
+ void crossbind_embind_finalize_value_object(CrossbindTid struct_ty) {
430
+ _embind_finalize_value_object(struct_ty);
431
+ }
432
+
433
+ } // extern "C"