koffi 1.3.12 → 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 +8 -10
- package/ChangeLog.md +48 -16
- package/README.md +6 -0
- 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 +160 -1
- package/doc/conf.py +14 -1
- package/doc/contribute.md +0 -1
- package/doc/dist/doctrees/benchmarks.doctree +0 -0
- 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/.buildinfo +1 -1
- package/doc/dist/html/_sources/benchmarks.md.txt +2 -2
- package/doc/dist/html/_sources/changes.md.txt +160 -1
- package/doc/dist/html/_sources/functions.md.txt +17 -13
- package/doc/dist/html/_sources/types.md.txt +87 -35
- package/doc/dist/html/benchmarks.html +7 -3
- package/doc/dist/html/changes.html +241 -14
- package/doc/dist/html/contribute.html +5 -1
- package/doc/dist/html/functions.html +30 -23
- package/doc/dist/html/genindex.html +5 -1
- package/doc/dist/html/index.html +13 -19
- package/doc/dist/html/memory.html +7 -3
- package/doc/dist/html/objects.inv +0 -0
- package/doc/dist/html/platforms.html +6 -2
- package/doc/dist/html/search.html +5 -1
- package/doc/dist/html/searchindex.js +1 -1
- package/doc/dist/html/start.html +5 -1
- package/doc/dist/html/types.html +104 -43
- package/doc/functions.md +139 -15
- package/doc/templates/badges.html +5 -0
- package/doc/types.md +108 -40
- package/package.json +2 -2
- package/qemu/qemu.js +1 -1
- package/qemu/registry/machines.json +5 -5
- package/qemu/registry/sha256sum.txt +16 -16
- package/src/abi_arm32.cc +91 -19
- package/src/abi_arm32_fwd.S +121 -57
- package/src/abi_arm64.cc +91 -19
- package/src/abi_arm64_fwd.S +96 -0
- package/src/abi_arm64_fwd.asm +128 -0
- package/src/abi_riscv64.cc +89 -19
- package/src/abi_riscv64_fwd.S +96 -0
- package/src/abi_x64_sysv.cc +94 -22
- package/src/abi_x64_sysv_fwd.S +96 -0
- package/src/abi_x64_win.cc +89 -19
- package/src/abi_x64_win_fwd.asm +128 -0
- package/src/abi_x86.cc +94 -19
- package/src/abi_x86_fwd.S +96 -0
- package/src/abi_x86_fwd.asm +128 -0
- package/src/call.cc +128 -78
- package/src/call.hh +17 -4
- package/src/ffi.cc +514 -145
- package/src/ffi.hh +30 -9
- package/src/index.js +4 -2
- package/src/parser.cc +19 -44
- package/src/util.cc +160 -27
- package/src/util.hh +7 -2
- package/test/async.js +1 -2
- package/test/callbacks.js +56 -11
- package/test/misc.c +50 -15
- package/test/raylib.js +2 -2
- package/test/sqlite.js +27 -19
- package/test/sync.js +71 -35
- package/vendor/libcc/libcc.cc +18 -5
- package/vendor/libcc/libcc.hh +70 -23
- package/build/qemu/1.3.12/koffi_darwin_arm64.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_darwin_x64.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_freebsd_arm64.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_freebsd_ia32.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_freebsd_x64.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_linux_arm32hf.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_linux_arm64.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_linux_ia32.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_linux_riscv64hf64.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_linux_x64.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_openbsd_ia32.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_openbsd_x64.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_win32_arm64.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_win32_ia32.tar.gz +0 -0
- package/build/qemu/1.3.12/koffi_win32_x64.tar.gz +0 -0
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,
|
|
@@ -51,6 +52,7 @@ enum class PrimitiveKind {
|
|
|
51
52
|
Array,
|
|
52
53
|
Float32,
|
|
53
54
|
Float64,
|
|
55
|
+
Prototype,
|
|
54
56
|
Callback
|
|
55
57
|
};
|
|
56
58
|
static const char *const PrimitiveKindNames[] = {
|
|
@@ -71,6 +73,7 @@ static const char *const PrimitiveKindNames[] = {
|
|
|
71
73
|
"Array",
|
|
72
74
|
"Float32",
|
|
73
75
|
"Float64",
|
|
76
|
+
"Prototype",
|
|
74
77
|
"Callback"
|
|
75
78
|
};
|
|
76
79
|
|
|
@@ -78,6 +81,8 @@ struct TypeInfo;
|
|
|
78
81
|
struct RecordMember;
|
|
79
82
|
struct FunctionInfo;
|
|
80
83
|
|
|
84
|
+
typedef void DisposeFunc (Napi::Env env, const TypeInfo *type, const void *ptr);
|
|
85
|
+
|
|
81
86
|
struct TypeInfo {
|
|
82
87
|
enum class ArrayHint {
|
|
83
88
|
Array,
|
|
@@ -87,16 +92,22 @@ struct TypeInfo {
|
|
|
87
92
|
|
|
88
93
|
const char *name;
|
|
89
94
|
|
|
90
|
-
Napi::ObjectReference defn;
|
|
91
|
-
|
|
92
95
|
PrimitiveKind primitive;
|
|
93
96
|
int16_t size;
|
|
94
97
|
int16_t align;
|
|
95
98
|
|
|
99
|
+
DisposeFunc *dispose;
|
|
100
|
+
Napi::FunctionReference dispose_ref;
|
|
101
|
+
|
|
96
102
|
HeapArray<RecordMember> members; // Record only
|
|
97
|
-
|
|
103
|
+
union {
|
|
104
|
+
const void *marker;
|
|
105
|
+
const TypeInfo *type; // Pointer or array
|
|
106
|
+
const FunctionInfo *proto; // Callback only
|
|
107
|
+
} ref;
|
|
98
108
|
ArrayHint hint; // Array only
|
|
99
|
-
|
|
109
|
+
|
|
110
|
+
mutable Napi::ObjectReference defn;
|
|
100
111
|
|
|
101
112
|
RG_HASHTABLE_HANDLER(TypeInfo, name);
|
|
102
113
|
};
|
|
@@ -104,7 +115,7 @@ struct TypeInfo {
|
|
|
104
115
|
struct RecordMember {
|
|
105
116
|
const char *name;
|
|
106
117
|
const TypeInfo *type;
|
|
107
|
-
int16_t
|
|
118
|
+
int16_t offset;
|
|
108
119
|
};
|
|
109
120
|
|
|
110
121
|
struct LibraryHolder {
|
|
@@ -161,6 +172,11 @@ struct ParameterInfo {
|
|
|
161
172
|
#endif
|
|
162
173
|
};
|
|
163
174
|
|
|
175
|
+
struct ValueCast {
|
|
176
|
+
Napi::Reference<Napi::Value> ref;
|
|
177
|
+
const TypeInfo *type;
|
|
178
|
+
};
|
|
179
|
+
|
|
164
180
|
// Also used for callbacks, even though many members are not used in this case
|
|
165
181
|
struct FunctionInfo {
|
|
166
182
|
mutable std::atomic_int refcount {1};
|
|
@@ -199,6 +215,7 @@ struct InstanceMemory {
|
|
|
199
215
|
Span<uint8_t> heap;
|
|
200
216
|
|
|
201
217
|
uint16_t generation; // Can wrap without risk
|
|
218
|
+
|
|
202
219
|
int16_t depth;
|
|
203
220
|
bool temporary;
|
|
204
221
|
};
|
|
@@ -206,24 +223,28 @@ struct InstanceMemory {
|
|
|
206
223
|
struct TrampolineInfo {
|
|
207
224
|
const FunctionInfo *proto;
|
|
208
225
|
Napi::FunctionReference func;
|
|
209
|
-
|
|
226
|
+
|
|
227
|
+
int32_t generation;
|
|
210
228
|
};
|
|
211
229
|
|
|
212
230
|
struct InstanceData {
|
|
213
231
|
~InstanceData();
|
|
214
232
|
|
|
215
233
|
BucketArray<TypeInfo> types;
|
|
216
|
-
|
|
234
|
+
HashMap<const char *, const TypeInfo *> types_map;
|
|
217
235
|
BucketArray<FunctionInfo> callbacks;
|
|
218
236
|
|
|
219
237
|
bool debug;
|
|
220
238
|
uint64_t tag_lower;
|
|
239
|
+
const TypeInfo *void_type;
|
|
221
240
|
|
|
222
241
|
LocalArray<InstanceMemory *, 9> memories;
|
|
223
242
|
int temporaries = 0;
|
|
224
243
|
|
|
225
|
-
TrampolineInfo trampolines[MaxTrampolines];
|
|
226
|
-
|
|
244
|
+
TrampolineInfo trampolines[MaxTrampolines * 2];
|
|
245
|
+
int16_t next_trampoline = 0;
|
|
246
|
+
int16_t temp_trampolines = 0;
|
|
247
|
+
uint32_t registered_trampolines = 0;
|
|
227
248
|
|
|
228
249
|
BlockAllocator str_alloc;
|
|
229
250
|
|
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,8 @@ bool PrototypeParser::Parse(const char *str, FunctionInfo *out_func)
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
param.type = ParseType();
|
|
68
|
-
if (param.type
|
|
69
|
-
|
|
70
|
-
MarkError("Type %1 cannot be used as a parameter", param.type->name);
|
|
68
|
+
if (!CanPassType(param.type)) {
|
|
69
|
+
MarkError("Type %1 cannot be used as a parameter (maybe try %1 *)", param.type->name);
|
|
71
70
|
return false;
|
|
72
71
|
}
|
|
73
72
|
|
|
@@ -144,60 +143,36 @@ void PrototypeParser::Tokenize(const char *str)
|
|
|
144
143
|
|
|
145
144
|
const TypeInfo *PrototypeParser::ParseType()
|
|
146
145
|
{
|
|
147
|
-
HeapArray<char> buf(&instance->str_alloc);
|
|
148
|
-
|
|
149
|
-
Size indirect = 0;
|
|
150
|
-
|
|
151
146
|
Size start = offset;
|
|
152
|
-
while (offset < tokens.len && IsIdentifier(tokens[offset])) {
|
|
153
|
-
Span<const char> tok = tokens[offset++];
|
|
154
147
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if (offset == start) {
|
|
161
|
-
if (offset < tokens.len) {
|
|
162
|
-
MarkError("Unexpected token '%1', expected type", tokens[offset]);
|
|
163
|
-
} else {
|
|
164
|
-
MarkError("Unexpected end of prototype, expected type");
|
|
165
|
-
}
|
|
148
|
+
if (offset >= tokens.len) {
|
|
149
|
+
MarkError("Unexpected end of prototype, expected type");
|
|
150
|
+
return instance->types_map.FindValue("void", nullptr);
|
|
151
|
+
} else if (!IsIdentifier(tokens[offset])) {
|
|
152
|
+
MarkError("Unexpected token '%1', expected type", tokens[offset]);
|
|
166
153
|
return instance->types_map.FindValue("void", nullptr);
|
|
167
154
|
}
|
|
168
|
-
|
|
155
|
+
|
|
156
|
+
while (offset < tokens.len && (IsIdentifier(tokens[offset]) ||
|
|
157
|
+
tokens[offset] == '*')) {
|
|
169
158
|
offset++;
|
|
170
|
-
indirect++;
|
|
171
159
|
}
|
|
172
|
-
|
|
160
|
+
offset += (offset < tokens.len && tokens[offset] == "!");
|
|
173
161
|
|
|
174
|
-
while (
|
|
175
|
-
const
|
|
162
|
+
while (offset >= start) {
|
|
163
|
+
Span<const char> str = MakeSpan(tokens[start].ptr, tokens[offset].end() - tokens[start].ptr);
|
|
164
|
+
const TypeInfo *type = ResolveType(instance, str);
|
|
176
165
|
|
|
177
166
|
if (type) {
|
|
178
|
-
|
|
179
|
-
type = GetPointerType(instance, type);
|
|
180
|
-
RG_ASSERT(type);
|
|
181
|
-
}
|
|
167
|
+
offset++;
|
|
182
168
|
return type;
|
|
183
169
|
}
|
|
184
170
|
|
|
185
|
-
// Truncate last token
|
|
186
|
-
{
|
|
187
|
-
Span<const char> remain;
|
|
188
|
-
SplitStrReverse(buf, ' ', &remain);
|
|
189
|
-
buf.len = remain.len;
|
|
190
|
-
buf.ptr[buf.len] = 0;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
if (indirect) {
|
|
194
|
-
offset -= indirect;
|
|
195
|
-
indirect = 0;
|
|
196
|
-
}
|
|
197
171
|
offset--;
|
|
198
172
|
}
|
|
173
|
+
offset = start;
|
|
199
174
|
|
|
200
|
-
MarkError("Unknown type '%1'", tokens[
|
|
175
|
+
MarkError("Unknown or invalid type name '%1'", tokens[offset]);
|
|
201
176
|
return instance->types_map.FindValue("void", nullptr);
|
|
202
177
|
}
|
|
203
178
|
|
package/src/util.cc
CHANGED
|
@@ -20,21 +20,20 @@
|
|
|
20
20
|
|
|
21
21
|
namespace RG {
|
|
22
22
|
|
|
23
|
-
const TypeInfo *ResolveType(
|
|
23
|
+
const TypeInfo *ResolveType(Napi::Value value, int *out_directions)
|
|
24
24
|
{
|
|
25
|
+
Napi::Env env = value.Env();
|
|
26
|
+
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
27
|
+
|
|
25
28
|
if (value.IsString()) {
|
|
26
29
|
std::string str = value.As<Napi::String>();
|
|
27
|
-
|
|
28
|
-
const TypeInfo *type = instance->types_map.FindValue(str.c_str(), nullptr);
|
|
30
|
+
const TypeInfo *type = ResolveType(instance, str.c_str(), out_directions);
|
|
29
31
|
|
|
30
32
|
if (!type) {
|
|
31
|
-
ThrowError<Napi::TypeError>(
|
|
33
|
+
ThrowError<Napi::TypeError>(env, "Unknown or invalid type name '%1'", str.c_str());
|
|
32
34
|
return nullptr;
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
if (out_directions) {
|
|
36
|
-
*out_directions = 1;
|
|
37
|
-
}
|
|
38
37
|
return type;
|
|
39
38
|
} else if (CheckValueTag(instance, value, &TypeInfoMarker)) {
|
|
40
39
|
Napi::External<TypeInfo> external = value.As<Napi::External<TypeInfo>>();
|
|
@@ -49,45 +48,175 @@ const TypeInfo *ResolveType(const InstanceData *instance, Napi::Value value, int
|
|
|
49
48
|
}
|
|
50
49
|
return type;
|
|
51
50
|
} else {
|
|
52
|
-
ThrowError<Napi::TypeError>(
|
|
51
|
+
ThrowError<Napi::TypeError>(env, "Unexpected %1 value as type specifier, expected string or type", GetValueType(instance, value));
|
|
53
52
|
return nullptr;
|
|
54
53
|
}
|
|
55
54
|
}
|
|
56
55
|
|
|
57
|
-
const TypeInfo *
|
|
56
|
+
const TypeInfo *ResolveType(InstanceData *instance, Span<const char> str, int *out_directions)
|
|
58
57
|
{
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
Span<const char> remain = TrimStr(str);
|
|
59
|
+
|
|
60
|
+
int indirect = 0;
|
|
61
|
+
bool dispose = false;
|
|
62
|
+
|
|
63
|
+
while (remain.len >= 6 && StartsWith(remain, "const") && IsAsciiWhite(remain[5])) {
|
|
64
|
+
remain = remain.Take(6, remain.len - 6);
|
|
65
|
+
remain = TrimStr(remain);
|
|
64
66
|
}
|
|
67
|
+
if (remain.len && remain[remain.len - 1] == '!') {
|
|
68
|
+
dispose = true;
|
|
65
69
|
|
|
66
|
-
|
|
67
|
-
|
|
70
|
+
remain = remain.Take(0, remain.len - 1);
|
|
71
|
+
remain = TrimStr(remain);
|
|
72
|
+
}
|
|
73
|
+
while (remain.len) {
|
|
74
|
+
if (remain[remain.len - 1] == '*') {
|
|
75
|
+
remain = remain.Take(0, remain.len - 1);
|
|
76
|
+
indirect++;
|
|
77
|
+
} else if (remain.len >= 6 && EndsWith(remain, "const") && IsAsciiWhite(remain[remain.len - 6])) {
|
|
78
|
+
remain = remain.Take(0, remain.len - 6);
|
|
79
|
+
} else {
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
remain = TrimStr(remain);
|
|
83
|
+
}
|
|
68
84
|
|
|
69
|
-
TypeInfo *type = instance->types_map.FindValue(
|
|
85
|
+
const TypeInfo *type = instance->types_map.FindValue(remain, nullptr);
|
|
70
86
|
|
|
71
87
|
if (!type) {
|
|
72
|
-
|
|
88
|
+
// Try with cleaned up spaces
|
|
89
|
+
if (remain.len < 256) {
|
|
90
|
+
LocalArray<char, 256> buf;
|
|
91
|
+
for (Size i = 0; i < remain.len; i++) {
|
|
92
|
+
char c = remain[i];
|
|
93
|
+
|
|
94
|
+
if (IsAsciiWhite(c)) {
|
|
95
|
+
buf.Append(' ');
|
|
96
|
+
while (++i < remain.len && IsAsciiWhite(remain[i]));
|
|
97
|
+
i--;
|
|
98
|
+
} else {
|
|
99
|
+
buf.Append(c);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
73
102
|
|
|
74
|
-
|
|
103
|
+
type = instance->types_map.FindValue(buf, nullptr);
|
|
104
|
+
}
|
|
75
105
|
|
|
76
|
-
type
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
106
|
+
if (!type)
|
|
107
|
+
return nullptr;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (indirect) {
|
|
111
|
+
type = MakePointerType(instance, type, indirect);
|
|
112
|
+
RG_ASSERT(type);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (dispose) {
|
|
116
|
+
if (type->primitive != PrimitiveKind::String &&
|
|
117
|
+
type->primitive != PrimitiveKind::String16 &&
|
|
118
|
+
indirect != 1)
|
|
119
|
+
return nullptr;
|
|
80
120
|
|
|
81
|
-
instance->
|
|
121
|
+
TypeInfo *copy = instance->types.AppendDefault();
|
|
122
|
+
|
|
123
|
+
memcpy((void *)copy, (const void *)type, RG_SIZE(*type));
|
|
124
|
+
copy->name = "<anonymous>";
|
|
125
|
+
copy->members.allocator = GetNullAllocator();
|
|
126
|
+
copy->dispose = [](Napi::Env, const TypeInfo *, const void *ptr) { free((void *)ptr); };
|
|
127
|
+
|
|
128
|
+
type = copy;
|
|
82
129
|
}
|
|
83
130
|
|
|
131
|
+
if (out_directions) {
|
|
132
|
+
*out_directions = 1;
|
|
133
|
+
}
|
|
84
134
|
return type;
|
|
85
135
|
}
|
|
86
136
|
|
|
137
|
+
const TypeInfo *MakePointerType(InstanceData *instance, const TypeInfo *ref, int count)
|
|
138
|
+
{
|
|
139
|
+
RG_ASSERT(count >= 1);
|
|
140
|
+
|
|
141
|
+
for (int i = 0; i < count; i++) {
|
|
142
|
+
char name_buf[256];
|
|
143
|
+
Fmt(name_buf, "%1%2*", ref->name, EndsWith(ref->name, "*") ? "" : " ");
|
|
144
|
+
|
|
145
|
+
TypeInfo *type = (TypeInfo *)instance->types_map.FindValue(name_buf, nullptr);
|
|
146
|
+
|
|
147
|
+
if (!type) {
|
|
148
|
+
type = instance->types.AppendDefault();
|
|
149
|
+
|
|
150
|
+
type->name = DuplicateString(name_buf, &instance->str_alloc).ptr;
|
|
151
|
+
|
|
152
|
+
if (ref->primitive != PrimitiveKind::Prototype) {
|
|
153
|
+
type->primitive = PrimitiveKind::Pointer;
|
|
154
|
+
type->size = RG_SIZE(void *);
|
|
155
|
+
type->align = RG_SIZE(void *);
|
|
156
|
+
type->ref.type = ref;
|
|
157
|
+
} else {
|
|
158
|
+
type->primitive = PrimitiveKind::Callback;
|
|
159
|
+
type->size = RG_SIZE(void *);
|
|
160
|
+
type->align = RG_SIZE(void *);
|
|
161
|
+
type->ref.proto = ref->ref.proto;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
instance->types_map.Set(type->name, type);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
ref = type;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return ref;
|
|
171
|
+
}
|
|
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
|
+
|
|
87
207
|
const char *GetValueType(const InstanceData *instance, Napi::Value value)
|
|
88
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";
|
|
89
218
|
for (const TypeInfo &type: instance->types) {
|
|
90
|
-
if (CheckValueTag(instance, value,
|
|
219
|
+
if (type.ref.marker && CheckValueTag(instance, value, type.ref.marker))
|
|
91
220
|
return type.name;
|
|
92
221
|
}
|
|
93
222
|
|
|
@@ -130,6 +259,8 @@ const char *GetValueType(const InstanceData *instance, Napi::Value value)
|
|
|
130
259
|
|
|
131
260
|
void SetValueTag(const InstanceData *instance, Napi::Value value, const void *marker)
|
|
132
261
|
{
|
|
262
|
+
RG_ASSERT(marker);
|
|
263
|
+
|
|
133
264
|
napi_type_tag tag = { instance->tag_lower, (uint64_t)marker };
|
|
134
265
|
napi_status status = napi_type_tag_object(value.Env(), value, &tag);
|
|
135
266
|
RG_ASSERT(status == napi_ok);
|
|
@@ -137,6 +268,8 @@ void SetValueTag(const InstanceData *instance, Napi::Value value, const void *ma
|
|
|
137
268
|
|
|
138
269
|
bool CheckValueTag(const InstanceData *instance, Napi::Value value, const void *marker)
|
|
139
270
|
{
|
|
271
|
+
RG_ASSERT(marker);
|
|
272
|
+
|
|
140
273
|
bool match = false;
|
|
141
274
|
|
|
142
275
|
if (!IsNullOrUndefined(value)) {
|
|
@@ -174,8 +307,8 @@ static int AnalyseFlatRec(const TypeInfo *type, int offset, int count, FunctionR
|
|
|
174
307
|
}
|
|
175
308
|
}
|
|
176
309
|
} else if (type->primitive == PrimitiveKind::Array) {
|
|
177
|
-
count *= type->size / type->ref->size;
|
|
178
|
-
offset = AnalyseFlatRec(type->ref, offset, count, func);
|
|
310
|
+
count *= type->size / type->ref.type->size;
|
|
311
|
+
offset = AnalyseFlatRec(type->ref.type, offset, count, func);
|
|
179
312
|
} else {
|
|
180
313
|
func(type, offset, count);
|
|
181
314
|
offset += count;
|
package/src/util.hh
CHANGED
|
@@ -67,8 +67,13 @@ static inline bool IsFloat(const TypeInfo *type)
|
|
|
67
67
|
return fp;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
const TypeInfo *ResolveType(
|
|
71
|
-
const TypeInfo *
|
|
70
|
+
const TypeInfo *ResolveType(Napi::Value value, int *out_directions = nullptr);
|
|
71
|
+
const TypeInfo *ResolveType(InstanceData *instance, Span<const char> str, int *out_directions = nullptr);
|
|
72
|
+
const TypeInfo *MakePointerType(InstanceData *instance, const TypeInfo *type, int count = 1);
|
|
73
|
+
|
|
74
|
+
bool CanPassType(const TypeInfo *type);
|
|
75
|
+
bool CanReturnType(const TypeInfo *type);
|
|
76
|
+
bool CanStoreType(const TypeInfo *type);
|
|
72
77
|
|
|
73
78
|
// Can be slow, only use for error messages
|
|
74
79
|
const char *GetValueType(const InstanceData *instance, Napi::Value value);
|
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',
|
|
@@ -32,13 +31,14 @@ const BFG = koffi.struct('BFG', {
|
|
|
32
31
|
const SimpleCallback = koffi.callback('int SimpleCallback(const char *str)');
|
|
33
32
|
const RecursiveCallback = koffi.callback('RecursiveCallback', 'float', ['int', 'str', 'double']);
|
|
34
33
|
const BigCallback = koffi.callback('BFG BigCallback(BFG bfg)');
|
|
34
|
+
const SuperCallback = koffi.callback('void SuperCallback(int i, int v1, double v2, int v3, int v4, int v5, int v6, float v7, int v8)');
|
|
35
35
|
const ApplyCallback = koffi.callback('int __stdcall ApplyCallback(int a, int b, int c)');
|
|
36
36
|
const IntCallback = koffi.callback('int IntCallback(int x)');
|
|
37
37
|
|
|
38
38
|
const StructCallbacks = koffi.struct('StructCallbacks', {
|
|
39
|
-
first: IntCallback,
|
|
40
|
-
second: IntCallback,
|
|
41
|
-
third: IntCallback
|
|
39
|
+
first: koffi.pointer(IntCallback),
|
|
40
|
+
second: 'IntCallback *',
|
|
41
|
+
third: 'IntCallback *'
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
main();
|
|
@@ -54,15 +54,18 @@ async function main() {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
async function test() {
|
|
57
|
-
const lib_filename =
|
|
57
|
+
const lib_filename = __dirname + '/build/misc' + koffi.extension;
|
|
58
58
|
const lib = koffi.load(lib_filename);
|
|
59
59
|
|
|
60
|
-
const CallJS = lib.func('int CallJS(const char *str, SimpleCallback cb)');
|
|
61
|
-
const CallRecursiveJS = lib.func('float CallRecursiveJS(int i, RecursiveCallback func)');
|
|
62
|
-
const ModifyBFG = lib.func('BFG ModifyBFG(int x, double y, const char *str, BigCallback func, _Out_ BFG *p)');
|
|
63
|
-
const
|
|
64
|
-
const
|
|
60
|
+
const CallJS = lib.func('int CallJS(const char *str, SimpleCallback *cb)');
|
|
61
|
+
const CallRecursiveJS = lib.func('float CallRecursiveJS(int i, RecursiveCallback *func)');
|
|
62
|
+
const ModifyBFG = lib.func('BFG ModifyBFG(int x, double y, const char *str, BigCallback *func, _Out_ BFG *p)');
|
|
63
|
+
const Recurse8 = lib.func('void Recurse8(int i, SuperCallback *func)');
|
|
64
|
+
const ApplyStd = lib.func('int ApplyStd(int a, int b, int c, ApplyCallback *func)');
|
|
65
|
+
const ApplyMany = lib.func('int ApplyMany(int x, IntCallback **funcs, int length)');
|
|
65
66
|
const ApplyStruct = lib.func('int ApplyStruct(int x, StructCallbacks callbacks)');
|
|
67
|
+
const SetCallback = lib.func('void SetCallback(IntCallback *func)');
|
|
68
|
+
const CallCallback = lib.func('int CallCallback(int x)');
|
|
66
69
|
|
|
67
70
|
// Simple test similar to README example
|
|
68
71
|
{
|
|
@@ -96,6 +99,35 @@ async function test() {
|
|
|
96
99
|
assert.deepEqual(out, { a: 2, b: 4, c: -25, d: 'X/Yo!/X', e: 54, inner: { f: 10, g: 3 } });
|
|
97
100
|
}
|
|
98
101
|
|
|
102
|
+
// With many parameters
|
|
103
|
+
{
|
|
104
|
+
let a = [], b = [], c = [], d = [], e = [], f = [], g = [], h = [];
|
|
105
|
+
|
|
106
|
+
let fn = (i, v1, v2, v3, v4, v5, v6, v7, v8) => {
|
|
107
|
+
a.push(v1);
|
|
108
|
+
b.push(v2);
|
|
109
|
+
c.push(v3);
|
|
110
|
+
d.push(v4);
|
|
111
|
+
e.push(v5);
|
|
112
|
+
f.push(v6);
|
|
113
|
+
g.push(v7);
|
|
114
|
+
h.push(v8);
|
|
115
|
+
|
|
116
|
+
if (i)
|
|
117
|
+
Recurse8(i - 1, fn);
|
|
118
|
+
};
|
|
119
|
+
Recurse8(3, fn);
|
|
120
|
+
|
|
121
|
+
assert.deepEqual(a, [3, 2, 1, 0]);
|
|
122
|
+
assert.deepEqual(b, [6, 4, 2, 0]);
|
|
123
|
+
assert.deepEqual(c, [4, 3, 2, 1]);
|
|
124
|
+
assert.deepEqual(d, [7, 5, 3, 1]);
|
|
125
|
+
assert.deepEqual(e, [0, 1, 2, 3]);
|
|
126
|
+
assert.deepEqual(f, [103, 102, 101, 100]);
|
|
127
|
+
assert.deepEqual(g, [1, 0, 1, 0]);
|
|
128
|
+
assert.deepEqual(h, [-4, -3, -2, -1]);
|
|
129
|
+
}
|
|
130
|
+
|
|
99
131
|
// Stdcall callbacks
|
|
100
132
|
{
|
|
101
133
|
let ret = ApplyStd(1, 5, 9, (a, b, c) => a + b * c);
|
|
@@ -115,4 +147,17 @@ async function test() {
|
|
|
115
147
|
let ret = ApplyStruct(27, callbacks);
|
|
116
148
|
assert.equal(ret, -177);
|
|
117
149
|
}
|
|
150
|
+
|
|
151
|
+
// Persistent callback
|
|
152
|
+
{
|
|
153
|
+
SetCallback(x => -x);
|
|
154
|
+
assert.throws(() => CallCallback(27), { message: /non-registered callback/ });
|
|
155
|
+
|
|
156
|
+
let cb = koffi.register(x => -x, koffi.pointer(IntCallback));
|
|
157
|
+
SetCallback(cb);
|
|
158
|
+
assert.equal(CallCallback(27), -27);
|
|
159
|
+
|
|
160
|
+
assert.equal(koffi.unregister(cb), null);
|
|
161
|
+
assert.throws(() => koffi.unregister(cb));
|
|
162
|
+
}
|
|
118
163
|
}
|