@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
package/cpp/src/emscripten/val.h
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
#include <climits>
|
|
18
18
|
#include "./wire.h"
|
|
19
19
|
#include <cstdint> // uintptr_t
|
|
20
|
+
#include <stdexcept>
|
|
20
21
|
#include <vector>
|
|
21
22
|
|
|
22
23
|
// #include <android/log.h>
|
|
@@ -48,15 +49,14 @@ enum {
|
|
|
48
49
|
_EMVAL_FALSE = 4
|
|
49
50
|
};
|
|
50
51
|
|
|
51
|
-
typedef struct _EM_DESTRUCTORS* EM_DESTRUCTORS;
|
|
52
52
|
typedef struct _EM_METHOD_CALLER* EM_METHOD_CALLER;
|
|
53
53
|
typedef facebook::jsi::Value EM_GENERIC_WIRE_TYPE;
|
|
54
|
-
typedef const void* EM_VAR_ARGS;
|
|
55
54
|
|
|
56
55
|
void _emval_incref(EM_VAL value);
|
|
57
56
|
void _emval_decref(EM_VAL value);
|
|
58
57
|
|
|
59
|
-
|
|
58
|
+
// Runs the destructors the latest _emval_as or _emval_call_method conversion queued.
|
|
59
|
+
void _emval_run_destructors();
|
|
60
60
|
|
|
61
61
|
EM_VAL _emval_new_array();
|
|
62
62
|
EM_VAL _emval_new_array_from_memory_view(EM_VAL mv);
|
|
@@ -65,19 +65,19 @@ EM_VAL _emval_new_cstring(const char*);
|
|
|
65
65
|
EM_VAL _emval_new_u8string(const char*);
|
|
66
66
|
EM_VAL _emval_new_u16string(const char16_t*);
|
|
67
67
|
|
|
68
|
-
EM_VAL _emval_take_value(TYPEID type,
|
|
68
|
+
EM_VAL _emval_take_value(TYPEID type, const facebook::jsi::Value& value);
|
|
69
69
|
|
|
70
70
|
EM_VAL _emval_new(
|
|
71
71
|
EM_VAL value,
|
|
72
72
|
unsigned argCount,
|
|
73
73
|
const TYPEID argTypes[],
|
|
74
|
-
|
|
74
|
+
facebook::jsi::Value* argv);
|
|
75
75
|
|
|
76
76
|
EM_VAL _emval_get_global(const char* name);
|
|
77
77
|
EM_VAL _emval_get_module_property(const char* name);
|
|
78
78
|
EM_VAL _emval_get_property(EM_VAL object, EM_VAL key);
|
|
79
79
|
void _emval_set_property(EM_VAL object, EM_VAL key, EM_VAL value);
|
|
80
|
-
EM_GENERIC_WIRE_TYPE _emval_as(EM_VAL value, TYPEID returnType
|
|
80
|
+
EM_GENERIC_WIRE_TYPE _emval_as(EM_VAL value, TYPEID returnType);
|
|
81
81
|
int64_t _emval_as_int64(EM_VAL value, TYPEID returnType);
|
|
82
82
|
uint64_t _emval_as_uint64(EM_VAL value, TYPEID returnType);
|
|
83
83
|
|
|
@@ -91,7 +91,7 @@ EM_VAL _emval_call(
|
|
|
91
91
|
EM_VAL value,
|
|
92
92
|
unsigned argCount,
|
|
93
93
|
const TYPEID argTypes[],
|
|
94
|
-
|
|
94
|
+
facebook::jsi::Value* argv);
|
|
95
95
|
|
|
96
96
|
// DO NOT call this more than once per signature. It will
|
|
97
97
|
// leak generated function objects!
|
|
@@ -102,13 +102,14 @@ EM_GENERIC_WIRE_TYPE _emval_call_method(
|
|
|
102
102
|
EM_METHOD_CALLER caller,
|
|
103
103
|
EM_VAL handle,
|
|
104
104
|
const char* methodName,
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
unsigned argCount,
|
|
106
|
+
facebook::jsi::Value* argv);
|
|
107
107
|
void _emval_call_void_method(
|
|
108
108
|
EM_METHOD_CALLER caller,
|
|
109
109
|
EM_VAL handle,
|
|
110
110
|
const char* methodName,
|
|
111
|
-
|
|
111
|
+
unsigned argCount,
|
|
112
|
+
facebook::jsi::Value* argv);
|
|
112
113
|
EM_VAL _emval_typeof(EM_VAL value);
|
|
113
114
|
bool _emval_instanceof(EM_VAL object, EM_VAL constructor);
|
|
114
115
|
bool _emval_is_number(EM_VAL object);
|
|
@@ -146,20 +147,17 @@ struct Signature {
|
|
|
146
147
|
}
|
|
147
148
|
};
|
|
148
149
|
|
|
150
|
+
// Scoped to one conversion: every _emval_as / _emval_call_method that returns queues exactly one
|
|
151
|
+
// destructor list in JS, and this runs it once the C++ side has read the value.
|
|
149
152
|
struct DestructorsRunner {
|
|
150
153
|
public:
|
|
151
|
-
|
|
152
|
-
: destructors(d)
|
|
153
|
-
{}
|
|
154
|
+
DestructorsRunner() = default;
|
|
154
155
|
~DestructorsRunner() {
|
|
155
|
-
_emval_run_destructors(
|
|
156
|
+
_emval_run_destructors();
|
|
156
157
|
}
|
|
157
158
|
|
|
158
159
|
DestructorsRunner(const DestructorsRunner&) = delete;
|
|
159
160
|
void operator=(const DestructorsRunner&) = delete;
|
|
160
|
-
|
|
161
|
-
private:
|
|
162
|
-
EM_DESTRUCTORS destructors;
|
|
163
161
|
};
|
|
164
162
|
|
|
165
163
|
template<typename WireType>
|
|
@@ -178,115 +176,27 @@ struct GenericWireTypeConverter<Pointee*> {
|
|
|
178
176
|
|
|
179
177
|
template<typename T>
|
|
180
178
|
T fromGenericWireType(facebook::jsi::Value& g) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
struct PackSize;
|
|
191
|
-
|
|
192
|
-
template<>
|
|
193
|
-
struct PackSize<> {
|
|
194
|
-
static constexpr size_t value = 0;
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
template<typename Arg, typename... Args>
|
|
198
|
-
struct PackSize<Arg, Args...> {
|
|
199
|
-
static constexpr size_t value = (sizeof(typename BindingType<Arg>::WireType) + 7) / 8 + PackSize<Args...>::value;
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
union GenericWireType {
|
|
203
|
-
union {
|
|
204
|
-
unsigned u;
|
|
205
|
-
float f;
|
|
206
|
-
// Use uint32_t for pointer values. This limits us, for now, to 32-bit
|
|
207
|
-
// address ranges even on wasm64. This is enforced by assertions below.
|
|
208
|
-
// TODO(sbc): Allow full 64-bit address range here under wasm64, most
|
|
209
|
-
// likely by increasing the size of GenericWireType on wasm64.
|
|
210
|
-
uint32_t p;
|
|
211
|
-
} w[2];
|
|
212
|
-
double d;
|
|
213
|
-
uint64_t u;
|
|
214
|
-
};
|
|
215
|
-
static_assert(sizeof(GenericWireType) == 8, "GenericWireType must be 8 bytes");
|
|
216
|
-
static_assert(alignof(GenericWireType) == 8, "GenericWireType must be 8-byte-aligned");
|
|
217
|
-
|
|
218
|
-
inline void writeGenericWireType(GenericWireType*& cursor, float wt) {
|
|
219
|
-
cursor->w[0].f = wt;
|
|
220
|
-
++cursor;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
inline void writeGenericWireType(GenericWireType*& cursor, double wt) {
|
|
224
|
-
cursor->d = wt;
|
|
225
|
-
++cursor;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
inline void writeGenericWireType(GenericWireType*& cursor, int64_t wt) {
|
|
229
|
-
cursor->u = wt;
|
|
230
|
-
++cursor;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
inline void writeGenericWireType(GenericWireType*& cursor, uint64_t wt) {
|
|
234
|
-
cursor->u = wt;
|
|
235
|
-
++cursor;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
template<typename T>
|
|
239
|
-
void writeGenericWireType(GenericWireType*& cursor, T* wt) {
|
|
240
|
-
uint64_t short_ptr = reinterpret_cast<uint64_t>(wt);
|
|
241
|
-
// assert(short_ptr <= UINT32_MAX);
|
|
242
|
-
cursor->u = short_ptr;
|
|
243
|
-
++cursor;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
template<typename ElementType>
|
|
247
|
-
inline void writeGenericWireType(GenericWireType*& cursor, const memory_view<ElementType>& wt) {
|
|
248
|
-
uintptr_t short_ptr = reinterpret_cast<uintptr_t>(wt.data);
|
|
249
|
-
// This wire slot stores the data pointer in 32 bits (upstream wasm32 layout). On 64-bit
|
|
250
|
-
// native a pointer above 4 GiB cannot fit; abort loudly rather than silently truncating to
|
|
251
|
-
// a bogus address (release builds compile out the assert). Full 64-bit memory_view support
|
|
252
|
-
// needs a wider wire encoding on both the writer and the reader.
|
|
253
|
-
assert(short_ptr <= UINT32_MAX);
|
|
254
|
-
if (short_ptr > UINT32_MAX) {
|
|
255
|
-
abort();
|
|
256
|
-
}
|
|
257
|
-
cursor->w[0].u = wt.size;
|
|
258
|
-
cursor->w[1].p = static_cast<uint32_t>(short_ptr);
|
|
259
|
-
++cursor;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
template<typename T>
|
|
263
|
-
void writeGenericWireType(GenericWireType*& cursor, T wt) {
|
|
264
|
-
cursor->w[0].u = static_cast<unsigned>(wt);
|
|
265
|
-
++cursor;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
inline void writeGenericWireTypes(GenericWireType*&) {
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
template<typename First, typename... Rest>
|
|
272
|
-
EMSCRIPTEN_ALWAYS_INLINE void writeGenericWireTypes(GenericWireType*& cursor, First&& first, Rest&&... rest) {
|
|
273
|
-
writeGenericWireType(cursor, BindingType<First>::toWireType(std::forward<First>(first)));
|
|
274
|
-
writeGenericWireTypes(cursor, std::forward<Rest>(rest)...);
|
|
179
|
+
if constexpr (std::is_same<T, std::string>::value) {
|
|
180
|
+
return g.asString(*jsRuntime).utf8(*jsRuntime);
|
|
181
|
+
} else if constexpr (std::is_same<T, facebook::jsi::Value>::value) {
|
|
182
|
+
return std::move(g);
|
|
183
|
+
} else {
|
|
184
|
+
typedef typename BindingType<T>::WireType WireType;
|
|
185
|
+
WireType wt = GenericWireTypeConverter<WireType>::from(g);
|
|
186
|
+
return BindingType<T>::fromWireType(wt);
|
|
187
|
+
}
|
|
275
188
|
}
|
|
276
189
|
|
|
190
|
+
// Arguments cross as the values embind's JS types accept (toWireType2), the same way bound
|
|
191
|
+
// functions return them, never as bytes JS would have to read out of native memory.
|
|
192
|
+
// jsi::Value has no copy constructor: each toWireType2 prvalue initializes its element directly,
|
|
193
|
+
// so routing one through a named const Value first would stop this from compiling.
|
|
277
194
|
template<typename... Args>
|
|
278
|
-
struct
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
writeGenericWireTypes(cursor, std::forward<Args>(args)...);
|
|
282
|
-
}
|
|
195
|
+
struct WireValues {
|
|
196
|
+
explicit WireValues(Args&&... args)
|
|
197
|
+
: values{{BindingType<Args>::toWireType2(*jsRuntime, std::forward<Args>(args))...}} {}
|
|
283
198
|
|
|
284
|
-
|
|
285
|
-
return elements.data();
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
private:
|
|
289
|
-
std::array<GenericWireType, PackSize<Args...>::value> elements;
|
|
199
|
+
std::array<facebook::jsi::Value, sizeof...(Args)> values;
|
|
290
200
|
};
|
|
291
201
|
|
|
292
202
|
template<typename ReturnType, typename... Args>
|
|
@@ -294,15 +204,14 @@ struct MethodCaller {
|
|
|
294
204
|
static ReturnType call(EM_VAL handle, const char* methodName, Args&&... args) {
|
|
295
205
|
auto caller = Signature<ReturnType, Args...>::get_method_caller();
|
|
296
206
|
|
|
297
|
-
|
|
298
|
-
EM_DESTRUCTORS destructors;
|
|
207
|
+
WireValues<Args...> argv(std::forward<Args>(args)...);
|
|
299
208
|
EM_GENERIC_WIRE_TYPE result = _emval_call_method(
|
|
300
209
|
caller,
|
|
301
210
|
handle,
|
|
302
211
|
methodName,
|
|
303
|
-
|
|
304
|
-
argv);
|
|
305
|
-
DestructorsRunner rd
|
|
212
|
+
sizeof...(Args),
|
|
213
|
+
argv.values.data());
|
|
214
|
+
DestructorsRunner rd;
|
|
306
215
|
return fromGenericWireType<ReturnType>(result);
|
|
307
216
|
}
|
|
308
217
|
};
|
|
@@ -312,12 +221,13 @@ struct MethodCaller<void, Args...> {
|
|
|
312
221
|
static void call(EM_VAL handle, const char* methodName, Args&&... args) {
|
|
313
222
|
auto caller = Signature<void, Args...>::get_method_caller();
|
|
314
223
|
|
|
315
|
-
|
|
224
|
+
WireValues<Args...> argv(std::forward<Args>(args)...);
|
|
316
225
|
_emval_call_void_method(
|
|
317
226
|
caller,
|
|
318
227
|
handle,
|
|
319
228
|
methodName,
|
|
320
|
-
|
|
229
|
+
sizeof...(Args),
|
|
230
|
+
argv.values.data());
|
|
321
231
|
}
|
|
322
232
|
};
|
|
323
233
|
|
|
@@ -410,9 +320,9 @@ public:
|
|
|
410
320
|
explicit val(T&& value) {
|
|
411
321
|
using namespace internal;
|
|
412
322
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
323
|
+
handle = _emval_take_value(
|
|
324
|
+
internal::TypeID<T>::get(),
|
|
325
|
+
BindingType<T>::toWireType2(*jsRuntime, std::forward<T>(value)));
|
|
416
326
|
}
|
|
417
327
|
|
|
418
328
|
val() : handle(EM_VAL(internal::_EMVAL_UNDEFINED)) {}
|
|
@@ -555,15 +465,10 @@ public:
|
|
|
555
465
|
T as(Policies...) const {
|
|
556
466
|
using namespace internal;
|
|
557
467
|
|
|
558
|
-
typedef BindingType<T> BT;
|
|
559
468
|
typename WithPolicies<Policies...>::template ArgTypeList<T> targetType;
|
|
560
469
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
handle,
|
|
564
|
-
targetType.getTypes()[0],
|
|
565
|
-
&destructors);
|
|
566
|
-
DestructorsRunner dr(destructors);
|
|
470
|
+
EM_GENERIC_WIRE_TYPE result = _emval_as(handle, targetType.getTypes()[0]);
|
|
471
|
+
DestructorsRunner dr;
|
|
567
472
|
return fromGenericWireType<T>(result);
|
|
568
473
|
}
|
|
569
474
|
|
|
@@ -629,8 +534,8 @@ private:
|
|
|
629
534
|
using namespace internal;
|
|
630
535
|
|
|
631
536
|
WithPolicies<>::ArgTypeList<Args...> argList;
|
|
632
|
-
|
|
633
|
-
return val(impl(handle, argList.getCount(), argList.getTypes(), argv));
|
|
537
|
+
WireValues<Args...> argv(std::forward<Args>(args)...);
|
|
538
|
+
return val(impl(handle, argList.getCount(), argList.getTypes(), argv.values.data()));
|
|
634
539
|
}
|
|
635
540
|
|
|
636
541
|
template<typename T>
|
|
@@ -669,7 +574,7 @@ struct BindingType<val> {
|
|
|
669
574
|
static val fromWireType2(facebook::jsi::Runtime& rt, WireType2& v) {
|
|
670
575
|
// return val::undefined();
|
|
671
576
|
// return val::take_ownership(reinterpret_cast<EM_VAL>((int) v.getNumber()));
|
|
672
|
-
return val::take_ownership(reinterpret_cast<EM_VAL>(
|
|
577
|
+
return val::take_ownership(reinterpret_cast<EM_VAL>(bigIntUint64(rt, v)));
|
|
673
578
|
}
|
|
674
579
|
};
|
|
675
580
|
|
|
@@ -690,17 +595,23 @@ std::vector<T> vecFromJSArray(const val& v, Policies... policies) {
|
|
|
690
595
|
|
|
691
596
|
template <typename T>
|
|
692
597
|
std::vector<T> convertJSArrayToNumberVector(const val& v) {
|
|
693
|
-
|
|
598
|
+
// size_t is not a registered embind type on every 64-bit ABI; the length is a Number anyway.
|
|
599
|
+
const double l = v["length"].as<double>();
|
|
600
|
+
if (!(l >= 0 && l <= static_cast<double>(std::vector<T>().max_size()))) {
|
|
601
|
+
throw std::length_error("crossbind: the array length is not a valid size");
|
|
602
|
+
}
|
|
694
603
|
|
|
695
604
|
std::vector<T> rv;
|
|
696
|
-
rv.resize(l);
|
|
605
|
+
rv.resize(static_cast<size_t>(l));
|
|
697
606
|
|
|
698
607
|
// Copy the array into our vector through the use of typed arrays.
|
|
699
608
|
// It will try to convert each element through Number().
|
|
700
609
|
// See https://www.ecma-international.org/ecma-262/6.0/#sec-%typedarray%.prototype.set-array-offset
|
|
701
610
|
// and https://www.ecma-international.org/ecma-262/6.0/#sec-tonumber
|
|
702
|
-
|
|
703
|
-
|
|
611
|
+
// The typed array is JS-owned and copied out: one aliasing rv would need external memory.
|
|
612
|
+
val typedArray = val::global(internal::typedArrayNameOf<T>()).new_(l);
|
|
613
|
+
typedArray.call<void>("set", v);
|
|
614
|
+
internal::copyFromTypedArray(*jsRuntime, typedArray.as<facebook::jsi::Value>(), rv.data(), rv.size() * sizeof(T));
|
|
704
615
|
|
|
705
616
|
return rv;
|
|
706
617
|
}
|
|
@@ -21,11 +21,13 @@
|
|
|
21
21
|
#include <cstdio>
|
|
22
22
|
#include <cstdlib>
|
|
23
23
|
#include <cstdint>
|
|
24
|
+
#include <cstring>
|
|
24
25
|
#include <memory>
|
|
25
26
|
#include <optional>
|
|
26
27
|
#include <string>
|
|
27
28
|
|
|
28
29
|
#include <jsi/jsi.h>
|
|
30
|
+
#include "../crossbind/direct_reads.h"
|
|
29
31
|
|
|
30
32
|
// embind registers std::basic_string<unsigned char> as the raw-byte counterpart to UTF-8
|
|
31
33
|
// std::string, but the standard has never defined std::char_traits for unsigned char. libc++ used
|
|
@@ -96,25 +98,40 @@ constexpr bool has_unbound_type_names = false;
|
|
|
96
98
|
|
|
97
99
|
namespace internal {
|
|
98
100
|
|
|
101
|
+
// A BigInt read straight from the engine where the runtime offers crossbind::IDirectReads, instead
|
|
102
|
+
// of cloning it into a jsi::BigInt first; `this` arrives this way on every method call. Anything
|
|
103
|
+
// that is not a BigInt, or does not fit, takes the jsi path and throws as it did.
|
|
104
|
+
inline uint64_t bigIntUint64(facebook::jsi::Runtime& rt, const facebook::jsi::Value& v) {
|
|
105
|
+
auto* reads = static_cast<crossbind::IDirectReads*>(rt.castInterface(crossbind::IDirectReads::uuid));
|
|
106
|
+
uint64_t bits = 0;
|
|
107
|
+
bool lossless = false;
|
|
108
|
+
if (reads != nullptr && reads->bigIntToUint64(v, bits, lossless) && lossless) return bits;
|
|
109
|
+
return v.asBigInt(rt).asUint64(rt);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
inline int64_t bigIntInt64(facebook::jsi::Runtime& rt, const facebook::jsi::Value& v) {
|
|
113
|
+
auto* reads = static_cast<crossbind::IDirectReads*>(rt.castInterface(crossbind::IDirectReads::uuid));
|
|
114
|
+
int64_t bits = 0;
|
|
115
|
+
bool lossless = false;
|
|
116
|
+
if (reads != nullptr && reads->bigIntToInt64(v, bits, lossless) && lossless) return bits;
|
|
117
|
+
return v.asBigInt(rt).asInt64(rt);
|
|
118
|
+
}
|
|
119
|
+
|
|
99
120
|
// JS hands native pointers over as BigInt addresses, but a null pointer arrives as the number 0
|
|
100
121
|
// and enum values as plain numbers: take both spellings.
|
|
101
122
|
inline uint64_t addressFromValue(facebook::jsi::Runtime& rt, const facebook::jsi::Value& v) {
|
|
102
123
|
if (v.isNumber()) return static_cast<uint64_t>(v.asNumber());
|
|
103
124
|
if (v.isNull() || v.isUndefined()) return 0;
|
|
104
|
-
return
|
|
125
|
+
return bigIntUint64(rt, v);
|
|
105
126
|
}
|
|
106
127
|
|
|
107
128
|
inline int64_t integerFromValue(facebook::jsi::Runtime& rt, const facebook::jsi::Value& v) {
|
|
108
129
|
if (v.isNumber()) return static_cast<int64_t>(v.asNumber());
|
|
109
|
-
return
|
|
130
|
+
return bigIntInt64(rt, v);
|
|
110
131
|
}
|
|
111
132
|
|
|
112
133
|
typedef const void* TYPEID;
|
|
113
134
|
|
|
114
|
-
// Defined in bind.cpp: registers a JS-side ArrayBuffer window covering `ptr` so
|
|
115
|
-
// heap reads on values we hand out by pointer cannot miss every window.
|
|
116
|
-
void ensureWindowFor(uint64_t ptr);
|
|
117
|
-
|
|
118
135
|
// We don't need the full std::type_info implementation. We
|
|
119
136
|
// just need a unique identifier per type and polymorphic type
|
|
120
137
|
// identification.
|
|
@@ -396,7 +413,7 @@ struct BindingType<int64_t> {
|
|
|
396
413
|
return facebook::jsi::BigInt::fromInt64(rt, b);
|
|
397
414
|
}
|
|
398
415
|
static WireType fromWireType2(facebook::jsi::Runtime& rt, WireType2& wt) {
|
|
399
|
-
return
|
|
416
|
+
return bigIntInt64(rt, wt);
|
|
400
417
|
}
|
|
401
418
|
};
|
|
402
419
|
|
|
@@ -416,7 +433,57 @@ struct BindingType<uint64_t> {
|
|
|
416
433
|
return facebook::jsi::BigInt::fromUint64(rt, b);
|
|
417
434
|
}
|
|
418
435
|
static WireType fromWireType2(facebook::jsi::Runtime& rt, WireType2& wt) {
|
|
419
|
-
return
|
|
436
|
+
return bigIntUint64(rt, wt);
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
// int64_t is long long on Apple platforms and long on Android: the other 64-bit pair crosses as a BigInt too.
|
|
441
|
+
template<typename T>
|
|
442
|
+
constexpr bool isOther64BitInteger = std::is_integral_v<T> && sizeof(T) == 8
|
|
443
|
+
&& !std::is_same_v<T, int64_t> && !std::is_same_v<T, uint64_t>;
|
|
444
|
+
|
|
445
|
+
template<typename T>
|
|
446
|
+
struct BindingType<T, std::enable_if_t<isOther64BitInteger<T>>> {
|
|
447
|
+
using Exact = std::conditional_t<std::is_signed_v<T>, int64_t, uint64_t>;
|
|
448
|
+
typedef T WireType;
|
|
449
|
+
typedef const facebook::jsi::Value WireType2;
|
|
450
|
+
|
|
451
|
+
static WireType toWireType(WireType b) {
|
|
452
|
+
return b;
|
|
453
|
+
}
|
|
454
|
+
static WireType fromWireType(WireType wt) {
|
|
455
|
+
return wt;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
static WireType2 toWireType2(facebook::jsi::Runtime& rt, WireType b) {
|
|
459
|
+
return BindingType<Exact>::toWireType2(rt, static_cast<Exact>(b));
|
|
460
|
+
}
|
|
461
|
+
static WireType fromWireType2(facebook::jsi::Runtime& rt, WireType2& wt) {
|
|
462
|
+
return static_cast<WireType>(BindingType<Exact>::fromWireType2(rt, wt));
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
// Windows keeps long at 32 bits, still a type apart from int: it crosses as a Number like int does.
|
|
467
|
+
template<typename T>
|
|
468
|
+
constexpr bool isNarrowLong = (std::is_same_v<T, long> || std::is_same_v<T, unsigned long>) && sizeof(T) == 4;
|
|
469
|
+
|
|
470
|
+
template<typename T>
|
|
471
|
+
struct BindingType<T, std::enable_if_t<isNarrowLong<T>>> {
|
|
472
|
+
typedef T WireType;
|
|
473
|
+
typedef const facebook::jsi::Value WireType2;
|
|
474
|
+
|
|
475
|
+
static WireType toWireType(WireType b) {
|
|
476
|
+
return b;
|
|
477
|
+
}
|
|
478
|
+
static WireType fromWireType(WireType wt) {
|
|
479
|
+
return wt;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
static WireType2 toWireType2(facebook::jsi::Runtime& rt, WireType b) {
|
|
483
|
+
return WireType2((double) b);
|
|
484
|
+
}
|
|
485
|
+
static WireType fromWireType2(facebook::jsi::Runtime& rt, WireType2& wt) {
|
|
486
|
+
return (WireType) wt.getNumber();
|
|
420
487
|
}
|
|
421
488
|
};
|
|
422
489
|
|
|
@@ -434,64 +501,80 @@ struct BindingType<uint64_t> {
|
|
|
434
501
|
};
|
|
435
502
|
|
|
436
503
|
// jsi strings cross as UTF-8, so a std::u16string (embind's byte-preserving string) is transcoded; every code unit of
|
|
437
|
-
// well-formed UTF-16 survives the round trip.
|
|
504
|
+
// well-formed UTF-16 survives the round trip. Both directions size their output once and write through a pointer:
|
|
505
|
+
// a push_back per unit was most of the cost of moving bytes (readBytes) across.
|
|
438
506
|
inline std::string utf16ToUtf8(const std::u16string& text) {
|
|
439
|
-
|
|
440
|
-
out
|
|
441
|
-
|
|
507
|
+
// At most three bytes per code unit; a surrogate pair takes four for two.
|
|
508
|
+
std::string out(text.size() * 3, '\0');
|
|
509
|
+
char* o = out.data();
|
|
510
|
+
const size_t n = text.size();
|
|
511
|
+
for (size_t i = 0; i < n; ++i) {
|
|
442
512
|
char32_t cp = text[i];
|
|
443
|
-
if (cp
|
|
513
|
+
if (cp < 0x800) {
|
|
514
|
+
// One byte or two, chosen without a branch: in byte data half the units are above 0x7F
|
|
515
|
+
// at random. The spare second byte stays inside the three reserved per unit.
|
|
516
|
+
const bool isTwoBytes = cp >= 0x80;
|
|
517
|
+
o[0] = static_cast<char>(isTwoBytes ? 0xC0 | (cp >> 6) : cp);
|
|
518
|
+
o[1] = static_cast<char>(0x80 | (cp & 0x3F));
|
|
519
|
+
o += 1 + isTwoBytes;
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
if (cp >= 0xD800 && cp <= 0xDBFF && i + 1 < n && text[i + 1] >= 0xDC00 && text[i + 1] <= 0xDFFF) {
|
|
444
523
|
cp = 0x10000 + ((cp - 0xD800) << 10) + (text[++i] - 0xDC00);
|
|
445
524
|
} else if (cp >= 0xD800 && cp <= 0xDFFF) {
|
|
446
525
|
cp = 0xFFFD;
|
|
447
526
|
}
|
|
448
|
-
if (cp <
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
out.push_back(static_cast<char>(0x80 | (cp & 0x3F)));
|
|
453
|
-
} else if (cp < 0x10000) {
|
|
454
|
-
out.push_back(static_cast<char>(0xE0 | (cp >> 12)));
|
|
455
|
-
out.push_back(static_cast<char>(0x80 | ((cp >> 6) & 0x3F)));
|
|
456
|
-
out.push_back(static_cast<char>(0x80 | (cp & 0x3F)));
|
|
527
|
+
if (cp < 0x10000) {
|
|
528
|
+
*o++ = static_cast<char>(0xE0 | (cp >> 12));
|
|
529
|
+
*o++ = static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
|
|
530
|
+
*o++ = static_cast<char>(0x80 | (cp & 0x3F));
|
|
457
531
|
} else {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
532
|
+
*o++ = static_cast<char>(0xF0 | (cp >> 18));
|
|
533
|
+
*o++ = static_cast<char>(0x80 | ((cp >> 12) & 0x3F));
|
|
534
|
+
*o++ = static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
|
|
535
|
+
*o++ = static_cast<char>(0x80 | (cp & 0x3F));
|
|
462
536
|
}
|
|
463
537
|
}
|
|
538
|
+
out.resize(static_cast<size_t>(o - out.data()));
|
|
464
539
|
return out;
|
|
465
540
|
}
|
|
466
541
|
|
|
467
542
|
inline std::u16string utf8ToUtf16(const std::string& text) {
|
|
468
|
-
|
|
469
|
-
out
|
|
543
|
+
// Never more code units than bytes.
|
|
544
|
+
std::u16string out(text.size(), u'\0');
|
|
545
|
+
char16_t* o = out.data();
|
|
546
|
+
const size_t n = text.size();
|
|
470
547
|
size_t i = 0;
|
|
471
|
-
while (i <
|
|
548
|
+
while (i < n) {
|
|
472
549
|
unsigned char lead = static_cast<unsigned char>(text[i]);
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
550
|
+
if (lead < 0x80) {
|
|
551
|
+
*o++ = lead;
|
|
552
|
+
++i;
|
|
553
|
+
continue;
|
|
554
|
+
}
|
|
555
|
+
size_t extra = (lead >> 5) == 0x6 ? 1 : (lead >> 4) == 0xE ? 2 : (lead >> 3) == 0x1E ? 3 : 4;
|
|
556
|
+
char32_t cp = extra == 1 ? (lead & 0x1F) : extra == 2 ? (lead & 0x0F) : (lead & 0x07);
|
|
557
|
+
bool valid = extra < 4 && i + extra < n;
|
|
476
558
|
for (size_t k = 1; valid && k <= extra; ++k) {
|
|
477
559
|
unsigned char next = static_cast<unsigned char>(text[i + k]);
|
|
478
560
|
valid = (next >> 6) == 0x2;
|
|
479
561
|
cp = (cp << 6) | (next & 0x3F);
|
|
480
562
|
}
|
|
481
563
|
if (!valid) {
|
|
482
|
-
|
|
564
|
+
*o++ = 0xFFFD;
|
|
483
565
|
++i;
|
|
484
566
|
continue;
|
|
485
567
|
}
|
|
486
568
|
i += extra + 1;
|
|
487
569
|
if (cp >= 0x10000) {
|
|
488
570
|
cp -= 0x10000;
|
|
489
|
-
|
|
490
|
-
|
|
571
|
+
*o++ = static_cast<char16_t>(0xD800 + (cp >> 10));
|
|
572
|
+
*o++ = static_cast<char16_t>(0xDC00 + (cp & 0x3FF));
|
|
491
573
|
} else {
|
|
492
|
-
|
|
574
|
+
*o++ = static_cast<char16_t>(cp);
|
|
493
575
|
}
|
|
494
576
|
}
|
|
577
|
+
out.resize(static_cast<size_t>(o - out.data()));
|
|
495
578
|
return out;
|
|
496
579
|
}
|
|
497
580
|
|
|
@@ -521,11 +604,12 @@ struct BindingType<std::basic_string<T>> {
|
|
|
521
604
|
return facebook::jsi::String::createFromUtf8(rt, v);
|
|
522
605
|
}
|
|
523
606
|
}
|
|
607
|
+
// asString, not getString: a non-string argument then throws into JS instead of being read as one.
|
|
524
608
|
static String fromWireType2(facebook::jsi::Runtime& rt, WireType2& v) {
|
|
525
609
|
if constexpr (std::is_same_v<T, char16_t>) {
|
|
526
|
-
return utf8ToUtf16(v.
|
|
610
|
+
return utf8ToUtf16(v.asString(rt).utf8(rt));
|
|
527
611
|
} else {
|
|
528
|
-
return v.
|
|
612
|
+
return v.asString(rt).utf8(rt);
|
|
529
613
|
}
|
|
530
614
|
}
|
|
531
615
|
};
|
|
@@ -616,20 +700,14 @@ struct GenericBindingType {
|
|
|
616
700
|
}
|
|
617
701
|
|
|
618
702
|
static WireType2 toWireType2(facebook::jsi::Runtime& rt, const T& v) {
|
|
619
|
-
|
|
620
|
-
// memory reads on the new object die with "Heap error ... no covering window".
|
|
621
|
-
uint64_t ptr = reinterpret_cast<uint64_t>(new T(v));
|
|
622
|
-
ensureWindowFor(ptr);
|
|
623
|
-
return facebook::jsi::BigInt::fromUint64(rt, ptr);
|
|
703
|
+
return facebook::jsi::BigInt::fromUint64(rt, reinterpret_cast<uint64_t>(new T(v)));
|
|
624
704
|
}
|
|
625
705
|
static WireType2 toWireType2(facebook::jsi::Runtime& rt, T&& v) {
|
|
626
|
-
|
|
627
|
-
ensureWindowFor(ptr);
|
|
628
|
-
return facebook::jsi::BigInt::fromUint64(rt, ptr);
|
|
706
|
+
return facebook::jsi::BigInt::fromUint64(rt, reinterpret_cast<uint64_t>(new T(std::forward<T>(v))));
|
|
629
707
|
}
|
|
630
708
|
|
|
631
709
|
static ActualT& fromWireType2(facebook::jsi::Runtime& rt, WireType2& wt) {
|
|
632
|
-
return *reinterpret_cast<WireType>(
|
|
710
|
+
return *reinterpret_cast<WireType>(bigIntUint64(rt, wt));
|
|
633
711
|
}
|
|
634
712
|
};
|
|
635
713
|
|
|
@@ -697,6 +775,46 @@ constexpr bool typeSupportsMemoryView() {
|
|
|
697
775
|
sizeof(T) == 4 || sizeof(T) == 8));
|
|
698
776
|
}
|
|
699
777
|
|
|
778
|
+
template<typename T>
|
|
779
|
+
constexpr const char* typedArrayNameOf() {
|
|
780
|
+
static_assert(typeSupportsMemoryView<T>(), "type does not map to a typed array");
|
|
781
|
+
if constexpr (std::is_floating_point<T>::value) {
|
|
782
|
+
return sizeof(T) == 4 ? "Float32Array" : "Float64Array";
|
|
783
|
+
} else if constexpr (sizeof(T) == 1) {
|
|
784
|
+
return std::is_signed<T>::value ? "Int8Array" : "Uint8Array";
|
|
785
|
+
} else if constexpr (sizeof(T) == 2) {
|
|
786
|
+
return std::is_signed<T>::value ? "Int16Array" : "Uint16Array";
|
|
787
|
+
} else if constexpr (sizeof(T) == 4) {
|
|
788
|
+
return std::is_signed<T>::value ? "Int32Array" : "Uint32Array";
|
|
789
|
+
} else {
|
|
790
|
+
return std::is_signed<T>::value ? "BigInt64Array" : "BigUint64Array";
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// Native memory reaches JS only as a copy in an ArrayBuffer JS allocated: Electron's V8 memory
|
|
795
|
+
// cage refuses ArrayBuffers over external memory.
|
|
796
|
+
inline facebook::jsi::Value copyToTypedArray(
|
|
797
|
+
facebook::jsi::Runtime& rt, const char* arrayName, const void* data, size_t count, size_t bytes) {
|
|
798
|
+
facebook::jsi::Object array = rt.global().getPropertyAsFunction(rt, arrayName)
|
|
799
|
+
.callAsConstructor(rt, static_cast<double>(count)).asObject(rt);
|
|
800
|
+
if (bytes > 0) {
|
|
801
|
+
facebook::jsi::ArrayBuffer buffer = array.getProperty(rt, "buffer").asObject(rt).getArrayBuffer(rt);
|
|
802
|
+
memcpy(buffer.data(rt), data, bytes);
|
|
803
|
+
}
|
|
804
|
+
return facebook::jsi::Value(std::move(array));
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
inline void copyFromTypedArray(facebook::jsi::Runtime& rt, const facebook::jsi::Value& typedArray, void* data, size_t bytes) {
|
|
808
|
+
if (bytes == 0) return;
|
|
809
|
+
facebook::jsi::Object view = typedArray.asObject(rt);
|
|
810
|
+
size_t offset = static_cast<size_t>(view.getProperty(rt, "byteOffset").asNumber());
|
|
811
|
+
facebook::jsi::ArrayBuffer buffer = view.getProperty(rt, "buffer").asObject(rt).getArrayBuffer(rt);
|
|
812
|
+
if (offset > buffer.size(rt) || bytes > buffer.size(rt) - offset) {
|
|
813
|
+
throw facebook::jsi::JSINativeException("crossbind: the typed array is smaller than the native buffer it fills");
|
|
814
|
+
}
|
|
815
|
+
memcpy(data, buffer.data(rt) + offset, bytes);
|
|
816
|
+
}
|
|
817
|
+
|
|
700
818
|
} // namespace internal
|
|
701
819
|
|
|
702
820
|
template<typename ElementType>
|
|
@@ -734,9 +852,13 @@ struct BindingType<memory_view<ElementType>> {
|
|
|
734
852
|
// on the C++ side, nor is toWireType implemented in
|
|
735
853
|
// JavaScript.)
|
|
736
854
|
typedef memory_view<ElementType> WireType;
|
|
855
|
+
typedef const facebook::jsi::Value WireType2;
|
|
737
856
|
static WireType toWireType(const memory_view<ElementType>& mv) {
|
|
738
857
|
return mv;
|
|
739
858
|
}
|
|
859
|
+
static WireType2 toWireType2(facebook::jsi::Runtime& rt, const memory_view<ElementType>& mv) {
|
|
860
|
+
return copyToTypedArray(rt, typedArrayNameOf<ElementType>(), mv.data, mv.size, mv.size * sizeof(ElementType));
|
|
861
|
+
}
|
|
740
862
|
};
|
|
741
863
|
|
|
742
864
|
} // namespace internal
|