@crossbind/core-embind-jsi 2.0.0-beta.60 → 2.0.0-beta.63
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/LICENSE +76 -0
- package/README.md +1 -1
- package/cpp/src/crossbind/direct_reads.h +30 -0
- package/cpp/src/emscripten/bind.cpp +133 -175
- package/cpp/src/emscripten/bind.h +57 -77
- package/cpp/src/emscripten/val.h +57 -146
- package/cpp/src/emscripten/wire.h +169 -47
- package/js/embind.js +128 -774
- package/package.json +9 -1
|
@@ -35,12 +35,6 @@ namespace emscripten {
|
|
|
35
35
|
facebook::jsi::Runtime* jsRuntime = nullptr;
|
|
36
36
|
namespace internal {
|
|
37
37
|
|
|
38
|
-
// Dynamic heap window registration. JS side keeps multiple ArrayBuffer
|
|
39
|
-
// windows over native memory; this ensures every pointer we hand to JS
|
|
40
|
-
// is covered by at least one window before being read/written there.
|
|
41
|
-
void ensureWindowFor(uint64_t ptr);
|
|
42
|
-
void registerHeapWindow(uint64_t offset);
|
|
43
|
-
|
|
44
38
|
template<typename T>
|
|
45
39
|
struct Bugra {
|
|
46
40
|
static jsi::Value toValue(T rawValue);
|
|
@@ -58,21 +52,17 @@ namespace emscripten {
|
|
|
58
52
|
|
|
59
53
|
template<typename T>
|
|
60
54
|
jsi::Value Bugra<T>::toValue(T* rawValue) {
|
|
61
|
-
|
|
62
|
-
ensureWindowFor(ptr);
|
|
63
|
-
return jsi::BigInt::fromUint64(*jsRuntime, ptr);
|
|
55
|
+
return jsi::BigInt::fromUint64(*jsRuntime, reinterpret_cast<uint64_t>(rawValue));
|
|
64
56
|
}
|
|
65
57
|
|
|
66
58
|
template<typename T>
|
|
67
59
|
jsi::Value Bugra<T>::toValue(T** rawValue) {
|
|
68
|
-
|
|
69
|
-
ensureWindowFor(ptr);
|
|
70
|
-
return jsi::BigInt::fromUint64(*jsRuntime, ptr);
|
|
60
|
+
return jsi::BigInt::fromUint64(*jsRuntime, reinterpret_cast<uint64_t>(rawValue));
|
|
71
61
|
}
|
|
72
62
|
|
|
73
63
|
template<typename T>
|
|
74
64
|
T Bugra<T>::fromValue(jsi::Value& rawValue) {
|
|
75
|
-
return reinterpret_cast<T>(
|
|
65
|
+
return reinterpret_cast<T>(bigIntUint64(*jsRuntime, rawValue));
|
|
76
66
|
}
|
|
77
67
|
|
|
78
68
|
template<> inline jsi::Value Bugra<bool>::toValue(bool rawValue) {
|
|
@@ -100,14 +90,14 @@ namespace emscripten {
|
|
|
100
90
|
return jsi::BigInt::fromInt64(*jsRuntime, rawValue);
|
|
101
91
|
}
|
|
102
92
|
template<> inline int64_t Bugra<int64_t>::fromValue(jsi::Value& rawValue) {
|
|
103
|
-
return
|
|
93
|
+
return bigIntInt64(*jsRuntime, rawValue);
|
|
104
94
|
}
|
|
105
95
|
|
|
106
96
|
template<> inline jsi::Value Bugra<uint64_t>::toValue(uint64_t rawValue) {
|
|
107
97
|
return jsi::BigInt::fromUint64(*jsRuntime, rawValue);
|
|
108
98
|
}
|
|
109
99
|
template<> inline uint64_t Bugra<uint64_t>::fromValue(jsi::Value& rawValue) {
|
|
110
|
-
return
|
|
100
|
+
return bigIntUint64(*jsRuntime, rawValue);
|
|
111
101
|
}
|
|
112
102
|
|
|
113
103
|
template<> inline jsi::Value Bugra<std::string>::toValue(std::string rawValue) {
|
|
@@ -143,7 +133,7 @@ namespace emscripten {
|
|
|
143
133
|
}
|
|
144
134
|
|
|
145
135
|
template<> inline EM_VAL Bugra<EM_VAL>::fromValue(jsi::Value& rawValue) {
|
|
146
|
-
return reinterpret_cast<EM_VAL>(
|
|
136
|
+
return reinterpret_cast<EM_VAL>(bigIntUint64(*jsRuntime, rawValue));
|
|
147
137
|
// return reinterpret_cast<EM_VAL>((uint32_t) rawValue.getNumber());
|
|
148
138
|
}
|
|
149
139
|
|
|
@@ -151,111 +141,155 @@ namespace emscripten {
|
|
|
151
141
|
return jsi::BigInt::fromUint64(*jsRuntime, reinterpret_cast<uint64_t>(rawValue));
|
|
152
142
|
}
|
|
153
143
|
|
|
154
|
-
|
|
155
|
-
|
|
144
|
+
// JS helpers and argument type arrays, looked up once per runtime. Every new runtime gets
|
|
145
|
+
// a new cache and the old one is dropped without being destroyed: it holds JSI values, and
|
|
146
|
+
// the runtime they belong to may already be gone.
|
|
147
|
+
struct RuntimeCache {
|
|
148
|
+
std::unordered_map<const char*, jsi::Function> helpers;
|
|
149
|
+
std::unordered_map<const TYPEID*, jsi::Array> typeLists;
|
|
150
|
+
};
|
|
151
|
+
static RuntimeCache* runtimeCache = new RuntimeCache();
|
|
152
|
+
|
|
153
|
+
// The name must be a string literal: the cache is keyed by its address.
|
|
154
|
+
static jsi::Function& helper(const char* name) {
|
|
155
|
+
auto found = runtimeCache->helpers.find(name);
|
|
156
|
+
if (found != runtimeCache->helpers.end()) {
|
|
157
|
+
return found->second;
|
|
158
|
+
}
|
|
159
|
+
return runtimeCache->helpers.emplace(name, jsRuntime->global().getPropertyAsFunction(*jsRuntime, name)).first->second;
|
|
156
160
|
}
|
|
157
161
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
+
// An argument type list is a static array per signature, so its address identifies it.
|
|
163
|
+
static jsi::Value typeList(const TYPEID argTypes[], unsigned argCount) {
|
|
164
|
+
auto found = runtimeCache->typeLists.find(argTypes);
|
|
165
|
+
if (found == runtimeCache->typeLists.end()) {
|
|
166
|
+
found = runtimeCache->typeLists.emplace(argTypes, Bugra<TYPEID>::toArrayValue(std::vector<TYPEID>(argTypes, argTypes + argCount))).first;
|
|
167
|
+
}
|
|
168
|
+
return jsi::Value(*jsRuntime, found->second);
|
|
162
169
|
}
|
|
163
170
|
|
|
171
|
+
// Handles up to _EMVAL_FALSE are fixed in JS (0 is a moved-from val) and never counted.
|
|
172
|
+
static bool isCounted(EM_VAL value) {
|
|
173
|
+
return reinterpret_cast<uintptr_t>(value) > _EMVAL_FALSE;
|
|
174
|
+
}
|
|
164
175
|
|
|
165
176
|
|
|
166
177
|
void _emval_register_symbol(const char* value) {
|
|
167
|
-
|
|
178
|
+
helper("__emval_register_symbol").call(
|
|
168
179
|
*jsRuntime,
|
|
169
180
|
Bugra<const char*>::toValue(value)
|
|
170
181
|
);
|
|
171
182
|
}
|
|
172
183
|
|
|
184
|
+
// A val can outlive its runtime (the host clears jsRuntime when it goes away, and
|
|
185
|
+
// thread-local vals are destroyed inside exit()); its handle then has nothing to release.
|
|
173
186
|
void _emval_incref(EM_VAL value) {
|
|
174
|
-
jsRuntime
|
|
187
|
+
if (jsRuntime == nullptr || !isCounted(value)) return;
|
|
188
|
+
helper("__emval_incref").call(
|
|
175
189
|
*jsRuntime,
|
|
176
190
|
Bugra<EM_VAL>::toValue(value)
|
|
177
191
|
);
|
|
178
192
|
}
|
|
179
193
|
void _emval_decref(EM_VAL value) {
|
|
180
|
-
jsRuntime
|
|
194
|
+
if (jsRuntime == nullptr || !isCounted(value)) return;
|
|
195
|
+
helper("__emval_decref").call(
|
|
181
196
|
*jsRuntime,
|
|
182
197
|
Bugra<EM_VAL>::toValue(value)
|
|
183
198
|
);
|
|
184
199
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
200
|
+
// Runs from DestructorsRunner's destructor, where a thrown exception would end the process.
|
|
201
|
+
void _emval_run_destructors() {
|
|
202
|
+
if (jsRuntime == nullptr) return;
|
|
203
|
+
try {
|
|
204
|
+
helper("__emval_run_destructors").call(*jsRuntime);
|
|
205
|
+
} catch (const std::exception& error) {
|
|
206
|
+
fprintf(stderr, "crossbind: releasing a converted value failed: %s\n", error.what());
|
|
207
|
+
}
|
|
190
208
|
}
|
|
191
209
|
EM_VAL _emval_new_array() {
|
|
192
|
-
auto response =
|
|
210
|
+
auto response = helper("__emval_new_array").call(
|
|
193
211
|
*jsRuntime
|
|
194
212
|
);
|
|
195
213
|
return Bugra<EM_VAL>::fromValue(response);
|
|
196
214
|
}
|
|
197
215
|
EM_VAL _emval_new_array_from_memory_view(EM_VAL mv) {
|
|
198
|
-
auto response =
|
|
216
|
+
auto response = helper("__emval_new_array_from_memory_view").call(
|
|
199
217
|
*jsRuntime,
|
|
200
218
|
Bugra<EM_VAL>::toValue(mv)
|
|
201
219
|
);
|
|
202
220
|
return Bugra<EM_VAL>::fromValue(response);
|
|
203
221
|
}
|
|
204
222
|
EM_VAL _emval_new_object() {
|
|
205
|
-
auto response =
|
|
223
|
+
auto response = helper("__emval_new_object").call(
|
|
206
224
|
*jsRuntime
|
|
207
225
|
);
|
|
208
226
|
return Bugra<EM_VAL>::fromValue(response);
|
|
209
227
|
}
|
|
210
228
|
EM_VAL _emval_new_cstring(const char* value) {
|
|
211
|
-
auto response =
|
|
229
|
+
auto response = helper("__emval_new_cstring").call(
|
|
212
230
|
*jsRuntime,
|
|
213
231
|
Bugra<const char*>::toValue(value)
|
|
214
232
|
);
|
|
215
233
|
return Bugra<EM_VAL>::fromValue(response);
|
|
216
234
|
}
|
|
217
235
|
EM_VAL _emval_new_u8string(const char* value) {
|
|
218
|
-
auto response =
|
|
236
|
+
auto response = helper("__emval_new_u8string").call(
|
|
219
237
|
*jsRuntime,
|
|
220
238
|
Bugra<const char*>::toValue(value)
|
|
221
239
|
);
|
|
222
240
|
return Bugra<EM_VAL>::fromValue(response);
|
|
223
241
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
auto response = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_take_value").call(
|
|
242
|
+
EM_VAL _emval_new_u16string(const char16_t* value) {
|
|
243
|
+
auto response = helper("__emval_new_u16string").call(
|
|
227
244
|
*jsRuntime,
|
|
228
|
-
|
|
229
|
-
Bugra<EM_VAR_ARGS>::toValue(argv)
|
|
245
|
+
jsi::String::createFromUtf8(*jsRuntime, utf16ToUtf8(value))
|
|
230
246
|
);
|
|
231
247
|
return Bugra<EM_VAL>::fromValue(response);
|
|
232
248
|
}
|
|
233
|
-
EM_VAL
|
|
234
|
-
auto response =
|
|
249
|
+
EM_VAL _emval_take_value(TYPEID type, const jsi::Value& value) {
|
|
250
|
+
auto response = helper("__emval_take_value").call(
|
|
235
251
|
*jsRuntime,
|
|
252
|
+
Bugra<TYPEID>::toValue(type),
|
|
253
|
+
value
|
|
254
|
+
);
|
|
255
|
+
return Bugra<EM_VAL>::fromValue(response);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// The JS half receives its fixed arguments first, then each argument's wire value.
|
|
259
|
+
template<typename... Leading>
|
|
260
|
+
jsi::Value callWithWireValues(const char* name, unsigned argCount, jsi::Value* argv, Leading&&... leading) {
|
|
261
|
+
std::vector<jsi::Value> args;
|
|
262
|
+
args.reserve(sizeof...(Leading) + argCount);
|
|
263
|
+
(args.emplace_back(std::forward<Leading>(leading)), ...);
|
|
264
|
+
for (unsigned i = 0; i < argCount; ++i) {
|
|
265
|
+
args.push_back(std::move(argv[i]));
|
|
266
|
+
}
|
|
267
|
+
return helper(name).call(*jsRuntime, static_cast<const jsi::Value*>(args.data()), args.size());
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
EM_VAL _emval_new(EM_VAL value, unsigned argCount, const TYPEID argTypes[], jsi::Value* argv) {
|
|
271
|
+
auto response = callWithWireValues("__emval_new", argCount, argv,
|
|
236
272
|
Bugra<EM_VAL>::toValue(value),
|
|
237
273
|
Bugra<unsigned>::toValue(argCount),
|
|
238
|
-
|
|
239
|
-
Bugra<EM_VAR_ARGS>::toValue(argv)
|
|
240
|
-
);
|
|
274
|
+
typeList(argTypes, argCount));
|
|
241
275
|
return Bugra<EM_VAL>::fromValue(response);
|
|
242
276
|
}
|
|
243
277
|
EM_VAL _emval_get_global(const char* name) {
|
|
244
|
-
auto response =
|
|
278
|
+
auto response = helper("__emval_get_global").call(
|
|
245
279
|
*jsRuntime,
|
|
246
280
|
Bugra<const char*>::toValue(name)
|
|
247
281
|
);
|
|
248
282
|
return Bugra<EM_VAL>::fromValue(response);
|
|
249
283
|
}
|
|
250
284
|
EM_VAL _emval_get_module_property(const char* name) {
|
|
251
|
-
auto response =
|
|
285
|
+
auto response = helper("__emval_get_module_property").call(
|
|
252
286
|
*jsRuntime,
|
|
253
287
|
Bugra<const char*>::toValue(name)
|
|
254
288
|
);
|
|
255
289
|
return Bugra<EM_VAL>::fromValue(response);
|
|
256
290
|
}
|
|
257
291
|
EM_VAL _emval_get_property(EM_VAL object, EM_VAL key) {
|
|
258
|
-
auto response =
|
|
292
|
+
auto response = helper("__emval_get_property").call(
|
|
259
293
|
*jsRuntime,
|
|
260
294
|
Bugra<EM_VAL>::toValue(object),
|
|
261
295
|
Bugra<EM_VAL>::toValue(key)
|
|
@@ -263,24 +297,22 @@ namespace emscripten {
|
|
|
263
297
|
return Bugra<EM_VAL>::fromValue(response);
|
|
264
298
|
}
|
|
265
299
|
void _emval_set_property(EM_VAL object, EM_VAL key, EM_VAL value) {
|
|
266
|
-
|
|
300
|
+
helper("__emval_set_property").call(
|
|
267
301
|
*jsRuntime,
|
|
268
302
|
Bugra<EM_VAL>::toValue(object),
|
|
269
303
|
Bugra<EM_VAL>::toValue(key),
|
|
270
304
|
Bugra<EM_VAL>::toValue(value)
|
|
271
305
|
);
|
|
272
306
|
}
|
|
273
|
-
EM_GENERIC_WIRE_TYPE _emval_as(EM_VAL value, TYPEID returnType
|
|
274
|
-
|
|
307
|
+
EM_GENERIC_WIRE_TYPE _emval_as(EM_VAL value, TYPEID returnType) {
|
|
308
|
+
return helper("__emval_as").call(
|
|
275
309
|
*jsRuntime,
|
|
276
310
|
Bugra<EM_VAL>::toValue(value),
|
|
277
|
-
Bugra<TYPEID>::toValue(returnType)
|
|
278
|
-
Bugra<EM_DESTRUCTORS*>::toValue(destructors)
|
|
311
|
+
Bugra<TYPEID>::toValue(returnType)
|
|
279
312
|
);
|
|
280
|
-
return response;
|
|
281
313
|
}
|
|
282
314
|
int64_t _emval_as_int64(EM_VAL value, TYPEID returnType) {
|
|
283
|
-
auto response =
|
|
315
|
+
auto response = helper("__emval_as_int64").call(
|
|
284
316
|
*jsRuntime,
|
|
285
317
|
Bugra<EM_VAL>::toValue(value),
|
|
286
318
|
Bugra<TYPEID>::toValue(returnType)
|
|
@@ -288,7 +320,7 @@ namespace emscripten {
|
|
|
288
320
|
return Bugra<int64_t>::fromValue(response);
|
|
289
321
|
}
|
|
290
322
|
uint64_t _emval_as_uint64(EM_VAL value, TYPEID returnType) {
|
|
291
|
-
auto response =
|
|
323
|
+
auto response = helper("__emval_as_uint64").call(
|
|
292
324
|
*jsRuntime,
|
|
293
325
|
Bugra<EM_VAL>::toValue(value),
|
|
294
326
|
Bugra<TYPEID>::toValue(returnType)
|
|
@@ -296,7 +328,7 @@ namespace emscripten {
|
|
|
296
328
|
return Bugra<uint64_t>::fromValue(response);
|
|
297
329
|
}
|
|
298
330
|
bool _emval_equals(EM_VAL first, EM_VAL second) {
|
|
299
|
-
auto response =
|
|
331
|
+
auto response = helper("__emval_equals").call(
|
|
300
332
|
*jsRuntime,
|
|
301
333
|
Bugra<EM_VAL>::toValue(first),
|
|
302
334
|
Bugra<EM_VAL>::toValue(second)
|
|
@@ -304,7 +336,7 @@ namespace emscripten {
|
|
|
304
336
|
return Bugra<bool>::fromValue(response);
|
|
305
337
|
}
|
|
306
338
|
bool _emval_strictly_equals(EM_VAL first, EM_VAL second) {
|
|
307
|
-
auto response =
|
|
339
|
+
auto response = helper("__emval_strictly_equals").call(
|
|
308
340
|
*jsRuntime,
|
|
309
341
|
Bugra<EM_VAL>::toValue(first),
|
|
310
342
|
Bugra<EM_VAL>::toValue(second)
|
|
@@ -312,7 +344,7 @@ namespace emscripten {
|
|
|
312
344
|
return Bugra<bool>::fromValue(response);
|
|
313
345
|
}
|
|
314
346
|
bool _emval_greater_than(EM_VAL first, EM_VAL second) {
|
|
315
|
-
auto response =
|
|
347
|
+
auto response = helper("__emval_greater_than").call(
|
|
316
348
|
*jsRuntime,
|
|
317
349
|
Bugra<EM_VAL>::toValue(first),
|
|
318
350
|
Bugra<EM_VAL>::toValue(second)
|
|
@@ -320,7 +352,7 @@ namespace emscripten {
|
|
|
320
352
|
return Bugra<bool>::fromValue(response);
|
|
321
353
|
}
|
|
322
354
|
bool _emval_less_than(EM_VAL first, EM_VAL second) {
|
|
323
|
-
auto response =
|
|
355
|
+
auto response = helper("__emval_less_than").call(
|
|
324
356
|
*jsRuntime,
|
|
325
357
|
Bugra<EM_VAL>::toValue(first),
|
|
326
358
|
Bugra<EM_VAL>::toValue(second)
|
|
@@ -328,59 +360,48 @@ namespace emscripten {
|
|
|
328
360
|
return Bugra<bool>::fromValue(response);
|
|
329
361
|
}
|
|
330
362
|
bool _emval_not(EM_VAL object) {
|
|
331
|
-
auto response =
|
|
363
|
+
auto response = helper("__emval_not").call(
|
|
332
364
|
*jsRuntime,
|
|
333
365
|
Bugra<EM_VAL>::toValue(object)
|
|
334
366
|
);
|
|
335
367
|
return Bugra<bool>::fromValue(response);
|
|
336
368
|
}
|
|
337
|
-
EM_VAL _emval_call(EM_VAL value, unsigned argCount, const TYPEID argTypes[],
|
|
338
|
-
auto response =
|
|
339
|
-
*jsRuntime,
|
|
369
|
+
EM_VAL _emval_call(EM_VAL value, unsigned argCount, const TYPEID argTypes[], jsi::Value* argv) {
|
|
370
|
+
auto response = callWithWireValues("__emval_call", argCount, argv,
|
|
340
371
|
Bugra<EM_VAL>::toValue(value),
|
|
341
372
|
Bugra<unsigned>::toValue(argCount),
|
|
342
|
-
|
|
343
|
-
Bugra<EM_VAR_ARGS>::toValue(argv)
|
|
344
|
-
);
|
|
373
|
+
typeList(argTypes, argCount));
|
|
345
374
|
return Bugra<EM_VAL>::fromValue(response);
|
|
346
375
|
}
|
|
347
376
|
EM_METHOD_CALLER _emval_get_method_caller(unsigned argCount, const TYPEID argTypes[]) {
|
|
348
|
-
auto response =
|
|
377
|
+
auto response = helper("__emval_get_method_caller").call(
|
|
349
378
|
*jsRuntime,
|
|
350
379
|
Bugra<unsigned>::toValue(argCount),
|
|
351
|
-
|
|
380
|
+
typeList(argTypes, argCount)
|
|
352
381
|
);
|
|
353
382
|
return Bugra<EM_METHOD_CALLER>::fromValue(response);
|
|
354
383
|
}
|
|
355
|
-
EM_GENERIC_WIRE_TYPE _emval_call_method(EM_METHOD_CALLER caller, EM_VAL handle, const char* methodName,
|
|
356
|
-
|
|
357
|
-
*jsRuntime,
|
|
384
|
+
EM_GENERIC_WIRE_TYPE _emval_call_method(EM_METHOD_CALLER caller, EM_VAL handle, const char* methodName, unsigned argCount, jsi::Value* argv) {
|
|
385
|
+
return callWithWireValues("__emval_call_method", argCount, argv,
|
|
358
386
|
Bugra<EM_METHOD_CALLER>::toValue(caller),
|
|
359
387
|
Bugra<EM_VAL>::toValue(handle),
|
|
360
|
-
Bugra<const char*>::toValue(methodName)
|
|
361
|
-
Bugra<EM_DESTRUCTORS*>::toValue(destructors),
|
|
362
|
-
Bugra<EM_VAR_ARGS>::toValue(argv)
|
|
363
|
-
);
|
|
364
|
-
return response;
|
|
388
|
+
Bugra<const char*>::toValue(methodName));
|
|
365
389
|
}
|
|
366
|
-
void _emval_call_void_method(EM_METHOD_CALLER caller, EM_VAL handle, const char* methodName,
|
|
367
|
-
|
|
368
|
-
*jsRuntime,
|
|
390
|
+
void _emval_call_void_method(EM_METHOD_CALLER caller, EM_VAL handle, const char* methodName, unsigned argCount, jsi::Value* argv) {
|
|
391
|
+
callWithWireValues("__emval_call_void_method", argCount, argv,
|
|
369
392
|
Bugra<EM_METHOD_CALLER>::toValue(caller),
|
|
370
393
|
Bugra<EM_VAL>::toValue(handle),
|
|
371
|
-
Bugra<const char*>::toValue(methodName)
|
|
372
|
-
Bugra<EM_VAR_ARGS>::toValue(argv)
|
|
373
|
-
);
|
|
394
|
+
Bugra<const char*>::toValue(methodName));
|
|
374
395
|
}
|
|
375
396
|
EM_VAL _emval_typeof(EM_VAL value) {
|
|
376
|
-
auto response =
|
|
397
|
+
auto response = helper("__emval_typeof").call(
|
|
377
398
|
*jsRuntime,
|
|
378
399
|
Bugra<EM_VAL>::toValue(value)
|
|
379
400
|
);
|
|
380
401
|
return Bugra<EM_VAL>::fromValue(response);
|
|
381
402
|
}
|
|
382
403
|
bool _emval_instanceof(EM_VAL object, EM_VAL constructor) {
|
|
383
|
-
auto response =
|
|
404
|
+
auto response = helper("__emval_instanceof").call(
|
|
384
405
|
*jsRuntime,
|
|
385
406
|
Bugra<EM_VAL>::toValue(object),
|
|
386
407
|
Bugra<EM_VAL>::toValue(constructor)
|
|
@@ -388,21 +409,21 @@ namespace emscripten {
|
|
|
388
409
|
return Bugra<bool>::fromValue(response);
|
|
389
410
|
}
|
|
390
411
|
bool _emval_is_number(EM_VAL object) {
|
|
391
|
-
auto response =
|
|
412
|
+
auto response = helper("__emval_is_number").call(
|
|
392
413
|
*jsRuntime,
|
|
393
414
|
Bugra<EM_VAL>::toValue(object)
|
|
394
415
|
);
|
|
395
416
|
return Bugra<bool>::fromValue(response);
|
|
396
417
|
}
|
|
397
418
|
bool _emval_is_string(EM_VAL object) {
|
|
398
|
-
auto response =
|
|
419
|
+
auto response = helper("__emval_is_string").call(
|
|
399
420
|
*jsRuntime,
|
|
400
421
|
Bugra<EM_VAL>::toValue(object)
|
|
401
422
|
);
|
|
402
423
|
return Bugra<bool>::fromValue(response);
|
|
403
424
|
}
|
|
404
425
|
bool _emval_in(EM_VAL item, EM_VAL object) {
|
|
405
|
-
auto response =
|
|
426
|
+
auto response = helper("__emval_in").call(
|
|
406
427
|
*jsRuntime,
|
|
407
428
|
Bugra<EM_VAL>::toValue(item),
|
|
408
429
|
Bugra<EM_VAL>::toValue(object)
|
|
@@ -410,7 +431,7 @@ namespace emscripten {
|
|
|
410
431
|
return Bugra<bool>::fromValue(response);
|
|
411
432
|
}
|
|
412
433
|
bool _emval_delete(EM_VAL object, EM_VAL property) {
|
|
413
|
-
auto response =
|
|
434
|
+
auto response = helper("__emval_delete").call(
|
|
414
435
|
*jsRuntime,
|
|
415
436
|
Bugra<EM_VAL>::toValue(object),
|
|
416
437
|
Bugra<EM_VAL>::toValue(property)
|
|
@@ -418,7 +439,7 @@ namespace emscripten {
|
|
|
418
439
|
return Bugra<bool>::fromValue(response);
|
|
419
440
|
}
|
|
420
441
|
bool _emval_throw(EM_VAL object) {
|
|
421
|
-
|
|
442
|
+
helper("__emval_throw").call(
|
|
422
443
|
*jsRuntime,
|
|
423
444
|
Bugra<EM_VAL>::toValue(object)
|
|
424
445
|
);
|
|
@@ -920,92 +941,13 @@ namespace emscripten {
|
|
|
920
941
|
|
|
921
942
|
static InitFunc *init_funcs = nullptr;
|
|
922
943
|
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
FixedBuffer(uint64_t offset) : offset(offset) {}
|
|
926
|
-
|
|
927
|
-
size_t size() const override {
|
|
928
|
-
return UINT32_MAX;
|
|
929
|
-
}
|
|
930
|
-
uint8_t *data() override {
|
|
931
|
-
return reinterpret_cast<uint8_t *>(offset);
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
int r = 3;
|
|
935
|
-
uint64_t offset;
|
|
936
|
-
};
|
|
937
|
-
|
|
938
|
-
// Tracks every heap window currently exposed to JS. Single-threaded
|
|
939
|
-
// access assumed (all JSI traffic flows through the JS thread today).
|
|
940
|
-
static std::vector<uint64_t> registeredOffsets;
|
|
941
|
-
|
|
942
|
-
void registerHeapWindow(uint64_t offset) {
|
|
943
|
-
auto buf = std::make_shared<FixedBuffer>(offset);
|
|
944
|
-
auto arrayBuffer = facebook::jsi::ArrayBuffer(*jsRuntime, buf);
|
|
945
|
-
jsRuntime->global()
|
|
946
|
-
.getPropertyAsFunction(*jsRuntime, "__crossbind_register_heap_window")
|
|
947
|
-
.call(*jsRuntime,
|
|
948
|
-
jsi::BigInt::fromUint64(*jsRuntime, offset),
|
|
949
|
-
arrayBuffer);
|
|
950
|
-
registeredOffsets.push_back(offset);
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
void ensureWindowFor(uint64_t ptr) {
|
|
954
|
-
for (uint64_t offset : registeredOffsets) {
|
|
955
|
-
if (ptr >= offset && (ptr - offset) < UINT32_MAX) return;
|
|
956
|
-
}
|
|
957
|
-
// Center the new window on `ptr` so nearby allocations also land in it.
|
|
958
|
-
uint64_t newOffset = (ptr > UINT32_MAX / 2)
|
|
959
|
-
? (ptr - UINT32_MAX / 2)
|
|
960
|
-
: 0;
|
|
961
|
-
registerHeapWindow(newOffset);
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
|
|
944
|
+
// Values cross already converted (toWireType2 / fromWireType2), so JS never reads native
|
|
945
|
+
// memory and no ArrayBuffer over it exists: Electron's V8 memory cage refuses those.
|
|
965
946
|
EMSCRIPTEN_KEEPALIVE void _embind_initialize_bindings(jsi::Runtime& rt, std::string path) {
|
|
966
947
|
CROSSBIND_DATA_PATH = path;
|
|
967
948
|
Crossbind::setEnv("CROSSBIND_DATA_PATH", path, false);
|
|
968
949
|
jsRuntime = &rt;
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
// Initial seed windows: anchored around .rodata (string literals)
|
|
972
|
-
// and the C++ heap. Additional windows are registered on demand
|
|
973
|
-
// by ensureWindowFor() as new pointer regions are exposed to JS.
|
|
974
|
-
const char* name = "M";
|
|
975
|
-
uint64_t namePtrNumber = reinterpret_cast<uint64_t>(name);
|
|
976
|
-
|
|
977
|
-
// Reference allocation to discover the heap region.
|
|
978
|
-
auto heapProbe = std::make_shared<FixedBuffer>(0);
|
|
979
|
-
uint64_t heapPtrNumber = reinterpret_cast<uint64_t>(heapProbe.get());
|
|
980
|
-
|
|
981
|
-
uint64_t offsetRodataLow = (namePtrNumber > UINT32_MAX)
|
|
982
|
-
? (namePtrNumber - UINT32_MAX) : 0;
|
|
983
|
-
uint64_t offsetHeapLow = (heapPtrNumber > UINT32_MAX)
|
|
984
|
-
? (heapPtrNumber - UINT32_MAX) : 0;
|
|
985
|
-
uint64_t offsetRodataLowest = (offsetRodataLow > UINT32_MAX)
|
|
986
|
-
? (offsetRodataLow - UINT32_MAX) : 0;
|
|
987
|
-
uint64_t offsetRodataHigh = namePtrNumber;
|
|
988
|
-
|
|
989
|
-
registerHeapWindow(offsetRodataLow);
|
|
990
|
-
registerHeapWindow(offsetHeapLow);
|
|
991
|
-
registerHeapWindow(offsetRodataLowest);
|
|
992
|
-
registerHeapWindow(offsetRodataHigh);
|
|
993
|
-
|
|
994
|
-
// Self-healing fallback: when a JS-side heap read misses every window, JS calls
|
|
995
|
-
// this to open one centered on the pointer. Unconditional on purpose - JS is the
|
|
996
|
-
// source of truth about its own windows, so no native covered-check here.
|
|
997
|
-
auto ensureFn = facebook::jsi::Function::createFromHostFunction(
|
|
998
|
-
rt,
|
|
999
|
-
facebook::jsi::PropNameID::forAscii(rt, "__crossbind_ensure_heap_window"),
|
|
1000
|
-
1,
|
|
1001
|
-
[](facebook::jsi::Runtime& rt2, const facebook::jsi::Value&,
|
|
1002
|
-
const facebook::jsi::Value* args, size_t) -> facebook::jsi::Value {
|
|
1003
|
-
uint64_t p = args[0].asBigInt(rt2).asUint64(rt2);
|
|
1004
|
-
uint64_t newOffset = (p > UINT32_MAX / 2) ? (p - UINT32_MAX / 2) : 0;
|
|
1005
|
-
registerHeapWindow(newOffset);
|
|
1006
|
-
return facebook::jsi::Value::undefined();
|
|
1007
|
-
});
|
|
1008
|
-
rt.global().setProperty(rt, "__crossbind_ensure_heap_window", ensureFn);
|
|
950
|
+
runtimeCache = new RuntimeCache();
|
|
1009
951
|
|
|
1010
952
|
for (auto *f = init_funcs; f; f = f->next) {
|
|
1011
953
|
f->init_func();
|
|
@@ -1071,7 +1013,13 @@ template <typename T> static void register_memory_view(const char* name) {
|
|
|
1071
1013
|
} // namespace
|
|
1072
1014
|
|
|
1073
1015
|
int Crossbind::setEnv(std::string key, std::string value, bool overwrite) {
|
|
1016
|
+
#ifdef _WIN32
|
|
1017
|
+
// The Windows CRT has no setenv; _putenv_s always replaces, so a value to keep is checked first.
|
|
1018
|
+
if (!overwrite && getenv(key.c_str()) != nullptr) return 0;
|
|
1019
|
+
return _putenv_s(key.c_str(), value.c_str());
|
|
1020
|
+
#else
|
|
1074
1021
|
return setenv(key.c_str(), value.c_str(), overwrite);
|
|
1022
|
+
#endif
|
|
1075
1023
|
}
|
|
1076
1024
|
std::string Crossbind::getEnv(std::string key) {
|
|
1077
1025
|
const char* value = getenv(key.c_str());
|
|
@@ -1095,11 +1043,21 @@ EMSCRIPTEN_BINDINGS(builtin) {
|
|
|
1095
1043
|
register_integer<signed int>("int");
|
|
1096
1044
|
register_integer<unsigned int>("unsigned int");
|
|
1097
1045
|
|
|
1098
|
-
// register_bigint<signed long>("long");
|
|
1099
|
-
// register_bigint<unsigned long>("unsigned long");
|
|
1100
|
-
|
|
1101
1046
|
register_bigint<int64_t>("int64_t");
|
|
1102
1047
|
register_bigint<uint64_t>("uint64_t");
|
|
1048
|
+
// int64_t is long long on Apple platforms and long on Android: the other 64-bit pair needs its own entry
|
|
1049
|
+
// (wire.h binds it as a BigInt as well).
|
|
1050
|
+
if (!std::is_same_v<long, int64_t> && sizeof(long) == 8) {
|
|
1051
|
+
register_bigint<long>("long");
|
|
1052
|
+
register_bigint<unsigned long>("unsigned long");
|
|
1053
|
+
} else if (!std::is_same_v<long long, int64_t>) {
|
|
1054
|
+
register_bigint<long long>("long long");
|
|
1055
|
+
register_bigint<unsigned long long>("unsigned long long");
|
|
1056
|
+
}
|
|
1057
|
+
if constexpr (sizeof(long) == 4) {
|
|
1058
|
+
register_integer<long>("long");
|
|
1059
|
+
register_integer<unsigned long>("unsigned long");
|
|
1060
|
+
}
|
|
1103
1061
|
|
|
1104
1062
|
register_float<float>("float");
|
|
1105
1063
|
register_float<double>("double");
|