koffi 1.3.12 → 2.0.0
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 +7 -2
- package/ChangeLog.md +42 -16
- package/README.md +6 -0
- package/build/qemu/2.0.0/koffi_darwin_arm64.tar.gz +0 -0
- package/build/qemu/2.0.0/koffi_darwin_x64.tar.gz +0 -0
- package/build/qemu/2.0.0/koffi_freebsd_arm64.tar.gz +0 -0
- package/build/qemu/2.0.0/koffi_freebsd_ia32.tar.gz +0 -0
- package/build/qemu/2.0.0/koffi_freebsd_x64.tar.gz +0 -0
- package/build/qemu/2.0.0/koffi_linux_arm32hf.tar.gz +0 -0
- package/build/qemu/2.0.0/koffi_linux_arm64.tar.gz +0 -0
- package/build/qemu/2.0.0/koffi_linux_ia32.tar.gz +0 -0
- package/build/qemu/2.0.0/koffi_linux_riscv64hf64.tar.gz +0 -0
- package/build/qemu/2.0.0/koffi_linux_x64.tar.gz +0 -0
- package/build/qemu/2.0.0/koffi_openbsd_ia32.tar.gz +0 -0
- package/build/qemu/2.0.0/koffi_openbsd_x64.tar.gz +0 -0
- package/build/qemu/2.0.0/koffi_win32_arm64.tar.gz +0 -0
- package/build/qemu/2.0.0/koffi_win32_ia32.tar.gz +0 -0
- package/build/qemu/2.0.0/koffi_win32_x64.tar.gz +0 -0
- package/doc/benchmarks.md +2 -2
- package/doc/changes.md +156 -1
- package/doc/contribute.md +0 -1
- 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/types.doctree +0 -0
- package/doc/dist/html/_sources/changes.md.txt +156 -1
- package/doc/dist/html/_sources/functions.md.txt +8 -4
- package/doc/dist/html/_sources/types.md.txt +9 -0
- package/doc/dist/html/benchmarks.html +1 -1
- package/doc/dist/html/changes.html +226 -14
- package/doc/dist/html/contribute.html +1 -1
- package/doc/dist/html/functions.html +15 -12
- package/doc/dist/html/genindex.html +1 -1
- package/doc/dist/html/index.html +6 -16
- package/doc/dist/html/memory.html +3 -3
- package/doc/dist/html/objects.inv +0 -0
- package/doc/dist/html/platforms.html +1 -1
- 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 +11 -3
- package/doc/functions.md +137 -13
- package/doc/types.md +35 -10
- package/package.json +1 -1
- package/qemu/registry/machines.json +5 -5
- package/qemu/registry/sha256sum.txt +16 -16
- package/src/abi_arm32.cc +90 -18
- package/src/abi_arm32_fwd.S +121 -57
- package/src/abi_arm64.cc +90 -18
- package/src/abi_arm64_fwd.S +96 -0
- package/src/abi_arm64_fwd.asm +128 -0
- package/src/abi_riscv64.cc +88 -18
- package/src/abi_riscv64_fwd.S +96 -0
- package/src/abi_x64_sysv.cc +93 -21
- package/src/abi_x64_sysv_fwd.S +96 -0
- package/src/abi_x64_win.cc +88 -18
- package/src/abi_x64_win_fwd.asm +128 -0
- package/src/abi_x86.cc +93 -18
- package/src/abi_x86_fwd.S +96 -0
- package/src/abi_x86_fwd.asm +128 -0
- package/src/call.cc +97 -63
- package/src/call.hh +2 -1
- package/src/ffi.cc +452 -140
- package/src/ffi.hh +23 -9
- package/src/parser.cc +18 -41
- package/src/util.cc +117 -27
- package/src/util.hh +3 -2
- package/test/callbacks.js +54 -8
- package/test/misc.c +29 -14
- package/test/raylib.js +1 -1
- package/test/sqlite.js +24 -16
- package/test/sync.js +41 -31
- 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
|
@@ -51,6 +51,7 @@ enum class PrimitiveKind {
|
|
|
51
51
|
Array,
|
|
52
52
|
Float32,
|
|
53
53
|
Float64,
|
|
54
|
+
Prototype,
|
|
54
55
|
Callback
|
|
55
56
|
};
|
|
56
57
|
static const char *const PrimitiveKindNames[] = {
|
|
@@ -71,6 +72,7 @@ static const char *const PrimitiveKindNames[] = {
|
|
|
71
72
|
"Array",
|
|
72
73
|
"Float32",
|
|
73
74
|
"Float64",
|
|
75
|
+
"Prototype",
|
|
74
76
|
"Callback"
|
|
75
77
|
};
|
|
76
78
|
|
|
@@ -78,6 +80,8 @@ struct TypeInfo;
|
|
|
78
80
|
struct RecordMember;
|
|
79
81
|
struct FunctionInfo;
|
|
80
82
|
|
|
83
|
+
typedef void DisposeFunc (Napi::Env env, const TypeInfo *type, const void *ptr);
|
|
84
|
+
|
|
81
85
|
struct TypeInfo {
|
|
82
86
|
enum class ArrayHint {
|
|
83
87
|
Array,
|
|
@@ -87,16 +91,22 @@ struct TypeInfo {
|
|
|
87
91
|
|
|
88
92
|
const char *name;
|
|
89
93
|
|
|
90
|
-
Napi::ObjectReference defn;
|
|
91
|
-
|
|
92
94
|
PrimitiveKind primitive;
|
|
93
95
|
int16_t size;
|
|
94
96
|
int16_t align;
|
|
95
97
|
|
|
98
|
+
DisposeFunc *dispose;
|
|
99
|
+
Napi::FunctionReference dispose_ref;
|
|
100
|
+
|
|
96
101
|
HeapArray<RecordMember> members; // Record only
|
|
97
|
-
|
|
102
|
+
union {
|
|
103
|
+
const void *marker;
|
|
104
|
+
const TypeInfo *type; // Pointer or array
|
|
105
|
+
const FunctionInfo *proto; // Callback only
|
|
106
|
+
} ref;
|
|
98
107
|
ArrayHint hint; // Array only
|
|
99
|
-
|
|
108
|
+
|
|
109
|
+
mutable Napi::ObjectReference defn;
|
|
100
110
|
|
|
101
111
|
RG_HASHTABLE_HANDLER(TypeInfo, name);
|
|
102
112
|
};
|
|
@@ -104,7 +114,7 @@ struct TypeInfo {
|
|
|
104
114
|
struct RecordMember {
|
|
105
115
|
const char *name;
|
|
106
116
|
const TypeInfo *type;
|
|
107
|
-
int16_t
|
|
117
|
+
int16_t offset;
|
|
108
118
|
};
|
|
109
119
|
|
|
110
120
|
struct LibraryHolder {
|
|
@@ -199,6 +209,7 @@ struct InstanceMemory {
|
|
|
199
209
|
Span<uint8_t> heap;
|
|
200
210
|
|
|
201
211
|
uint16_t generation; // Can wrap without risk
|
|
212
|
+
|
|
202
213
|
int16_t depth;
|
|
203
214
|
bool temporary;
|
|
204
215
|
};
|
|
@@ -206,14 +217,15 @@ struct InstanceMemory {
|
|
|
206
217
|
struct TrampolineInfo {
|
|
207
218
|
const FunctionInfo *proto;
|
|
208
219
|
Napi::FunctionReference func;
|
|
209
|
-
|
|
220
|
+
|
|
221
|
+
int32_t generation;
|
|
210
222
|
};
|
|
211
223
|
|
|
212
224
|
struct InstanceData {
|
|
213
225
|
~InstanceData();
|
|
214
226
|
|
|
215
227
|
BucketArray<TypeInfo> types;
|
|
216
|
-
|
|
228
|
+
HashMap<const char *, const TypeInfo *> types_map;
|
|
217
229
|
BucketArray<FunctionInfo> callbacks;
|
|
218
230
|
|
|
219
231
|
bool debug;
|
|
@@ -222,8 +234,10 @@ struct InstanceData {
|
|
|
222
234
|
LocalArray<InstanceMemory *, 9> memories;
|
|
223
235
|
int temporaries = 0;
|
|
224
236
|
|
|
225
|
-
TrampolineInfo trampolines[MaxTrampolines];
|
|
226
|
-
|
|
237
|
+
TrampolineInfo trampolines[MaxTrampolines * 2];
|
|
238
|
+
int16_t next_trampoline = 0;
|
|
239
|
+
int16_t temp_trampolines = 0;
|
|
240
|
+
uint32_t registered_trampolines = 0;
|
|
227
241
|
|
|
228
242
|
BlockAllocator str_alloc;
|
|
229
243
|
|
package/src/parser.cc
CHANGED
|
@@ -66,8 +66,9 @@ bool PrototypeParser::Parse(const char *str, FunctionInfo *out_func)
|
|
|
66
66
|
|
|
67
67
|
param.type = ParseType();
|
|
68
68
|
if (param.type->primitive == PrimitiveKind::Void ||
|
|
69
|
-
param.type->primitive == PrimitiveKind::Array
|
|
70
|
-
|
|
69
|
+
param.type->primitive == PrimitiveKind::Array ||
|
|
70
|
+
param.type->primitive == PrimitiveKind::Prototype) {
|
|
71
|
+
MarkError("Type %1 cannot be used as a parameter (maybe try %1 *)", param.type->name);
|
|
71
72
|
return false;
|
|
72
73
|
}
|
|
73
74
|
|
|
@@ -144,60 +145,36 @@ void PrototypeParser::Tokenize(const char *str)
|
|
|
144
145
|
|
|
145
146
|
const TypeInfo *PrototypeParser::ParseType()
|
|
146
147
|
{
|
|
147
|
-
HeapArray<char> buf(&instance->str_alloc);
|
|
148
|
-
|
|
149
|
-
Size indirect = 0;
|
|
150
|
-
|
|
151
148
|
Size start = offset;
|
|
152
|
-
while (offset < tokens.len && IsIdentifier(tokens[offset])) {
|
|
153
|
-
Span<const char> tok = tokens[offset++];
|
|
154
149
|
|
|
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
|
-
}
|
|
150
|
+
if (offset >= tokens.len) {
|
|
151
|
+
MarkError("Unexpected end of prototype, expected type");
|
|
152
|
+
return instance->types_map.FindValue("void", nullptr);
|
|
153
|
+
} else if (!IsIdentifier(tokens[offset])) {
|
|
154
|
+
MarkError("Unexpected token '%1', expected type", tokens[offset]);
|
|
166
155
|
return instance->types_map.FindValue("void", nullptr);
|
|
167
156
|
}
|
|
168
|
-
|
|
157
|
+
|
|
158
|
+
while (offset < tokens.len && (IsIdentifier(tokens[offset]) ||
|
|
159
|
+
tokens[offset] == '*')) {
|
|
169
160
|
offset++;
|
|
170
|
-
indirect++;
|
|
171
161
|
}
|
|
172
|
-
|
|
162
|
+
offset += (offset < tokens.len && tokens[offset] == "!");
|
|
173
163
|
|
|
174
|
-
while (
|
|
175
|
-
const
|
|
164
|
+
while (offset >= start) {
|
|
165
|
+
Span<const char> str = MakeSpan(tokens[start].ptr, tokens[offset].end() - tokens[start].ptr);
|
|
166
|
+
const TypeInfo *type = ResolveType(instance, str);
|
|
176
167
|
|
|
177
168
|
if (type) {
|
|
178
|
-
|
|
179
|
-
type = GetPointerType(instance, type);
|
|
180
|
-
RG_ASSERT(type);
|
|
181
|
-
}
|
|
169
|
+
offset++;
|
|
182
170
|
return type;
|
|
183
171
|
}
|
|
184
172
|
|
|
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
173
|
offset--;
|
|
198
174
|
}
|
|
175
|
+
offset = start;
|
|
199
176
|
|
|
200
|
-
MarkError("Unknown type '%1'", tokens[
|
|
177
|
+
MarkError("Unknown or invalid type name '%1'", tokens[offset]);
|
|
201
178
|
return instance->types_map.FindValue("void", nullptr);
|
|
202
179
|
}
|
|
203
180
|
|
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,132 @@ 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
|
-
type->ref = ref;
|
|
106
|
+
if (!type)
|
|
107
|
+
return nullptr;
|
|
108
|
+
}
|
|
80
109
|
|
|
81
|
-
|
|
110
|
+
if (indirect) {
|
|
111
|
+
type = MakePointerType(instance, type, indirect);
|
|
112
|
+
RG_ASSERT(type);
|
|
82
113
|
}
|
|
83
114
|
|
|
115
|
+
if (dispose) {
|
|
116
|
+
if (type->primitive != PrimitiveKind::String &&
|
|
117
|
+
type->primitive != PrimitiveKind::String16 &&
|
|
118
|
+
indirect != 1)
|
|
119
|
+
return nullptr;
|
|
120
|
+
|
|
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;
|
|
129
|
+
}
|
|
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
|
+
|
|
87
173
|
const char *GetValueType(const InstanceData *instance, Napi::Value value)
|
|
88
174
|
{
|
|
89
175
|
for (const TypeInfo &type: instance->types) {
|
|
90
|
-
if (CheckValueTag(instance, value,
|
|
176
|
+
if (CheckValueTag(instance, value, type.ref.marker))
|
|
91
177
|
return type.name;
|
|
92
178
|
}
|
|
93
179
|
|
|
@@ -130,6 +216,8 @@ const char *GetValueType(const InstanceData *instance, Napi::Value value)
|
|
|
130
216
|
|
|
131
217
|
void SetValueTag(const InstanceData *instance, Napi::Value value, const void *marker)
|
|
132
218
|
{
|
|
219
|
+
RG_ASSERT(marker);
|
|
220
|
+
|
|
133
221
|
napi_type_tag tag = { instance->tag_lower, (uint64_t)marker };
|
|
134
222
|
napi_status status = napi_type_tag_object(value.Env(), value, &tag);
|
|
135
223
|
RG_ASSERT(status == napi_ok);
|
|
@@ -137,6 +225,8 @@ void SetValueTag(const InstanceData *instance, Napi::Value value, const void *ma
|
|
|
137
225
|
|
|
138
226
|
bool CheckValueTag(const InstanceData *instance, Napi::Value value, const void *marker)
|
|
139
227
|
{
|
|
228
|
+
RG_ASSERT(marker);
|
|
229
|
+
|
|
140
230
|
bool match = false;
|
|
141
231
|
|
|
142
232
|
if (!IsNullOrUndefined(value)) {
|
|
@@ -174,8 +264,8 @@ static int AnalyseFlatRec(const TypeInfo *type, int offset, int count, FunctionR
|
|
|
174
264
|
}
|
|
175
265
|
}
|
|
176
266
|
} else if (type->primitive == PrimitiveKind::Array) {
|
|
177
|
-
count *= type->size / type->ref->size;
|
|
178
|
-
offset = AnalyseFlatRec(type->ref, offset, count, func);
|
|
267
|
+
count *= type->size / type->ref.type->size;
|
|
268
|
+
offset = AnalyseFlatRec(type->ref.type, offset, count, func);
|
|
179
269
|
} else {
|
|
180
270
|
func(type, offset, count);
|
|
181
271
|
offset += count;
|
package/src/util.hh
CHANGED
|
@@ -67,8 +67,9 @@ 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);
|
|
72
73
|
|
|
73
74
|
// Can be slow, only use for error messages
|
|
74
75
|
const char *GetValueType(const InstanceData *instance, Napi::Value value);
|
package/test/callbacks.js
CHANGED
|
@@ -32,13 +32,14 @@ const BFG = koffi.struct('BFG', {
|
|
|
32
32
|
const SimpleCallback = koffi.callback('int SimpleCallback(const char *str)');
|
|
33
33
|
const RecursiveCallback = koffi.callback('RecursiveCallback', 'float', ['int', 'str', 'double']);
|
|
34
34
|
const BigCallback = koffi.callback('BFG BigCallback(BFG bfg)');
|
|
35
|
+
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
36
|
const ApplyCallback = koffi.callback('int __stdcall ApplyCallback(int a, int b, int c)');
|
|
36
37
|
const IntCallback = koffi.callback('int IntCallback(int x)');
|
|
37
38
|
|
|
38
39
|
const StructCallbacks = koffi.struct('StructCallbacks', {
|
|
39
|
-
first: IntCallback,
|
|
40
|
-
second: IntCallback,
|
|
41
|
-
third: IntCallback
|
|
40
|
+
first: koffi.pointer(IntCallback),
|
|
41
|
+
second: 'IntCallback *',
|
|
42
|
+
third: 'IntCallback *'
|
|
42
43
|
});
|
|
43
44
|
|
|
44
45
|
main();
|
|
@@ -57,12 +58,15 @@ async function test() {
|
|
|
57
58
|
const lib_filename = path.dirname(__filename) + '/build/misc' + koffi.extension;
|
|
58
59
|
const lib = koffi.load(lib_filename);
|
|
59
60
|
|
|
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
|
|
61
|
+
const CallJS = lib.func('int CallJS(const char *str, SimpleCallback *cb)');
|
|
62
|
+
const CallRecursiveJS = lib.func('float CallRecursiveJS(int i, RecursiveCallback *func)');
|
|
63
|
+
const ModifyBFG = lib.func('BFG ModifyBFG(int x, double y, const char *str, BigCallback *func, _Out_ BFG *p)');
|
|
64
|
+
const Recurse8 = lib.func('void Recurse8(int i, SuperCallback *func)');
|
|
65
|
+
const ApplyStd = lib.func('int ApplyStd(int a, int b, int c, ApplyCallback *func)');
|
|
66
|
+
const ApplyMany = lib.func('int ApplyMany(int x, IntCallback **funcs, int length)');
|
|
65
67
|
const ApplyStruct = lib.func('int ApplyStruct(int x, StructCallbacks callbacks)');
|
|
68
|
+
const SetCallback = lib.func('void SetCallback(IntCallback *func)');
|
|
69
|
+
const CallCallback = lib.func('int CallCallback(int x)');
|
|
66
70
|
|
|
67
71
|
// Simple test similar to README example
|
|
68
72
|
{
|
|
@@ -96,6 +100,35 @@ async function test() {
|
|
|
96
100
|
assert.deepEqual(out, { a: 2, b: 4, c: -25, d: 'X/Yo!/X', e: 54, inner: { f: 10, g: 3 } });
|
|
97
101
|
}
|
|
98
102
|
|
|
103
|
+
// With many parameters
|
|
104
|
+
{
|
|
105
|
+
let a = [], b = [], c = [], d = [], e = [], f = [], g = [], h = [];
|
|
106
|
+
|
|
107
|
+
let fn = (i, v1, v2, v3, v4, v5, v6, v7, v8) => {
|
|
108
|
+
a.push(v1);
|
|
109
|
+
b.push(v2);
|
|
110
|
+
c.push(v3);
|
|
111
|
+
d.push(v4);
|
|
112
|
+
e.push(v5);
|
|
113
|
+
f.push(v6);
|
|
114
|
+
g.push(v7);
|
|
115
|
+
h.push(v8);
|
|
116
|
+
|
|
117
|
+
if (i)
|
|
118
|
+
Recurse8(i - 1, fn);
|
|
119
|
+
};
|
|
120
|
+
Recurse8(3, fn);
|
|
121
|
+
|
|
122
|
+
assert.deepEqual(a, [3, 2, 1, 0]);
|
|
123
|
+
assert.deepEqual(b, [6, 4, 2, 0]);
|
|
124
|
+
assert.deepEqual(c, [4, 3, 2, 1]);
|
|
125
|
+
assert.deepEqual(d, [7, 5, 3, 1]);
|
|
126
|
+
assert.deepEqual(e, [0, 1, 2, 3]);
|
|
127
|
+
assert.deepEqual(f, [103, 102, 101, 100]);
|
|
128
|
+
assert.deepEqual(g, [1, 0, 1, 0]);
|
|
129
|
+
assert.deepEqual(h, [-4, -3, -2, -1]);
|
|
130
|
+
}
|
|
131
|
+
|
|
99
132
|
// Stdcall callbacks
|
|
100
133
|
{
|
|
101
134
|
let ret = ApplyStd(1, 5, 9, (a, b, c) => a + b * c);
|
|
@@ -115,4 +148,17 @@ async function test() {
|
|
|
115
148
|
let ret = ApplyStruct(27, callbacks);
|
|
116
149
|
assert.equal(ret, -177);
|
|
117
150
|
}
|
|
151
|
+
|
|
152
|
+
// Persistent callback
|
|
153
|
+
{
|
|
154
|
+
SetCallback(x => -x);
|
|
155
|
+
assert.throws(() => CallCallback(27), { message: /non-registered callback/ });
|
|
156
|
+
|
|
157
|
+
let cb = koffi.register(x => -x, koffi.pointer(IntCallback));
|
|
158
|
+
SetCallback(cb);
|
|
159
|
+
assert.equal(CallCallback(27), -27);
|
|
160
|
+
|
|
161
|
+
assert.equal(koffi.unregister(cb), null);
|
|
162
|
+
assert.throws(() => koffi.unregister(cb));
|
|
163
|
+
}
|
|
118
164
|
}
|
package/test/misc.c
CHANGED
|
@@ -406,24 +406,21 @@ const char *ReturnBigString(const char *str)
|
|
|
406
406
|
EXPORT const char *ReturnBigString(const char *str)
|
|
407
407
|
#endif
|
|
408
408
|
{
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
size_t len = strlen(str);
|
|
412
|
-
memcpy(buf, str, len + 1);
|
|
413
|
-
|
|
414
|
-
return buf;
|
|
409
|
+
const char *copy = strdup(str);
|
|
410
|
+
return copy;
|
|
415
411
|
}
|
|
416
412
|
|
|
417
413
|
EXPORT const char *PrintFmt(const char *fmt, ...)
|
|
418
414
|
{
|
|
419
|
-
|
|
415
|
+
const int size = 256;
|
|
416
|
+
char *ptr = malloc(size);
|
|
420
417
|
|
|
421
418
|
va_list ap;
|
|
422
419
|
va_start(ap, fmt);
|
|
423
|
-
vsnprintf(
|
|
420
|
+
vsnprintf(ptr, size, fmt, ap);
|
|
424
421
|
va_end(ap);
|
|
425
422
|
|
|
426
|
-
return
|
|
423
|
+
return ptr;
|
|
427
424
|
}
|
|
428
425
|
|
|
429
426
|
size_t Length16(const char16_t *str)
|
|
@@ -437,16 +434,17 @@ size_t Length16(const char16_t *str)
|
|
|
437
434
|
|
|
438
435
|
EXPORT const char16_t *Concat16(const char16_t *str1, const char16_t *str2)
|
|
439
436
|
{
|
|
440
|
-
|
|
437
|
+
const int size = 1024;
|
|
438
|
+
char16_t *ptr = malloc(size * 2);
|
|
441
439
|
|
|
442
440
|
size_t len1 = Length16(str1);
|
|
443
441
|
size_t len2 = Length16(str2);
|
|
444
442
|
|
|
445
|
-
memcpy(
|
|
446
|
-
memcpy(
|
|
447
|
-
|
|
443
|
+
memcpy(ptr, str1, len1 * 2);
|
|
444
|
+
memcpy(ptr + len1, str2, len2 * 2);
|
|
445
|
+
ptr[len1 + len2] = 0;
|
|
448
446
|
|
|
449
|
-
return
|
|
447
|
+
return ptr;
|
|
450
448
|
}
|
|
451
449
|
|
|
452
450
|
EXPORT FixedString ReturnFixedStr(FixedString str)
|
|
@@ -549,6 +547,11 @@ EXPORT BFG ModifyBFG(int x, double y, const char *str, BFG (*func)(BFG bfg), BFG
|
|
|
549
547
|
return bfg;
|
|
550
548
|
}
|
|
551
549
|
|
|
550
|
+
EXPORT void Recurse8(int i, void (*func)(int i, int v1, double v2, int v3, int v4, int v5, int v6, float v7, int v8))
|
|
551
|
+
{
|
|
552
|
+
func(i, i, (double)(i * 2), i + 1, i * 2 + 1, 3 - i, 100 + i, (float)(i % 2), -i - 1);
|
|
553
|
+
}
|
|
554
|
+
|
|
552
555
|
EXPORT int ApplyStd(int a, int b, int c, ApplyCallback *func)
|
|
553
556
|
{
|
|
554
557
|
int ret = func(a, b, c);
|
|
@@ -618,3 +621,15 @@ EXPORT int ApplyStruct(int x, StructCallbacks callbacks)
|
|
|
618
621
|
|
|
619
622
|
return x;
|
|
620
623
|
}
|
|
624
|
+
|
|
625
|
+
static IntCallback *callback;
|
|
626
|
+
|
|
627
|
+
EXPORT void SetCallback(IntCallback *cb)
|
|
628
|
+
{
|
|
629
|
+
callback = cb;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
EXPORT int CallCallback(int x)
|
|
633
|
+
{
|
|
634
|
+
return callback(x);
|
|
635
|
+
}
|
package/test/raylib.js
CHANGED
package/test/sqlite.js
CHANGED
|
@@ -39,17 +39,17 @@ async function test() {
|
|
|
39
39
|
let lib_filename = path.dirname(__filename) + '/build/sqlite3' + koffi.extension;
|
|
40
40
|
let lib = koffi.load(lib_filename);
|
|
41
41
|
|
|
42
|
-
const sqlite3_open_v2 = lib.func('sqlite3_open_v2', 'int', ['str', koffi.out(koffi.pointer(sqlite3_db)), 'int', 'str']);
|
|
43
|
-
const sqlite3_exec = lib.func('sqlite3_exec', 'int', [sqlite3_db, 'str', 'void *', 'void *', 'void *']);
|
|
44
|
-
const sqlite3_prepare_v2 = lib.func('sqlite3_prepare_v2', 'int', [sqlite3_db, 'str', 'int', koffi.out(koffi.pointer(sqlite3_stmt)), 'string']);
|
|
45
|
-
const sqlite3_reset = lib.func('sqlite3_reset', 'int', [sqlite3_stmt]);
|
|
46
|
-
const sqlite3_bind_text = lib.func('sqlite3_bind_text', 'int', [sqlite3_stmt, 'int', 'str', 'int', 'void *']);
|
|
47
|
-
const sqlite3_bind_int = lib.func('sqlite3_bind_int', 'int', [sqlite3_stmt, 'int', 'int']);
|
|
48
|
-
const sqlite3_column_text = lib.func('sqlite3_column_text', 'str', [sqlite3_stmt, 'int']);
|
|
49
|
-
const sqlite3_column_int = lib.func('sqlite3_column_int', 'int', [sqlite3_stmt, 'int']);
|
|
50
|
-
const sqlite3_step = lib.func('sqlite3_step', 'int', [sqlite3_stmt]);
|
|
51
|
-
const sqlite3_finalize = lib.func('sqlite3_finalize', 'int', [sqlite3_stmt]);
|
|
52
|
-
const sqlite3_close_v2 = lib.func('sqlite3_close_v2', 'int', [sqlite3_db]);
|
|
42
|
+
const sqlite3_open_v2 = lib.func('sqlite3_open_v2', 'int', ['str', koffi.out(koffi.pointer(sqlite3_db, 2)), 'int', 'str']);
|
|
43
|
+
const sqlite3_exec = lib.func('sqlite3_exec', 'int', [koffi.pointer(sqlite3_db), 'str', 'void *', 'void *', 'void *']);
|
|
44
|
+
const sqlite3_prepare_v2 = lib.func('sqlite3_prepare_v2', 'int', [koffi.pointer(sqlite3_db), 'str', 'int', koffi.out(koffi.pointer(sqlite3_stmt, 2)), 'string']);
|
|
45
|
+
const sqlite3_reset = lib.func('sqlite3_reset', 'int', [koffi.pointer(sqlite3_stmt)]);
|
|
46
|
+
const sqlite3_bind_text = lib.func('sqlite3_bind_text', 'int', [koffi.pointer(sqlite3_stmt), 'int', 'str', 'int', 'void *']);
|
|
47
|
+
const sqlite3_bind_int = lib.func('sqlite3_bind_int', 'int', [koffi.pointer(sqlite3_stmt), 'int', 'int']);
|
|
48
|
+
const sqlite3_column_text = lib.func('sqlite3_column_text', 'str', [koffi.pointer(sqlite3_stmt), 'int']);
|
|
49
|
+
const sqlite3_column_int = lib.func('sqlite3_column_int', 'int', [koffi.pointer(sqlite3_stmt), 'int']);
|
|
50
|
+
const sqlite3_step = lib.func('sqlite3_step', 'int', [koffi.pointer(sqlite3_stmt)]);
|
|
51
|
+
const sqlite3_finalize = lib.func('sqlite3_finalize', 'int', [koffi.pointer(sqlite3_stmt)]);
|
|
52
|
+
const sqlite3_close_v2 = lib.func('sqlite3_close_v2', 'int', [koffi.pointer(sqlite3_db)]);
|
|
53
53
|
|
|
54
54
|
const SQLITE_OPEN_READWRITE = 0x2;
|
|
55
55
|
const SQLITE_OPEN_CREATE = 0x4;
|
|
@@ -57,20 +57,27 @@ async function test() {
|
|
|
57
57
|
const SQLITE_DONE = 101;
|
|
58
58
|
|
|
59
59
|
let filename = await create_temporary_file(path.join(os.tmpdir(), 'test_sqlite'));
|
|
60
|
-
let db =
|
|
60
|
+
let db = null;
|
|
61
61
|
|
|
62
62
|
let expected = Array.from(Array(200).keys()).map(i => [`TXT ${i}`, i % 7]);
|
|
63
63
|
|
|
64
64
|
try {
|
|
65
|
-
|
|
65
|
+
let ptr = [null];
|
|
66
|
+
|
|
67
|
+
// Open database
|
|
68
|
+
if (sqlite3_open_v2(filename, ptr, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, null) != 0)
|
|
66
69
|
throw new Error('Failed to open database');
|
|
70
|
+
db = ptr[0];
|
|
71
|
+
|
|
67
72
|
if (sqlite3_exec(db, 'CREATE TABLE foo (id INTEGER PRIMARY KEY, bar TEXT, value INT);', null, null, null) != 0)
|
|
68
73
|
throw new Error('Failed to create table');
|
|
69
74
|
|
|
70
|
-
let stmt =
|
|
75
|
+
let stmt = null;
|
|
71
76
|
|
|
72
|
-
if (sqlite3_prepare_v2(db, "INSERT INTO foo (bar, value) VALUES (?1, ?2)", -1,
|
|
77
|
+
if (sqlite3_prepare_v2(db, "INSERT INTO foo (bar, value) VALUES (?1, ?2)", -1, ptr, null) != 0)
|
|
73
78
|
throw new Error('Failed to prepare insert statement for table foo');
|
|
79
|
+
stmt = ptr[0];
|
|
80
|
+
|
|
74
81
|
for (let it of expected) {
|
|
75
82
|
sqlite3_reset(stmt);
|
|
76
83
|
|
|
@@ -82,8 +89,9 @@ async function test() {
|
|
|
82
89
|
}
|
|
83
90
|
sqlite3_finalize(stmt);
|
|
84
91
|
|
|
85
|
-
if (sqlite3_prepare_v2(db, "SELECT id, bar, value FROM foo ORDER BY id", -1,
|
|
92
|
+
if (sqlite3_prepare_v2(db, "SELECT id, bar, value FROM foo ORDER BY id", -1, ptr, null) != 0)
|
|
86
93
|
throw new Error('Failed to prepare select statement for table foo');
|
|
94
|
+
stmt = ptr[0];
|
|
87
95
|
for (let i = 0; i < expected.length; i++) {
|
|
88
96
|
let it = expected[i];
|
|
89
97
|
|