koffi 2.0.1 → 2.1.0-beta.1
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/CMakeLists.txt +1 -8
- package/benchmark/atoi_koffi.js +12 -8
- package/benchmark/atoi_napi.js +12 -8
- package/benchmark/atoi_node_ffi.js +11 -10
- package/benchmark/raylib_cc.cc +12 -9
- package/benchmark/raylib_koffi.js +15 -13
- package/benchmark/raylib_node_ffi.js +15 -13
- package/benchmark/raylib_node_raylib.js +14 -11
- package/build/qemu/2.1.0-beta.1/koffi_darwin_arm64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.1/koffi_darwin_x64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.1/koffi_freebsd_arm64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.1/koffi_freebsd_ia32.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.1/koffi_freebsd_x64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.1/koffi_linux_arm32hf.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.1/koffi_linux_arm64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.1/koffi_linux_ia32.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.1/koffi_linux_riscv64hf64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.1/koffi_linux_x64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.1/koffi_openbsd_ia32.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.1/koffi_openbsd_x64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.1/koffi_win32_arm64.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.1/koffi_win32_ia32.tar.gz +0 -0
- package/build/qemu/2.1.0-beta.1/koffi_win32_x64.tar.gz +0 -0
- package/doc/changes.md +12 -8
- package/doc/dist/doctrees/changes.doctree +0 -0
- package/doc/dist/doctrees/environment.pickle +0 -0
- package/doc/dist/doctrees/functions.doctree +0 -0
- package/doc/dist/doctrees/index.doctree +0 -0
- package/doc/dist/doctrees/types.doctree +0 -0
- package/doc/dist/html/_sources/changes.md.txt +12 -8
- package/doc/dist/html/_sources/functions.md.txt +4 -4
- package/doc/dist/html/_sources/types.md.txt +69 -34
- package/doc/dist/html/benchmarks.html +1 -1
- package/doc/dist/html/changes.html +44 -33
- package/doc/dist/html/contribute.html +1 -1
- package/doc/dist/html/functions.html +7 -7
- package/doc/dist/html/genindex.html +1 -1
- package/doc/dist/html/index.html +3 -3
- package/doc/dist/html/memory.html +1 -1
- package/doc/dist/html/objects.inv +0 -0
- package/doc/dist/html/platforms.html +2 -2
- package/doc/dist/html/search.html +1 -1
- package/doc/dist/html/searchindex.js +1 -1
- package/doc/dist/html/start.html +1 -1
- package/doc/dist/html/types.html +77 -40
- package/doc/functions.md +4 -4
- package/doc/types.md +68 -33
- package/package.json +2 -2
- package/qemu/qemu.js +1 -1
- package/src/call.cc +42 -26
- package/src/call.hh +15 -3
- package/src/ffi.cc +70 -13
- package/src/ffi.hh +7 -0
- package/src/index.js +4 -2
- package/src/parser.cc +3 -5
- package/src/util.cc +44 -1
- package/src/util.hh +4 -0
- package/test/async.js +1 -2
- package/test/callbacks.js +2 -3
- package/test/misc.c +21 -1
- package/test/raylib.js +1 -1
- package/test/sqlite.js +3 -3
- package/test/sync.js +27 -3
- package/build/qemu/2.0.1/koffi_darwin_arm64.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_darwin_x64.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_freebsd_arm64.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_freebsd_ia32.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_freebsd_x64.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_linux_arm32hf.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_linux_arm64.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_linux_ia32.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_linux_riscv64hf64.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_linux_x64.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_openbsd_ia32.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_openbsd_x64.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_win32_arm64.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_win32_ia32.tar.gz +0 -0
- package/build/qemu/2.0.1/koffi_win32_x64.tar.gz +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koffi",
|
|
3
|
-
"version": "2.0.1",
|
|
3
|
+
"version": "2.1.0-beta.1",
|
|
4
4
|
"description": "Fast and simple C FFI (foreign function interface) for Node.js",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"foreign",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"license": "AGPL-3.0",
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"cnoke": "^3.
|
|
28
|
+
"cnoke": "^3.2.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"chalk": "^4.1.2",
|
package/qemu/qemu.js
CHANGED
|
@@ -42,7 +42,7 @@ let qemu_prefix = null;
|
|
|
42
42
|
main();
|
|
43
43
|
|
|
44
44
|
async function main() {
|
|
45
|
-
script_dir = fs.realpathSync(
|
|
45
|
+
script_dir = fs.realpathSync(__dirname);
|
|
46
46
|
root_dir = fs.realpathSync(script_dir + '/../..');
|
|
47
47
|
|
|
48
48
|
// All the code assumes we are working from the script directory
|
package/src/call.cc
CHANGED
|
@@ -567,24 +567,26 @@ bool CallData::PushTypedArray(Napi::TypedArray array, Size len, const TypeInfo *
|
|
|
567
567
|
return false;
|
|
568
568
|
}
|
|
569
569
|
|
|
570
|
-
Size offset = 0;
|
|
571
570
|
const uint8_t *buf = (const uint8_t *)array.ArrayBuffer().Data();
|
|
572
571
|
|
|
573
|
-
if (RG_UNLIKELY(array.TypedArrayType() != GetTypedArrayType(ref)
|
|
572
|
+
if (RG_UNLIKELY(array.TypedArrayType() != GetTypedArrayType(ref) &&
|
|
573
|
+
ref != instance->void_type)) {
|
|
574
574
|
ThrowError<Napi::TypeError>(env, "Cannot use %1 value for %2 array", GetValueType(instance, array), ref->name);
|
|
575
575
|
return false;
|
|
576
576
|
}
|
|
577
577
|
|
|
578
578
|
if (realign) {
|
|
579
|
+
Size offset = 0;
|
|
580
|
+
Size size = (Size)array.ElementSize();
|
|
581
|
+
|
|
579
582
|
for (uint32_t i = 0; i < len; i++) {
|
|
580
|
-
|
|
581
|
-
offset = AlignLen(offset, align);
|
|
583
|
+
offset = AlignLen(offset, realign);
|
|
582
584
|
|
|
583
585
|
uint8_t *dest = origin + offset;
|
|
586
|
+
const uint8_t *src = buf + i * size;
|
|
584
587
|
|
|
585
|
-
memcpy(dest,
|
|
586
|
-
|
|
587
|
-
offset += ref->size;
|
|
588
|
+
memcpy(dest, src, size);
|
|
589
|
+
offset += size;
|
|
588
590
|
}
|
|
589
591
|
} else {
|
|
590
592
|
memcpy_safe(origin, buf, (size_t)array.ByteLength());
|
|
@@ -625,6 +627,16 @@ bool CallData::PushStringArray(Napi::Value obj, const TypeInfo *type, uint8_t *o
|
|
|
625
627
|
|
|
626
628
|
bool CallData::PushPointer(Napi::Value value, const ParameterInfo ¶m, void **out_ptr)
|
|
627
629
|
{
|
|
630
|
+
const TypeInfo *type = param.type;
|
|
631
|
+
|
|
632
|
+
if (CheckValueTag(instance, value, &CastMarker)) {
|
|
633
|
+
Napi::External<ValueCast> external = value.As<Napi::External<ValueCast>>();
|
|
634
|
+
ValueCast *cast = external.Data();
|
|
635
|
+
|
|
636
|
+
value = cast->ref.Value();
|
|
637
|
+
type = cast->type;
|
|
638
|
+
}
|
|
639
|
+
|
|
628
640
|
switch (value.Type()) {
|
|
629
641
|
case napi_undefined:
|
|
630
642
|
case napi_null: {
|
|
@@ -633,9 +645,11 @@ bool CallData::PushPointer(Napi::Value value, const ParameterInfo ¶m, void *
|
|
|
633
645
|
} break;
|
|
634
646
|
|
|
635
647
|
case napi_external: {
|
|
636
|
-
RG_ASSERT(
|
|
648
|
+
RG_ASSERT(type->primitive == PrimitiveKind::Pointer);
|
|
637
649
|
|
|
638
|
-
if (RG_UNLIKELY(!CheckValueTag(instance, value,
|
|
650
|
+
if (RG_UNLIKELY(!CheckValueTag(instance, value, type->ref.marker) &&
|
|
651
|
+
!CheckValueTag(instance, value, instance->void_type) &&
|
|
652
|
+
type->ref.type != instance->void_type))
|
|
639
653
|
goto unexpected;
|
|
640
654
|
|
|
641
655
|
*out_ptr = value.As<Napi::External<uint8_t>>().Data();
|
|
@@ -649,12 +663,12 @@ bool CallData::PushPointer(Napi::Value value, const ParameterInfo ¶m, void *
|
|
|
649
663
|
Napi::Array array = value.As<Napi::Array>();
|
|
650
664
|
|
|
651
665
|
Size len = (Size)array.Length();
|
|
652
|
-
Size size = len *
|
|
666
|
+
Size size = len * type->ref.type->size;
|
|
653
667
|
|
|
654
668
|
ptr = AllocHeap(size, 16);
|
|
655
669
|
|
|
656
670
|
if (param.directions & 1) {
|
|
657
|
-
if (!PushNormalArray(array, len,
|
|
671
|
+
if (!PushNormalArray(array, len, type->ref.type, ptr))
|
|
658
672
|
return false;
|
|
659
673
|
} else {
|
|
660
674
|
memset(ptr, 0, size);
|
|
@@ -668,27 +682,28 @@ bool CallData::PushPointer(Napi::Value value, const ParameterInfo ¶m, void *
|
|
|
668
682
|
ptr = AllocHeap(size, 16);
|
|
669
683
|
|
|
670
684
|
if (param.directions & 1) {
|
|
671
|
-
if (!PushTypedArray(array, len,
|
|
685
|
+
if (!PushTypedArray(array, len, type->ref.type, ptr))
|
|
672
686
|
return false;
|
|
673
687
|
} else {
|
|
674
|
-
if (RG_UNLIKELY(array.TypedArrayType() != GetTypedArrayType(
|
|
675
|
-
|
|
688
|
+
if (RG_UNLIKELY(array.TypedArrayType() != GetTypedArrayType(type->ref.type) &&
|
|
689
|
+
type->ref.type != instance->void_type)) {
|
|
690
|
+
ThrowError<Napi::TypeError>(env, "Cannot use %1 value for %2 array", GetValueType(instance, array), type->ref.type->name);
|
|
676
691
|
return false;
|
|
677
692
|
}
|
|
678
693
|
|
|
679
694
|
memset(ptr, 0, size);
|
|
680
695
|
}
|
|
681
|
-
} else if (RG_LIKELY(
|
|
696
|
+
} else if (RG_LIKELY(type->ref.type->primitive == PrimitiveKind::Record)) {
|
|
682
697
|
Napi::Object obj = value.As<Napi::Object>();
|
|
683
698
|
RG_ASSERT(IsObject(value));
|
|
684
699
|
|
|
685
|
-
ptr = AllocHeap(
|
|
700
|
+
ptr = AllocHeap(type->ref.type->size, 16);
|
|
686
701
|
|
|
687
702
|
if (param.directions & 1) {
|
|
688
|
-
if (!PushObject(obj,
|
|
703
|
+
if (!PushObject(obj, type->ref.type, ptr))
|
|
689
704
|
return false;
|
|
690
705
|
} else {
|
|
691
|
-
memset(ptr, 0,
|
|
706
|
+
memset(ptr, 0, type->size);
|
|
692
707
|
}
|
|
693
708
|
} else {
|
|
694
709
|
goto unexpected;
|
|
@@ -701,7 +716,7 @@ bool CallData::PushPointer(Napi::Value value, const ParameterInfo ¶m, void *
|
|
|
701
716
|
RG_ASSERT(status == napi_ok);
|
|
702
717
|
|
|
703
718
|
out->ptr = ptr;
|
|
704
|
-
out->type =
|
|
719
|
+
out->type = type->ref.type;
|
|
705
720
|
}
|
|
706
721
|
|
|
707
722
|
*out_ptr = ptr;
|
|
@@ -712,7 +727,7 @@ bool CallData::PushPointer(Napi::Value value, const ParameterInfo ¶m, void *
|
|
|
712
727
|
}
|
|
713
728
|
|
|
714
729
|
unexpected:
|
|
715
|
-
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected %3", GetValueType(instance, value), param.offset + 1,
|
|
730
|
+
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected %3", GetValueType(instance, value), param.offset + 1, type->name);
|
|
716
731
|
return false;
|
|
717
732
|
}
|
|
718
733
|
|
|
@@ -1016,24 +1031,25 @@ void CallData::PopNormalArray(Napi::Array array, const uint8_t *origin, const Ty
|
|
|
1016
1031
|
void CallData::PopTypedArray(Napi::TypedArray array, const uint8_t *origin, const TypeInfo *ref, int16_t realign)
|
|
1017
1032
|
{
|
|
1018
1033
|
RG_ASSERT(array.IsTypedArray());
|
|
1019
|
-
RG_ASSERT(GetTypedArrayType(ref) == array.TypedArrayType()
|
|
1034
|
+
RG_ASSERT(GetTypedArrayType(ref) == array.TypedArrayType() ||
|
|
1035
|
+
ref == instance->void_type);
|
|
1020
1036
|
|
|
1021
1037
|
uint8_t *buf = (uint8_t *)array.ArrayBuffer().Data();
|
|
1022
1038
|
|
|
1023
1039
|
if (realign) {
|
|
1024
1040
|
Size offset = 0;
|
|
1025
1041
|
Size len = (Size)array.ElementLength();
|
|
1042
|
+
Size size = (Size)array.ElementSize();
|
|
1026
1043
|
|
|
1027
1044
|
for (Size i = 0; i < len; i++) {
|
|
1028
|
-
|
|
1029
|
-
offset = AlignLen(offset, align);
|
|
1045
|
+
offset = AlignLen(offset, realign);
|
|
1030
1046
|
|
|
1031
|
-
uint8_t *dest = buf + i *
|
|
1047
|
+
uint8_t *dest = buf + i * size;
|
|
1032
1048
|
const uint8_t *src = origin + offset;
|
|
1033
1049
|
|
|
1034
|
-
memcpy(dest, src,
|
|
1050
|
+
memcpy(dest, src, size);
|
|
1035
1051
|
|
|
1036
|
-
offset +=
|
|
1052
|
+
offset += size;
|
|
1037
1053
|
}
|
|
1038
1054
|
} else {
|
|
1039
1055
|
memcpy_safe(buf, origin, (size_t)array.ByteLength());
|
package/src/call.hh
CHANGED
|
@@ -71,9 +71,21 @@ public:
|
|
|
71
71
|
CallData(Napi::Env env, InstanceData *instance, const FunctionInfo *func, InstanceMemory *mem);
|
|
72
72
|
~CallData();
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
#ifdef UNITY_BUILD
|
|
75
|
+
#ifdef _MSC_VER
|
|
76
|
+
#define INLINE_IF_UNITY __forceinline
|
|
77
|
+
#else
|
|
78
|
+
#define INLINE_IF_UNITY __attribute__((always_inline)) inline
|
|
79
|
+
#endif
|
|
80
|
+
#else
|
|
81
|
+
#define INLINE_IF_UNITY
|
|
82
|
+
#endif
|
|
83
|
+
|
|
84
|
+
INLINE_IF_UNITY bool Prepare(const Napi::CallbackInfo &info);
|
|
85
|
+
INLINE_IF_UNITY void Execute();
|
|
86
|
+
INLINE_IF_UNITY Napi::Value Complete();
|
|
87
|
+
|
|
88
|
+
#undef INLINE_IF_UNITY
|
|
77
89
|
|
|
78
90
|
void Relay(Size idx, uint8_t *own_sp, uint8_t *caller_sp, BackRegisters *out_reg);
|
|
79
91
|
|
package/src/ffi.cc
CHANGED
|
@@ -42,6 +42,7 @@ namespace RG {
|
|
|
42
42
|
|
|
43
43
|
// Value does not matter, the tag system uses memory addresses
|
|
44
44
|
const int TypeInfoMarker = 0xDEADBEEF;
|
|
45
|
+
const int CastMarker = 0xDEADBEEF;
|
|
45
46
|
|
|
46
47
|
static bool ChangeMemorySize(const char *name, Napi::Value value, Size *out_size)
|
|
47
48
|
{
|
|
@@ -209,18 +210,40 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
|
|
|
209
210
|
|
|
210
211
|
std::string key = ((Napi::Value)keys[i]).As<Napi::String>();
|
|
211
212
|
Napi::Value value = obj[key];
|
|
213
|
+
int16_t align = 0;
|
|
212
214
|
|
|
213
215
|
member.name = DuplicateString(key.c_str(), &instance->str_alloc).ptr;
|
|
216
|
+
|
|
217
|
+
if (value.IsArray()) {
|
|
218
|
+
Napi::Array array = value.As<Napi::Array>();
|
|
219
|
+
|
|
220
|
+
if (array.Length() != 2 || !((Napi::Value)array[0u]).IsNumber()) {
|
|
221
|
+
ThrowError<Napi::Error>(env, "Member specifier array must contain alignement value and type");
|
|
222
|
+
return env.Null();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
int64_t align64 = ((Napi::Value)array[0u]).As<Napi::Number>().Int64Value();
|
|
226
|
+
|
|
227
|
+
if (align64 < 1 || align64 > 64) {
|
|
228
|
+
ThrowError<Napi::Error>(env, "Alignment value must be between 1 and 64");
|
|
229
|
+
return env.Null();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
value = array[1u];
|
|
233
|
+
align = (int16_t)align64;
|
|
234
|
+
}
|
|
235
|
+
|
|
214
236
|
member.type = ResolveType(value);
|
|
215
237
|
if (!member.type)
|
|
216
238
|
return env.Null();
|
|
217
|
-
if (member.type
|
|
218
|
-
member.type->primitive == PrimitiveKind::Prototype) {
|
|
239
|
+
if (!CanStoreType(member.type)) {
|
|
219
240
|
ThrowError<Napi::TypeError>(env, "Type %1 cannot be used as a member (maybe try %1 *)", member.type->name);
|
|
220
241
|
return env.Null();
|
|
221
242
|
}
|
|
222
243
|
|
|
223
|
-
|
|
244
|
+
if (!align) {
|
|
245
|
+
align = pad ? member.type->align : 1;
|
|
246
|
+
}
|
|
224
247
|
member.offset = (int16_t)AlignLen(type->size, align);
|
|
225
248
|
|
|
226
249
|
type->size = (int16_t)(member.offset + member.type->size);
|
|
@@ -264,7 +287,7 @@ static Napi::Value CreatePackedStructType(const Napi::CallbackInfo &info)
|
|
|
264
287
|
return CreateStructType(info, false);
|
|
265
288
|
}
|
|
266
289
|
|
|
267
|
-
static Napi::Value
|
|
290
|
+
static Napi::Value CreateOpaqueType(const Napi::CallbackInfo &info)
|
|
268
291
|
{
|
|
269
292
|
Napi::Env env = info.Env();
|
|
270
293
|
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
@@ -618,8 +641,8 @@ static bool ParseClassicFunction(Napi::Env env, Napi::String name, Napi::Value r
|
|
|
618
641
|
func->ret.type = ResolveType(ret);
|
|
619
642
|
if (!func->ret.type)
|
|
620
643
|
return false;
|
|
621
|
-
if (func->ret.type
|
|
622
|
-
ThrowError<Napi::
|
|
644
|
+
if (!CanReturnType(func->ret.type)) {
|
|
645
|
+
ThrowError<Napi::TypeError>(env, "You are not allowed to directly return %1 values (maybe try %1 *)", func->ret.type->name);
|
|
623
646
|
return false;
|
|
624
647
|
}
|
|
625
648
|
|
|
@@ -645,9 +668,7 @@ static bool ParseClassicFunction(Napi::Env env, Napi::String name, Napi::Value r
|
|
|
645
668
|
param.type = ResolveType(parameters[j], ¶m.directions);
|
|
646
669
|
if (!param.type)
|
|
647
670
|
return false;
|
|
648
|
-
if (param.type
|
|
649
|
-
param.type->primitive == PrimitiveKind::Array ||
|
|
650
|
-
param.type->primitive == PrimitiveKind::Prototype) {
|
|
671
|
+
if (!CanPassType(param.type)) {
|
|
651
672
|
ThrowError<Napi::TypeError>(env, "Type %1 cannot be used as a parameter (maybe try %1 *)", param.type->name);
|
|
652
673
|
return false;
|
|
653
674
|
}
|
|
@@ -988,9 +1009,7 @@ static Napi::Value TranslateVariadicCall(const Napi::CallbackInfo &info)
|
|
|
988
1009
|
param.type = ResolveType(info[i], ¶m.directions);
|
|
989
1010
|
if (RG_UNLIKELY(!param.type))
|
|
990
1011
|
return env.Null();
|
|
991
|
-
if (RG_UNLIKELY(param.type
|
|
992
|
-
param.type->primitive == PrimitiveKind::Array ||
|
|
993
|
-
param.type->primitive == PrimitiveKind::Prototype)) {
|
|
1012
|
+
if (RG_UNLIKELY(!CanPassType(param.type))) {
|
|
994
1013
|
ThrowError<Napi::TypeError>(env, "Type %1 cannot be used as a parameter (maybe try %1 *)", PrimitiveKindNames[(int)param.type->primitive]);
|
|
995
1014
|
return env.Null();
|
|
996
1015
|
}
|
|
@@ -1434,6 +1453,8 @@ static void RegisterPrimitiveType(Napi::Env env, Napi::Object map, std::initiali
|
|
|
1434
1453
|
|
|
1435
1454
|
static Napi::Object InitBaseTypes(Napi::Env env)
|
|
1436
1455
|
{
|
|
1456
|
+
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
1457
|
+
|
|
1437
1458
|
Napi::Object types = Napi::Object::New(env);
|
|
1438
1459
|
|
|
1439
1460
|
RegisterPrimitiveType(env, types, {"void"}, PrimitiveKind::Void, 0, 0);
|
|
@@ -1464,6 +1485,9 @@ static Napi::Object InitBaseTypes(Napi::Env env)
|
|
|
1464
1485
|
RegisterPrimitiveType(env, types, {"char *", "str", "string"}, PrimitiveKind::String, RG_SIZE(void *), alignof(void *), "char");
|
|
1465
1486
|
RegisterPrimitiveType(env, types, {"char16_t *", "char16 *", "str16", "string16"}, PrimitiveKind::String16, RG_SIZE(void *), alignof(void *), "char16_t");
|
|
1466
1487
|
|
|
1488
|
+
instance->void_type = instance->types_map.FindValue("void", nullptr);
|
|
1489
|
+
RG_ASSERT(instance->void_type);
|
|
1490
|
+
|
|
1467
1491
|
types.Freeze();
|
|
1468
1492
|
|
|
1469
1493
|
return types;
|
|
@@ -1515,6 +1539,37 @@ InstanceData::~InstanceData()
|
|
|
1515
1539
|
}
|
|
1516
1540
|
}
|
|
1517
1541
|
|
|
1542
|
+
static Napi::Value CastValue(const Napi::CallbackInfo &info)
|
|
1543
|
+
{
|
|
1544
|
+
Napi::Env env = info.Env();
|
|
1545
|
+
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
1546
|
+
|
|
1547
|
+
if (RG_UNLIKELY(info.Length() < 2)) {
|
|
1548
|
+
ThrowError<Napi::TypeError>(env, "Expected 2 arguments, got %1", info.Length());
|
|
1549
|
+
return env.Null();
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
Napi::Value value = info[0];
|
|
1553
|
+
|
|
1554
|
+
const TypeInfo *type = ResolveType(info[1]);
|
|
1555
|
+
if (RG_UNLIKELY(!type))
|
|
1556
|
+
return env.Null();
|
|
1557
|
+
if (type->primitive != PrimitiveKind::Pointer) {
|
|
1558
|
+
ThrowError<Napi::TypeError>(env, "Only pointer types can be used for casting");
|
|
1559
|
+
return env.Null();
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
ValueCast *cast = new ValueCast;
|
|
1563
|
+
|
|
1564
|
+
cast->ref.Reset(value, 1);
|
|
1565
|
+
cast->type = type;
|
|
1566
|
+
|
|
1567
|
+
Napi::External<ValueCast> external = Napi::External<ValueCast>::New(env, cast, [](Napi::Env, ValueCast *cast) { delete cast; });
|
|
1568
|
+
SetValueTag(instance, external, &CastMarker);
|
|
1569
|
+
|
|
1570
|
+
return external;
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1518
1573
|
template <typename Func>
|
|
1519
1574
|
static void SetExports(Napi::Env env, Func func)
|
|
1520
1575
|
{
|
|
@@ -1522,7 +1577,7 @@ static void SetExports(Napi::Env env, Func func)
|
|
|
1522
1577
|
|
|
1523
1578
|
func("struct", Napi::Function::New(env, CreatePaddedStructType));
|
|
1524
1579
|
func("pack", Napi::Function::New(env, CreatePackedStructType));
|
|
1525
|
-
func("
|
|
1580
|
+
func("opaque", Napi::Function::New(env, CreateOpaqueType));
|
|
1526
1581
|
func("pointer", Napi::Function::New(env, CreatePointerType));
|
|
1527
1582
|
func("array", Napi::Function::New(env, CreateArrayType));
|
|
1528
1583
|
func("callback", Napi::Function::New(env, CreateCallbackType));
|
|
@@ -1545,6 +1600,8 @@ static void SetExports(Napi::Env env, Func func)
|
|
|
1545
1600
|
func("register", Napi::Function::New(env, RegisterCallback));
|
|
1546
1601
|
func("unregister", Napi::Function::New(env, UnregisterCallback));
|
|
1547
1602
|
|
|
1603
|
+
func("as", Napi::Function::New(env, CastValue));
|
|
1604
|
+
|
|
1548
1605
|
#if defined(_WIN32)
|
|
1549
1606
|
func("extension", Napi::String::New(env, ".dll"));
|
|
1550
1607
|
#elif defined(__APPLE__)
|
package/src/ffi.hh
CHANGED
|
@@ -32,6 +32,7 @@ static const Size MaxOutParameters = 4;
|
|
|
32
32
|
static const Size MaxTrampolines = 16;
|
|
33
33
|
|
|
34
34
|
extern const int TypeInfoMarker;
|
|
35
|
+
extern const int CastMarker;
|
|
35
36
|
|
|
36
37
|
enum class PrimitiveKind {
|
|
37
38
|
Void,
|
|
@@ -171,6 +172,11 @@ struct ParameterInfo {
|
|
|
171
172
|
#endif
|
|
172
173
|
};
|
|
173
174
|
|
|
175
|
+
struct ValueCast {
|
|
176
|
+
Napi::Reference<Napi::Value> ref;
|
|
177
|
+
const TypeInfo *type;
|
|
178
|
+
};
|
|
179
|
+
|
|
174
180
|
// Also used for callbacks, even though many members are not used in this case
|
|
175
181
|
struct FunctionInfo {
|
|
176
182
|
mutable std::atomic_int refcount {1};
|
|
@@ -230,6 +236,7 @@ struct InstanceData {
|
|
|
230
236
|
|
|
231
237
|
bool debug;
|
|
232
238
|
uint64_t tag_lower;
|
|
239
|
+
const TypeInfo *void_type;
|
|
233
240
|
|
|
234
241
|
LocalArray<InstanceMemory *, 9> memories;
|
|
235
242
|
int temporaries = 0;
|
package/src/index.js
CHANGED
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
-
const
|
|
18
|
+
const util = require('util');
|
|
19
19
|
|
|
20
|
-
let filename =
|
|
20
|
+
let filename = __dirname + '/../build/koffi.node';
|
|
21
21
|
module.exports = require(filename);
|
|
22
|
+
|
|
23
|
+
module.exports.handle = util.deprecate(module.exports.opaque, 'The koffi.handle() function was deprecated in Koffi 2.1, use koffi.opaque() instead', 'KOFFI001');
|
package/src/parser.cc
CHANGED
|
@@ -28,8 +28,8 @@ bool PrototypeParser::Parse(const char *str, FunctionInfo *out_func)
|
|
|
28
28
|
Tokenize(str);
|
|
29
29
|
|
|
30
30
|
out_func->ret.type = ParseType();
|
|
31
|
-
if (out_func->ret.type
|
|
32
|
-
MarkError("You are not allowed to directly return
|
|
31
|
+
if (!CanReturnType(out_func->ret.type)) {
|
|
32
|
+
MarkError("You are not allowed to directly return %1 values (maybe try %1 *)", out_func->ret.type->name);
|
|
33
33
|
return false;
|
|
34
34
|
}
|
|
35
35
|
if (Match("__cdecl")) {
|
|
@@ -65,9 +65,7 @@ bool PrototypeParser::Parse(const char *str, FunctionInfo *out_func)
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
param.type = ParseType();
|
|
68
|
-
if (param.type
|
|
69
|
-
param.type->primitive == PrimitiveKind::Array ||
|
|
70
|
-
param.type->primitive == PrimitiveKind::Prototype) {
|
|
68
|
+
if (!CanPassType(param.type)) {
|
|
71
69
|
MarkError("Type %1 cannot be used as a parameter (maybe try %1 *)", param.type->name);
|
|
72
70
|
return false;
|
|
73
71
|
}
|
package/src/util.cc
CHANGED
|
@@ -170,10 +170,53 @@ const TypeInfo *MakePointerType(InstanceData *instance, const TypeInfo *ref, int
|
|
|
170
170
|
return ref;
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
+
bool CanPassType(const TypeInfo *type)
|
|
174
|
+
{
|
|
175
|
+
if (type->primitive == PrimitiveKind::Void)
|
|
176
|
+
return false;
|
|
177
|
+
if (type->primitive == PrimitiveKind::Array)
|
|
178
|
+
return false;
|
|
179
|
+
if (type->primitive == PrimitiveKind::Prototype)
|
|
180
|
+
return false;
|
|
181
|
+
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
bool CanReturnType(const TypeInfo *type)
|
|
186
|
+
{
|
|
187
|
+
if (type->primitive == PrimitiveKind::Void && !TestStr(type->name, "void"))
|
|
188
|
+
return false;
|
|
189
|
+
if (type->primitive == PrimitiveKind::Array)
|
|
190
|
+
return false;
|
|
191
|
+
if (type->primitive == PrimitiveKind::Prototype)
|
|
192
|
+
return false;
|
|
193
|
+
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
bool CanStoreType(const TypeInfo *type)
|
|
198
|
+
{
|
|
199
|
+
if (type->primitive == PrimitiveKind::Void)
|
|
200
|
+
return false;
|
|
201
|
+
if (type->primitive == PrimitiveKind::Prototype)
|
|
202
|
+
return false;
|
|
203
|
+
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
|
|
173
207
|
const char *GetValueType(const InstanceData *instance, Napi::Value value)
|
|
174
208
|
{
|
|
209
|
+
if (CheckValueTag(instance, value, &CastMarker)) {
|
|
210
|
+
Napi::External<ValueCast> external = value.As<Napi::External<ValueCast>>();
|
|
211
|
+
ValueCast *cast = external.Data();
|
|
212
|
+
|
|
213
|
+
return cast->type->name;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (CheckValueTag(instance, value, &TypeInfoMarker))
|
|
217
|
+
return "Type";
|
|
175
218
|
for (const TypeInfo &type: instance->types) {
|
|
176
|
-
if (CheckValueTag(instance, value, type.ref.marker))
|
|
219
|
+
if (type.ref.marker && CheckValueTag(instance, value, type.ref.marker))
|
|
177
220
|
return type.name;
|
|
178
221
|
}
|
|
179
222
|
|
package/src/util.hh
CHANGED
|
@@ -71,6 +71,10 @@ const TypeInfo *ResolveType(Napi::Value value, int *out_directions = nullptr);
|
|
|
71
71
|
const TypeInfo *ResolveType(InstanceData *instance, Span<const char> str, int *out_directions = nullptr);
|
|
72
72
|
const TypeInfo *MakePointerType(InstanceData *instance, const TypeInfo *type, int count = 1);
|
|
73
73
|
|
|
74
|
+
bool CanPassType(const TypeInfo *type);
|
|
75
|
+
bool CanReturnType(const TypeInfo *type);
|
|
76
|
+
bool CanStoreType(const TypeInfo *type);
|
|
77
|
+
|
|
74
78
|
// Can be slow, only use for error messages
|
|
75
79
|
const char *GetValueType(const InstanceData *instance, Napi::Value value);
|
|
76
80
|
|
package/test/async.js
CHANGED
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
|
|
16
16
|
const koffi = require('./build/koffi.node');
|
|
17
17
|
const assert = require('assert');
|
|
18
|
-
const path = require('path');
|
|
19
18
|
|
|
20
19
|
const PackedBFG = koffi.pack('PackedBFG', {
|
|
21
20
|
a: 'int8_t',
|
|
@@ -42,7 +41,7 @@ async function main() {
|
|
|
42
41
|
}
|
|
43
42
|
|
|
44
43
|
async function test() {
|
|
45
|
-
const lib_filename =
|
|
44
|
+
const lib_filename = __dirname + '/build/misc' + koffi.extension;
|
|
46
45
|
const lib = koffi.load(lib_filename);
|
|
47
46
|
|
|
48
47
|
const ConcatenateToInt1 = lib.func('ConcatenateToInt1', 'int64_t', Array(12).fill('int8_t'));
|
package/test/callbacks.js
CHANGED
|
@@ -15,11 +15,10 @@
|
|
|
15
15
|
|
|
16
16
|
const koffi = require('./build/koffi.node');
|
|
17
17
|
const assert = require('assert');
|
|
18
|
-
const path = require('path');
|
|
19
18
|
|
|
20
19
|
const BFG = koffi.struct('BFG', {
|
|
21
20
|
a: 'int8_t',
|
|
22
|
-
b: 'int64_t',
|
|
21
|
+
b: [16, 'int64_t'],
|
|
23
22
|
c: 'char',
|
|
24
23
|
d: 'str',
|
|
25
24
|
e: 'short',
|
|
@@ -55,7 +54,7 @@ async function main() {
|
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
async function test() {
|
|
58
|
-
const lib_filename =
|
|
57
|
+
const lib_filename = __dirname + '/build/misc' + koffi.extension;
|
|
59
58
|
const lib = koffi.load(lib_filename);
|
|
60
59
|
|
|
61
60
|
const CallJS = lib.func('int CallJS(const char *str, SimpleCallback *cb)');
|
package/test/misc.c
CHANGED
|
@@ -90,7 +90,7 @@ typedef struct IJK8 { int64_t i; int64_t j; int64_t k; } IJK8;
|
|
|
90
90
|
|
|
91
91
|
typedef struct BFG {
|
|
92
92
|
int8_t a;
|
|
93
|
-
int64_t b;
|
|
93
|
+
char _pad1[15]; int64_t b;
|
|
94
94
|
signed char c;
|
|
95
95
|
const char *d;
|
|
96
96
|
short e;
|
|
@@ -399,6 +399,15 @@ EXPORT PackedBFG FASTCALL MakePackedBFG(int x, double y, PackedBFG *p, const cha
|
|
|
399
399
|
return bfg;
|
|
400
400
|
}
|
|
401
401
|
|
|
402
|
+
EXPORT void MakePolymorphBFG(int type, int x, double y, const char *str, void *p)
|
|
403
|
+
{
|
|
404
|
+
if (type == 0) {
|
|
405
|
+
MakeBFG(p, x, y, str);
|
|
406
|
+
} else if (type == 1) {
|
|
407
|
+
MakePackedBFG(x, y, p, str);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
402
411
|
#ifdef _WIN32
|
|
403
412
|
// Exported by DEF file
|
|
404
413
|
const char *ReturnBigString(const char *str)
|
|
@@ -633,3 +642,14 @@ EXPORT int CallCallback(int x)
|
|
|
633
642
|
{
|
|
634
643
|
return callback(x);
|
|
635
644
|
}
|
|
645
|
+
|
|
646
|
+
EXPORT void ReverseBytes(void *p, int len)
|
|
647
|
+
{
|
|
648
|
+
uint8_t *bytes = (uint8_t *)p;
|
|
649
|
+
|
|
650
|
+
for (int i = 0; i < len / 2; i++) {
|
|
651
|
+
uint8_t tmp = bytes[i];
|
|
652
|
+
bytes[i] = bytes[len - i - 1];
|
|
653
|
+
bytes[len - i - 1] = tmp;
|
|
654
|
+
}
|
|
655
|
+
}
|
package/test/raylib.js
CHANGED
|
@@ -83,7 +83,7 @@ async function main() {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
async function test() {
|
|
86
|
-
let lib_filename =
|
|
86
|
+
let lib_filename = __dirname + '/build/raylib' + koffi.extension;
|
|
87
87
|
let lib = koffi.load(lib_filename);
|
|
88
88
|
|
|
89
89
|
const InitWindow = lib.func('InitWindow', 'void', ['int', 'int', 'str']);
|
package/test/sqlite.js
CHANGED
|
@@ -20,8 +20,8 @@ const fs = require('fs');
|
|
|
20
20
|
const os = require('os');
|
|
21
21
|
const path = require('path');
|
|
22
22
|
|
|
23
|
-
const sqlite3 = koffi.
|
|
24
|
-
const sqlite3_stmt = koffi.
|
|
23
|
+
const sqlite3 = koffi.opaque('sqlite3');
|
|
24
|
+
const sqlite3_stmt = koffi.opaque('sqlite3_stmt');
|
|
25
25
|
|
|
26
26
|
main();
|
|
27
27
|
|
|
@@ -36,7 +36,7 @@ async function main() {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
async function test() {
|
|
39
|
-
let lib_filename =
|
|
39
|
+
let lib_filename = __dirname + '/build/sqlite3' + koffi.extension;
|
|
40
40
|
let lib = koffi.load(lib_filename);
|
|
41
41
|
|
|
42
42
|
const sqlite3_open_v2 = lib.func('sqlite3_open_v2', 'int', ['str', koffi.out(koffi.pointer(sqlite3, 2)), 'int', 'str']);
|