@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/adapters/web.cpp
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
// WEB adapter: stable flat C-ABI -> emscripten embind (passthrough, 10-param identical), plus
|
|
2
|
+
// the host-specific typeid getters. Registration fires from the producer crate's init-array
|
|
3
|
+
// (embind_rs::bindings!), so there is no C++ trigger here - the shim is getters + passthroughs.
|
|
4
|
+
#include "../include/crossbind_embind.h"
|
|
5
|
+
#include <emscripten/bind.h>
|
|
6
|
+
#include <emscripten/em_js.h>
|
|
7
|
+
#include <emscripten/val.h>
|
|
8
|
+
#include <cstdint>
|
|
9
|
+
#include <cstdlib>
|
|
10
|
+
#include <cstring>
|
|
11
|
+
#include <optional>
|
|
12
|
+
#include <string>
|
|
13
|
+
#include <typeinfo>
|
|
14
|
+
#include <unordered_set>
|
|
15
|
+
|
|
16
|
+
// Producer error escape (embind_rs::raise_err): decode the [u32 len][bytes] message, free it,
|
|
17
|
+
// and throw a real JS Error from inside the invoker - it propagates to the JS caller unchanged.
|
|
18
|
+
EM_JS(void, crossbind_embind_raise_error, (uint8_t* w), {
|
|
19
|
+
const len = HEAPU32[w >> 2];
|
|
20
|
+
const msg = UTF8ToString(w + 4, len);
|
|
21
|
+
_free(w);
|
|
22
|
+
throw new Error(msg);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Producer sig chars that emscripten's dynCall alphabet lacks: 's' (a jsi-marshalling hint;
|
|
26
|
+
// the wasm string wire is a real pointer) -> 'p', and 'u' (unsigned 64 - signedness lives in
|
|
27
|
+
// the registered typeid) -> 'j'. Translated copies are one-time registration data, leaked.
|
|
28
|
+
static const char* sigForWasm(const char* sig) {
|
|
29
|
+
if (!sig || (!std::strchr(sig, 's') && !std::strchr(sig, 'u'))) return sig;
|
|
30
|
+
char* copy = strdup(sig);
|
|
31
|
+
for (char* c = copy; *c; ++c) {
|
|
32
|
+
if (*c == 's') *c = 'p';
|
|
33
|
+
else if (*c == 'u') *c = 'j';
|
|
34
|
+
}
|
|
35
|
+
return copy;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
extern "C" {
|
|
39
|
+
|
|
40
|
+
// Primitive type keys: bridge to the builtin typeids emscripten registered.
|
|
41
|
+
const void* crossbind_tid_int() { return &typeid(int); }
|
|
42
|
+
const void* crossbind_tid_double() { return &typeid(double); }
|
|
43
|
+
const void* crossbind_tid_bool() { return &typeid(bool); }
|
|
44
|
+
const void* crossbind_tid_void() { return &typeid(void); }
|
|
45
|
+
const void* crossbind_tid_std_string() { return &typeid(std::string); }
|
|
46
|
+
const void* crossbind_tid_int64() { return &typeid(int64_t); }
|
|
47
|
+
const void* crossbind_tid_uint64() { return &typeid(uint64_t); }
|
|
48
|
+
// Adapter-local identities, NOT &typeid(std::optional<T>): linked C++ bindings may register
|
|
49
|
+
// the real optional typeids themselves, and these types never interop with C++ signatures.
|
|
50
|
+
const void* crossbind_tid_optional_int() { static const char t = 0; return &t; }
|
|
51
|
+
const void* crossbind_tid_optional_double() { static const char t = 0; return &t; }
|
|
52
|
+
const void* crossbind_tid_optional_bool() { static const char t = 0; return &t; }
|
|
53
|
+
const void* crossbind_tid_optional_string() { static const char t = 0; return &t; }
|
|
54
|
+
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Optional wire on wasm is an EM_VAL handle (None = the reserved undefined constant from
|
|
58
|
+
// emscripten's own header). Some-values become handles through Emval.toHandle; the registered
|
|
59
|
+
// optional type's fromWireType does toValue + decref, so the refcount stays balanced.
|
|
60
|
+
EM_JS(void*, crossbind_emval_from_number_js, (double v), { return Emval.toHandle(v); });
|
|
61
|
+
EM_JS(void*, crossbind_emval_from_bool_js, (int v), { return Emval.toHandle(!!v); });
|
|
62
|
+
EM_JS(void*, crossbind_emval_from_string_js, (uint8_t* w), {
|
|
63
|
+
const len = HEAPU32[w >> 2];
|
|
64
|
+
const s = UTF8ToString(w + 4, len);
|
|
65
|
+
_free(w);
|
|
66
|
+
return Emval.toHandle(s);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
extern "C" {
|
|
70
|
+
void* crossbind_emval_take_value(CrossbindTid inner, const void* argv) {
|
|
71
|
+
if (inner == crossbind_tid_int()) return crossbind_emval_from_number_js((double)*(const int32_t*)argv);
|
|
72
|
+
if (inner == crossbind_tid_double()) return crossbind_emval_from_number_js(*(const double*)argv);
|
|
73
|
+
if (inner == crossbind_tid_bool()) return crossbind_emval_from_bool_js((int)*(const uint8_t*)argv);
|
|
74
|
+
return crossbind_emval_from_string_js(*(uint8_t* const*)argv); // std::string wire pointer
|
|
75
|
+
}
|
|
76
|
+
void* crossbind_emval_undefined() {
|
|
77
|
+
return (void*)emscripten::internal::_EMVAL_UNDEFINED;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Optional PARAMETERS: embind hands the invoker an EM_VAL handle; these readers consume its
|
|
81
|
+
// reference (take_ownership) and yield the native repr, 0 for undefined/null.
|
|
82
|
+
int crossbind_emval_opt_i32(void* h, int32_t* out) {
|
|
83
|
+
emscripten::val v = emscripten::val::take_ownership((emscripten::EM_VAL)h);
|
|
84
|
+
if (v.isUndefined() || v.isNull()) return 0;
|
|
85
|
+
*out = v.as<int32_t>();
|
|
86
|
+
return 1;
|
|
87
|
+
}
|
|
88
|
+
int crossbind_emval_opt_f64(void* h, double* out) {
|
|
89
|
+
emscripten::val v = emscripten::val::take_ownership((emscripten::EM_VAL)h);
|
|
90
|
+
if (v.isUndefined() || v.isNull()) return 0;
|
|
91
|
+
*out = v.as<double>();
|
|
92
|
+
return 1;
|
|
93
|
+
}
|
|
94
|
+
int crossbind_emval_opt_bool(void* h, uint8_t* out) {
|
|
95
|
+
emscripten::val v = emscripten::val::take_ownership((emscripten::EM_VAL)h);
|
|
96
|
+
if (v.isUndefined() || v.isNull()) return 0;
|
|
97
|
+
*out = v.as<bool>() ? 1 : 0;
|
|
98
|
+
return 1;
|
|
99
|
+
}
|
|
100
|
+
int crossbind_emval_opt_string(void* h, uint8_t** out) {
|
|
101
|
+
emscripten::val v = emscripten::val::take_ownership((emscripten::EM_VAL)h);
|
|
102
|
+
if (v.isUndefined() || v.isNull()) return 0;
|
|
103
|
+
std::string s = v.as<std::string>();
|
|
104
|
+
uint8_t* w = (uint8_t*)malloc(4 + s.size());
|
|
105
|
+
uint32_t len = (uint32_t)s.size();
|
|
106
|
+
std::memcpy(w, &len, 4);
|
|
107
|
+
std::memcpy(w + 4, s.data(), s.size());
|
|
108
|
+
*out = w;
|
|
109
|
+
return 1;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// JSON-value bridge (serde_json::Value): deep copy through the host's canonical JSON codec.
|
|
113
|
+
// Wire is the usual [u32 len][bytes]; handles cross owned (incref on give, take_ownership on
|
|
114
|
+
// receive), matching the Option emval discipline above.
|
|
115
|
+
const void* crossbind_tid_emval() { return &typeid(emscripten::val); }
|
|
116
|
+
|
|
117
|
+
void* crossbind_emval_json_to_handle(uint8_t* w) {
|
|
118
|
+
const uint32_t len = *(const uint32_t*)w;
|
|
119
|
+
std::string s((const char*)(w + 4), len);
|
|
120
|
+
free(w);
|
|
121
|
+
emscripten::val v = emscripten::val::global("JSON").call<emscripten::val>("parse", emscripten::val::u8string(s.c_str()));
|
|
122
|
+
emscripten::EM_VAL h = v.as_handle();
|
|
123
|
+
emscripten::internal::_emval_incref(h);
|
|
124
|
+
return (void*)h;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
uint8_t* crossbind_emval_handle_to_json(void* h) {
|
|
128
|
+
emscripten::val v = emscripten::val::take_ownership((emscripten::EM_VAL)h);
|
|
129
|
+
emscripten::val j = emscripten::val::global("JSON").call<emscripten::val>("stringify", v);
|
|
130
|
+
// JSON.stringify(undefined / a bare function) yields undefined: cross as null.
|
|
131
|
+
std::string s = j.isUndefined() ? "null" : j.as<std::string>();
|
|
132
|
+
uint8_t* w = (uint8_t*)malloc(4 + s.size());
|
|
133
|
+
uint32_t len = (uint32_t)s.size();
|
|
134
|
+
std::memcpy(w, &len, 4);
|
|
135
|
+
std::memcpy(w + 4, s.data(), s.size());
|
|
136
|
+
return w;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// emscripten's real registration functions (extern "C" in bind.h).
|
|
140
|
+
void _embind_register_class(CrossbindTid, CrossbindTid, CrossbindTid, CrossbindTid, const char*, CrossbindFn, const char*, CrossbindFn, const char*, CrossbindFn, const char*, const char*, CrossbindFn);
|
|
141
|
+
void _embind_register_class_constructor(CrossbindTid, unsigned, const CrossbindTid*, const char*, CrossbindFn, CrossbindFn);
|
|
142
|
+
void _embind_register_class_function(CrossbindTid, const char*, unsigned, const CrossbindTid*, const char*, CrossbindFn, void*, unsigned, bool, bool);
|
|
143
|
+
void _embind_register_class_class_function(CrossbindTid, const char*, unsigned, const CrossbindTid*, const char*, CrossbindFn, CrossbindFn, bool, bool);
|
|
144
|
+
void _embind_register_function(const char*, unsigned, const CrossbindTid*, const char*, CrossbindFn, CrossbindFn, bool, bool);
|
|
145
|
+
void _embind_register_optional(CrossbindTid, CrossbindTid);
|
|
146
|
+
void _embind_register_smart_ptr(CrossbindTid, CrossbindTid, const char*, emscripten::sharing_policy, const char*, CrossbindFn, const char*, CrossbindFn, const char*, CrossbindFn, const char*, CrossbindFn);
|
|
147
|
+
void _embind_register_enum(CrossbindTid, const char*, size_t, bool, int);
|
|
148
|
+
void _embind_register_enum_value(CrossbindTid, const char*, int);
|
|
149
|
+
void _embind_register_value_object(CrossbindTid, const char*, const char*, CrossbindFn, const char*, CrossbindFn);
|
|
150
|
+
void _embind_register_value_object_field(CrossbindTid, const char*, CrossbindTid, const char*, CrossbindFn, void*, CrossbindTid, const char*, CrossbindFn, void*);
|
|
151
|
+
void _embind_finalize_value_object(CrossbindTid);
|
|
152
|
+
|
|
153
|
+
void crossbind_embind_register_class(CrossbindTid a, CrossbindTid b, CrossbindTid c, CrossbindTid d, const char* e, CrossbindFn f, const char* g, CrossbindFn h, const char* i, CrossbindFn j, const char* k, const char* l, CrossbindFn m) {
|
|
154
|
+
_embind_register_class(a, b, c, d, e, f, g, h, i, j, k, l, m);
|
|
155
|
+
}
|
|
156
|
+
void crossbind_embind_register_class_constructor(CrossbindTid a, unsigned b, const CrossbindTid* c, const char* d, CrossbindFn e, CrossbindFn f) {
|
|
157
|
+
_embind_register_class_constructor(a, b, c, sigForWasm(d), e, f);
|
|
158
|
+
}
|
|
159
|
+
void crossbind_embind_register_class_function(CrossbindTid a, const char* b, unsigned c, const CrossbindTid* d, const char* e, CrossbindFn f, void* g, unsigned h, bool i, bool j) {
|
|
160
|
+
_embind_register_class_function(a, b, c, d, sigForWasm(e), f, g, h, i, j); // 10-param, identical
|
|
161
|
+
}
|
|
162
|
+
void crossbind_embind_register_class_class_function(CrossbindTid a, const char* b, unsigned c, const CrossbindTid* d, const char* e, CrossbindFn f, CrossbindFn g, bool h, bool i) {
|
|
163
|
+
_embind_register_class_class_function(a, b, c, d, sigForWasm(e), f, g, h, i);
|
|
164
|
+
}
|
|
165
|
+
void crossbind_embind_register_function(const char* a, unsigned b, const CrossbindTid* c, const char* d, CrossbindFn e, CrossbindFn f, bool g, bool h) {
|
|
166
|
+
_embind_register_function(a, b, c, sigForWasm(d), e, f, g, h);
|
|
167
|
+
}
|
|
168
|
+
// Idempotent: every Rust archive in the link may ask for the same optional type.
|
|
169
|
+
void crossbind_embind_register_optional(CrossbindTid opt, CrossbindTid inner) {
|
|
170
|
+
static std::unordered_set<CrossbindTid> registered;
|
|
171
|
+
if (!registered.insert(opt).second) return;
|
|
172
|
+
_embind_register_optional(opt, inner);
|
|
173
|
+
}
|
|
174
|
+
void crossbind_embind_register_smart_ptr(CrossbindTid a, CrossbindTid b, const char* c, int d, const char* e, CrossbindFn f, const char* g, CrossbindFn h, const char* i, CrossbindFn j, const char* k, CrossbindFn l) {
|
|
175
|
+
_embind_register_smart_ptr(a, b, c, (emscripten::sharing_policy)d, e, f, g, h, i, j, k, l);
|
|
176
|
+
}
|
|
177
|
+
void crossbind_embind_register_enum(CrossbindTid a, const char* b, size_t c, bool d, int e) {
|
|
178
|
+
_embind_register_enum(a, b, c, d, e);
|
|
179
|
+
}
|
|
180
|
+
void crossbind_embind_register_enum_value(CrossbindTid a, const char* b, int c) {
|
|
181
|
+
_embind_register_enum_value(a, b, c);
|
|
182
|
+
}
|
|
183
|
+
void crossbind_embind_register_value_object(CrossbindTid a, const char* b, const char* c, CrossbindFn d, const char* e, CrossbindFn f) {
|
|
184
|
+
_embind_register_value_object(a, b, c, d, e, f);
|
|
185
|
+
}
|
|
186
|
+
void crossbind_embind_register_value_object_field(CrossbindTid a, const char* b, CrossbindTid c, const char* d, CrossbindFn e, void* f, CrossbindTid g, const char* h, CrossbindFn i, void* j) {
|
|
187
|
+
_embind_register_value_object_field(a, b, c, sigForWasm(d), e, f, g, sigForWasm(h), i, j);
|
|
188
|
+
}
|
|
189
|
+
void crossbind_embind_finalize_value_object(CrossbindTid a) {
|
|
190
|
+
_embind_finalize_value_object(a);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// emscripten's InitFunc constructor runs the init immediately and registers it for workers.
|
|
194
|
+
// Leaked on purpose: it lives for the program.
|
|
195
|
+
void crossbind_embind_register_bindings(void (*init)()) {
|
|
196
|
+
new emscripten::internal::InitFunc(init);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ---- live JS value hooks (JsValue / JsFunction) ----
|
|
202
|
+
// EM_JS bodies over embind's own Emval handle table (visible in EM_JS scope, like the Option
|
|
203
|
+
// helpers above). Handles are i32 table indexes on wasm32, so argv reads via HEAPU32.
|
|
204
|
+
EM_JS(void, crossbind_v_ref, (void* h), { __emval_incref(h); });
|
|
205
|
+
EM_JS(void, crossbind_v_unref, (void* h), { __emval_decref(h); });
|
|
206
|
+
EM_JS(void*, crossbind_v_from_f64, (double v), { return Emval.toHandle(v); });
|
|
207
|
+
EM_JS(void*, crossbind_v_from_bool, (int v), { return Emval.toHandle(!!v); });
|
|
208
|
+
EM_JS(void*, crossbind_v_from_str, (uint8_t* w), {
|
|
209
|
+
const len = HEAPU32[w >> 2];
|
|
210
|
+
const s = UTF8ToString(w + 4, len);
|
|
211
|
+
_free(w);
|
|
212
|
+
return Emval.toHandle(s);
|
|
213
|
+
});
|
|
214
|
+
EM_JS(void*, crossbind_v_get_prop, (void* h, uint8_t* w), {
|
|
215
|
+
const len = HEAPU32[w >> 2];
|
|
216
|
+
const k = UTF8ToString(w + 4, len);
|
|
217
|
+
_free(w);
|
|
218
|
+
return Emval.toHandle(Emval.toValue(h)[k]);
|
|
219
|
+
});
|
|
220
|
+
EM_JS(void, crossbind_v_set_prop, (void* h, uint8_t* w, void* v), {
|
|
221
|
+
const len = HEAPU32[w >> 2];
|
|
222
|
+
const k = UTF8ToString(w + 4, len);
|
|
223
|
+
_free(w);
|
|
224
|
+
Emval.toValue(h)[k] = Emval.toValue(v);
|
|
225
|
+
});
|
|
226
|
+
EM_JS(int, crossbind_v_kind, (void* h), {
|
|
227
|
+
const v = Emval.toValue(h);
|
|
228
|
+
if (v === null || v === undefined) return 0;
|
|
229
|
+
const t = typeof v;
|
|
230
|
+
if (t === 'boolean') return 1;
|
|
231
|
+
if (t === 'number') return 2;
|
|
232
|
+
if (t === 'string') return 3;
|
|
233
|
+
if (t === 'function') return 4;
|
|
234
|
+
return 5;
|
|
235
|
+
});
|
|
236
|
+
EM_JS(double, crossbind_v_as_f64, (void* h), { return Number(Emval.toValue(h)); });
|
|
237
|
+
EM_JS(int, crossbind_v_as_bool, (void* h), { return Emval.toValue(h) ? 1 : 0; });
|
|
238
|
+
EM_JS(uint8_t*, crossbind_v_as_str, (void* h), {
|
|
239
|
+
const s = String(Emval.toValue(h));
|
|
240
|
+
const len = lengthBytesUTF8(s);
|
|
241
|
+
const w = _malloc(4 + len);
|
|
242
|
+
HEAPU32[w >> 2] = len;
|
|
243
|
+
stringToUTF8(s, w + 4, len + 1);
|
|
244
|
+
return w;
|
|
245
|
+
});
|
|
246
|
+
EM_JS(void*, crossbind_v_call, (void* f, unsigned argc, void* argv), {
|
|
247
|
+
try {
|
|
248
|
+
const fn = Emval.toValue(f);
|
|
249
|
+
const args = [];
|
|
250
|
+
for (let i = 0; i < argc; i += 1) args.push(Emval.toValue(HEAPU32[(argv >> 2) + i]));
|
|
251
|
+
return Emval.toHandle(fn(...args));
|
|
252
|
+
} catch (e) {
|
|
253
|
+
globalThis.__crossbind_cb_err = String((e && e.message) || e);
|
|
254
|
+
return 0;
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
EM_JS(uint8_t*, crossbind_v_cb_err_take, (), {
|
|
258
|
+
const s = globalThis.__crossbind_cb_err;
|
|
259
|
+
if (s === undefined) return 0;
|
|
260
|
+
delete globalThis.__crossbind_cb_err;
|
|
261
|
+
const len = lengthBytesUTF8(s);
|
|
262
|
+
const w = _malloc(4 + len);
|
|
263
|
+
HEAPU32[w >> 2] = len;
|
|
264
|
+
stringToUTF8(s, w + 4, len + 1);
|
|
265
|
+
return w;
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
extern "C" void* crossbind_v_undef() { return (void*)emscripten::internal::_EMVAL_UNDEFINED; }
|
package/crate/Cargo.lock
ADDED