@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.
@@ -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
- uint64_t ptr = reinterpret_cast<uint64_t>(rawValue);
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
- uint64_t ptr = reinterpret_cast<uint64_t>(rawValue);
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>(rawValue.asBigInt(*jsRuntime).asUint64(*jsRuntime));
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 rawValue.getBigInt(*jsRuntime).asInt64(*jsRuntime);
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 rawValue.getBigInt(*jsRuntime).asUint64(*jsRuntime);
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>(rawValue.asBigInt(*jsRuntime).asUint64(*jsRuntime));
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
- template<> inline jsi::Value Bugra<EM_DESTRUCTORS>::toValue(EM_DESTRUCTORS rawValue) {
155
- return jsi::BigInt::fromUint64(*jsRuntime, reinterpret_cast<uint64_t>(rawValue));
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
- template<> inline jsi::Value Bugra<EM_DESTRUCTORS*>::toValue(EM_DESTRUCTORS* rawValue) {
159
- uint64_t ptr = reinterpret_cast<uint64_t>(rawValue);
160
- ensureWindowFor(ptr);
161
- return jsi::BigInt::fromUint64(*jsRuntime, ptr);
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
- jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_register_symbol").call(
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->global().getPropertyAsFunction(*jsRuntime, "__emval_incref").call(
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->global().getPropertyAsFunction(*jsRuntime, "__emval_decref").call(
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
- void _emval_run_destructors(EM_DESTRUCTORS handle) {
186
- jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_run_destructors").call(
187
- *jsRuntime,
188
- Bugra<EM_DESTRUCTORS>::toValue(handle)
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 = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_new_array").call(
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 = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_new_array_from_memory_view").call(
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 = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_new_object").call(
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 = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_new_cstring").call(
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 = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_new_u8string").call(
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
- // EM_VAL _emval_new_u16string(const char16_t*) {}
225
- EM_VAL _emval_take_value(TYPEID type, EM_VAR_ARGS argv) {
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
- Bugra<TYPEID>::toValue(type),
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 _emval_new(EM_VAL value, unsigned argCount, const TYPEID argTypes[], EM_VAR_ARGS argv) {
234
- auto response = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_new").call(
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
- Bugra<TYPEID>::toArrayValue(std::vector<TYPEID>(argTypes, argTypes + argCount)),
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 = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_get_global").call(
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 = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_get_module_property").call(
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 = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_get_property").call(
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
- jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_set_property").call(
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, EM_DESTRUCTORS* destructors) {
274
- auto response = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_as").call(
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 = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_as_int64").call(
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 = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_as_uint64").call(
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 = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_equals").call(
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 = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_strictly_equals").call(
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 = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_greater_than").call(
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 = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_less_than").call(
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 = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_not").call(
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[], EM_VAR_ARGS argv) {
338
- auto response = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_call").call(
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
- Bugra<TYPEID>::toArrayValue(std::vector<TYPEID>(argTypes, argTypes + argCount)),
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 = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_get_method_caller").call(
377
+ auto response = helper("__emval_get_method_caller").call(
349
378
  *jsRuntime,
350
379
  Bugra<unsigned>::toValue(argCount),
351
- Bugra<TYPEID>::toArrayValue(std::vector<TYPEID>(argTypes, argTypes + argCount))
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, EM_DESTRUCTORS* destructors, EM_VAR_ARGS argv) {
356
- auto response = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_call_method").call(
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, EM_VAR_ARGS argv) {
367
- jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_call_void_method").call(
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 = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_typeof").call(
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 = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_instanceof").call(
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 = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_is_number").call(
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 = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_is_string").call(
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 = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_in").call(
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 = jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_delete").call(
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
- jsRuntime->global().getPropertyAsFunction(*jsRuntime, "__emval_throw").call(
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
- struct FixedBuffer : facebook::jsi::MutableBuffer {
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");