koffi 1.0.1 → 1.0.4
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 +12 -11
- package/README.md +23 -20
- package/build/qemu/1.0.4/koffi_darwin_x64.tar.gz +0 -0
- package/build/qemu/1.0.4/koffi_freebsd_arm64.tar.gz +0 -0
- package/build/qemu/1.0.4/koffi_freebsd_ia32.tar.gz +0 -0
- package/build/qemu/1.0.4/koffi_freebsd_x64.tar.gz +0 -0
- package/build/qemu/1.0.4/koffi_linux_arm.tar.gz +0 -0
- package/build/qemu/1.0.4/koffi_linux_arm64.tar.gz +0 -0
- package/build/qemu/1.0.4/koffi_linux_ia32.tar.gz +0 -0
- package/build/qemu/1.0.4/koffi_linux_x64.tar.gz +0 -0
- package/build/qemu/1.0.4/koffi_win32_ia32.tar.gz +0 -0
- package/build/qemu/1.0.4/koffi_win32_x64.tar.gz +0 -0
- package/package.json +8 -4
- package/qemu/qemu.js +794 -0
- package/qemu/registry/machines.json +415 -0
- package/qemu/registry/sha256sum.txt +45 -0
- package/src/{call_arm32.cc → abi_arm32.cc} +153 -219
- package/src/{call_arm32_fwd.S → abi_arm32_fwd.S} +0 -0
- package/src/{call_arm64.cc → abi_arm64.cc} +130 -123
- package/src/{call_arm64_fwd.S → abi_arm64_fwd.S} +0 -0
- package/src/{call_x64_sysv.cc → abi_x64_sysv.cc} +138 -135
- package/src/{call_x64_sysv_fwd.S → abi_x64_sysv_fwd.S} +0 -0
- package/src/{call_x64_win.cc → abi_x64_win.cc} +107 -99
- package/src/{call_x64_win_fwd.asm → abi_x64_win_fwd.asm} +0 -0
- package/src/{call_x86.cc → abi_x86.cc} +110 -107
- package/src/{call_x86_fwd.S → abi_x86_fwd.S} +0 -0
- package/src/{call_x86_fwd.asm → abi_x86_fwd.asm} +0 -0
- package/src/call.cc +353 -0
- package/src/call.hh +132 -4
- package/src/ffi.cc +16 -2
- package/src/ffi.hh +8 -12
- package/src/util.cc +7 -280
- package/src/util.hh +0 -107
- package/test/CMakeLists.txt +1 -0
- package/test/misc.c +355 -0
- package/test/misc.def +3 -0
- package/test/misc.js +227 -0
- package/test/raylib.js +165 -0
- package/test/sqlite.js +104 -0
- package/vendor/libcc/libcc.hh +1 -1
- package/build/qemu/1.0.1/koffi_darwin_x64.tar.gz +0 -0
- package/build/qemu/1.0.1/koffi_freebsd_arm64.tar.gz +0 -0
- package/build/qemu/1.0.1/koffi_freebsd_ia32.tar.gz +0 -0
- package/build/qemu/1.0.1/koffi_freebsd_x64.tar.gz +0 -0
- package/build/qemu/1.0.1/koffi_linux_arm.tar.gz +0 -0
- package/build/qemu/1.0.1/koffi_linux_arm64.tar.gz +0 -0
- package/build/qemu/1.0.1/koffi_linux_ia32.tar.gz +0 -0
- package/build/qemu/1.0.1/koffi_linux_x64.tar.gz +0 -0
- package/build/qemu/1.0.1/koffi_win32_ia32.tar.gz +0 -0
- package/build/qemu/1.0.1/koffi_win32_x64.tar.gz +0 -0
package/src/call.cc
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
// This program is free software: you can redistribute it and/or modify
|
|
2
|
+
// it under the terms of the GNU Affero General Public License as published by
|
|
3
|
+
// the Free Software Foundation, either version 3 of the License, or
|
|
4
|
+
// (at your option) any later version.
|
|
5
|
+
//
|
|
6
|
+
// This program is distributed in the hope that it will be useful,
|
|
7
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
8
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
9
|
+
// GNU Affero General Public License for more details.
|
|
10
|
+
//
|
|
11
|
+
// You should have received a copy of the GNU Affero General Public License
|
|
12
|
+
// along with this program. If not, see https://www.gnu.org/licenses/.
|
|
13
|
+
|
|
14
|
+
#include "vendor/libcc/libcc.hh"
|
|
15
|
+
#include "call.hh"
|
|
16
|
+
#include "ffi.hh"
|
|
17
|
+
#include "util.hh"
|
|
18
|
+
|
|
19
|
+
#include <napi.h>
|
|
20
|
+
|
|
21
|
+
namespace RG {
|
|
22
|
+
|
|
23
|
+
CallData::CallData(Napi::Env env, InstanceData *instance, const FunctionInfo *func)
|
|
24
|
+
: env(env), instance(instance), func(func),
|
|
25
|
+
stack_mem(&instance->stack_mem), heap_mem(&instance->heap_mem),
|
|
26
|
+
old_stack_mem(instance->stack_mem), old_heap_mem(instance->heap_mem)
|
|
27
|
+
{
|
|
28
|
+
RG_ASSERT(AlignUp(stack_mem->ptr, 16) == stack_mem->ptr);
|
|
29
|
+
RG_ASSERT(AlignUp(stack_mem->end(), 16) == stack_mem->end());
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
CallData::~CallData()
|
|
33
|
+
{
|
|
34
|
+
instance->stack_mem = old_stack_mem;
|
|
35
|
+
instance->heap_mem = old_heap_mem;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const char *CallData::PushString(const Napi::Value &value)
|
|
39
|
+
{
|
|
40
|
+
RG_ASSERT(value.IsString());
|
|
41
|
+
|
|
42
|
+
Napi::Env env = value.Env();
|
|
43
|
+
|
|
44
|
+
Span<char> buf;
|
|
45
|
+
size_t len = 0;
|
|
46
|
+
napi_status status;
|
|
47
|
+
|
|
48
|
+
buf.ptr = (char *)heap_mem->ptr;
|
|
49
|
+
buf.len = std::max((Size)0, heap_mem->len - Kibibytes(32));
|
|
50
|
+
|
|
51
|
+
status = napi_get_value_string_utf8(env, value, buf.ptr, (size_t)buf.len, &len);
|
|
52
|
+
RG_ASSERT(status == napi_ok);
|
|
53
|
+
|
|
54
|
+
len++;
|
|
55
|
+
|
|
56
|
+
if (RG_LIKELY(len < (size_t)buf.len)) {
|
|
57
|
+
heap_mem->ptr += (Size)len;
|
|
58
|
+
heap_mem->len -= (Size)len;
|
|
59
|
+
} else {
|
|
60
|
+
status = napi_get_value_string_utf8(env, value, nullptr, 0, &len);
|
|
61
|
+
RG_ASSERT(status == napi_ok);
|
|
62
|
+
|
|
63
|
+
len++;
|
|
64
|
+
|
|
65
|
+
buf.ptr = (char *)Allocator::Allocate(&big_alloc, (Size)len);
|
|
66
|
+
buf.len = (Size)len;
|
|
67
|
+
|
|
68
|
+
status = napi_get_value_string_utf8(env, value, buf.ptr, (size_t)buf.len, &len);
|
|
69
|
+
RG_ASSERT(status == napi_ok);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return buf.ptr;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const char16_t *CallData::PushString16(const Napi::Value &value)
|
|
76
|
+
{
|
|
77
|
+
RG_ASSERT(value.IsString());
|
|
78
|
+
|
|
79
|
+
Napi::Env env = value.Env();
|
|
80
|
+
|
|
81
|
+
Span<char16_t> buf;
|
|
82
|
+
size_t len = 0;
|
|
83
|
+
napi_status status;
|
|
84
|
+
|
|
85
|
+
buf.ptr = (char16_t *)heap_mem->ptr;
|
|
86
|
+
buf.len = std::max((Size)0, heap_mem->len - Kibibytes(32)) / 2;
|
|
87
|
+
|
|
88
|
+
status = napi_get_value_string_utf16(env, value, buf.ptr, (size_t)buf.len, &len);
|
|
89
|
+
RG_ASSERT(status == napi_ok);
|
|
90
|
+
|
|
91
|
+
len++;
|
|
92
|
+
|
|
93
|
+
if (RG_LIKELY(len < (size_t)buf.len)) {
|
|
94
|
+
heap_mem->ptr += (Size)len * 2;
|
|
95
|
+
heap_mem->len -= (Size)len * 2;
|
|
96
|
+
} else {
|
|
97
|
+
status = napi_get_value_string_utf16(env, value, nullptr, 0, &len);
|
|
98
|
+
RG_ASSERT(status == napi_ok);
|
|
99
|
+
|
|
100
|
+
len++;
|
|
101
|
+
|
|
102
|
+
buf.ptr = (char16_t *)Allocator::Allocate(&big_alloc, (Size)len * 2);
|
|
103
|
+
buf.len = (Size)len;
|
|
104
|
+
|
|
105
|
+
status = napi_get_value_string_utf16(env, value, buf.ptr, (size_t)buf.len, &len);
|
|
106
|
+
RG_ASSERT(status == napi_ok);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return buf.ptr;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
bool CallData::PushObject(const Napi::Object &obj, const TypeInfo *type, uint8_t *dest)
|
|
113
|
+
{
|
|
114
|
+
Napi::Env env = obj.Env();
|
|
115
|
+
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
116
|
+
|
|
117
|
+
RG_ASSERT(IsObject(obj));
|
|
118
|
+
RG_ASSERT(type->primitive == PrimitiveKind::Record);
|
|
119
|
+
|
|
120
|
+
for (const RecordMember &member: type->members) {
|
|
121
|
+
Napi::Value value = obj.Get(member.name);
|
|
122
|
+
|
|
123
|
+
if (RG_UNLIKELY(value.IsUndefined())) {
|
|
124
|
+
ThrowError<Napi::TypeError>(env, "Missing expected object property '%1'", member.name);
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
dest = AlignUp(dest, member.align);
|
|
129
|
+
|
|
130
|
+
switch (member.type->primitive) {
|
|
131
|
+
case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
|
|
132
|
+
|
|
133
|
+
case PrimitiveKind::Bool: {
|
|
134
|
+
if (RG_UNLIKELY(!value.IsBoolean())) {
|
|
135
|
+
ThrowError<Napi::TypeError>(env, "Unexpected value %1 for member '%2', expected boolean", GetValueType(instance, value), member.name);
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
bool b = value.As<Napi::Boolean>();
|
|
140
|
+
*(bool *)dest = b;
|
|
141
|
+
} break;
|
|
142
|
+
|
|
143
|
+
case PrimitiveKind::Int8:
|
|
144
|
+
case PrimitiveKind::UInt8:
|
|
145
|
+
case PrimitiveKind::Int16:
|
|
146
|
+
case PrimitiveKind::UInt16:
|
|
147
|
+
case PrimitiveKind::Int32:
|
|
148
|
+
case PrimitiveKind::UInt32:
|
|
149
|
+
case PrimitiveKind::Int64:
|
|
150
|
+
case PrimitiveKind::UInt64: {
|
|
151
|
+
if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
|
|
152
|
+
ThrowError<Napi::TypeError>(env, "Unexpected value %1 for member '%2', expected number", GetValueType(instance, value), member.name);
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
int64_t v = CopyNumber<int64_t>(value);
|
|
157
|
+
memcpy(dest, &v, member.type->size); // Little Endian
|
|
158
|
+
} break;
|
|
159
|
+
case PrimitiveKind::String: {
|
|
160
|
+
if (RG_UNLIKELY(!value.IsString())) {
|
|
161
|
+
ThrowError<Napi::TypeError>(env, "Unexpected value %1 for member '%2', expected string", GetValueType(instance, value), member.name);
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const char *str = PushString(value);
|
|
166
|
+
if (RG_UNLIKELY(!str))
|
|
167
|
+
return false;
|
|
168
|
+
*(const char **)dest = str;
|
|
169
|
+
} break;
|
|
170
|
+
case PrimitiveKind::String16: {
|
|
171
|
+
if (RG_UNLIKELY(!value.IsString())) {
|
|
172
|
+
ThrowError<Napi::TypeError>(env, "Unexpected value %1 for member '%2', expected string", GetValueType(instance, value), member.name);
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const char16_t *str16 = PushString16(value);
|
|
177
|
+
if (RG_UNLIKELY(!str16))
|
|
178
|
+
return false;
|
|
179
|
+
*(const char16_t **)dest = str16;
|
|
180
|
+
} break;
|
|
181
|
+
case PrimitiveKind::Pointer: {
|
|
182
|
+
if (RG_UNLIKELY(!CheckValueTag(instance, value, member.type))) {
|
|
183
|
+
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for member '%2', expected %3", GetValueType(instance, value), member.name, member.type->name);
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
Napi::External external = value.As<Napi::External<void>>();
|
|
188
|
+
void *ptr = external.Data();
|
|
189
|
+
*(void **)dest = ptr;
|
|
190
|
+
} break;
|
|
191
|
+
case PrimitiveKind::Record: {
|
|
192
|
+
if (RG_UNLIKELY(!IsObject(value))) {
|
|
193
|
+
ThrowError<Napi::TypeError>(env, "Unexpected value %1 for member '%2', expected object", GetValueType(instance, value), member.name);
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
Napi::Object obj = value.As<Napi::Object>();
|
|
198
|
+
if (!PushObject(obj, member.type, dest))
|
|
199
|
+
return false;
|
|
200
|
+
} break;
|
|
201
|
+
case PrimitiveKind::Float32: {
|
|
202
|
+
if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
|
|
203
|
+
ThrowError<Napi::TypeError>(env, "Unexpected value %1 for member '%2', expected number", GetValueType(instance, value), member.name);
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
float f = CopyNumber<float>(value);
|
|
208
|
+
memcpy(dest, &f, 4);
|
|
209
|
+
} break;
|
|
210
|
+
case PrimitiveKind::Float64: {
|
|
211
|
+
if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
|
|
212
|
+
ThrowError<Napi::TypeError>(env, "Unexpected value %1 for member '%2', expected number", GetValueType(instance, value), member.name);
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
double d = CopyNumber<double>(value);
|
|
217
|
+
memcpy(dest, &d, 8);
|
|
218
|
+
} break;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
dest += member.type->size;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
void CallData::PopObject(Napi::Object obj, const uint8_t *ptr, const TypeInfo *type)
|
|
228
|
+
{
|
|
229
|
+
Napi::Env env = obj.Env();
|
|
230
|
+
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
231
|
+
|
|
232
|
+
RG_ASSERT(type->primitive == PrimitiveKind::Record);
|
|
233
|
+
|
|
234
|
+
for (const RecordMember &member: type->members) {
|
|
235
|
+
ptr = AlignUp(ptr, member.align);
|
|
236
|
+
|
|
237
|
+
switch (member.type->primitive) {
|
|
238
|
+
case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
|
|
239
|
+
|
|
240
|
+
case PrimitiveKind::Bool: {
|
|
241
|
+
bool b = *(bool *)ptr;
|
|
242
|
+
obj.Set(member.name, Napi::Boolean::New(env, b));
|
|
243
|
+
} break;
|
|
244
|
+
case PrimitiveKind::Int8: {
|
|
245
|
+
double d = (double)*(int8_t *)ptr;
|
|
246
|
+
obj.Set(member.name, Napi::Number::New(env, d));
|
|
247
|
+
} break;
|
|
248
|
+
case PrimitiveKind::UInt8: {
|
|
249
|
+
double d = (double)*(uint8_t *)ptr;
|
|
250
|
+
obj.Set(member.name, Napi::Number::New(env, d));
|
|
251
|
+
} break;
|
|
252
|
+
case PrimitiveKind::Int16: {
|
|
253
|
+
double d = (double)*(int16_t *)ptr;
|
|
254
|
+
obj.Set(member.name, Napi::Number::New(env, d));
|
|
255
|
+
} break;
|
|
256
|
+
case PrimitiveKind::UInt16: {
|
|
257
|
+
double d = (double)*(uint16_t *)ptr;
|
|
258
|
+
obj.Set(member.name, Napi::Number::New(env, d));
|
|
259
|
+
} break;
|
|
260
|
+
case PrimitiveKind::Int32: {
|
|
261
|
+
double d = (double)*(int32_t *)ptr;
|
|
262
|
+
obj.Set(member.name, Napi::Number::New(env, d));
|
|
263
|
+
} break;
|
|
264
|
+
case PrimitiveKind::UInt32: {
|
|
265
|
+
double d = (double)*(uint32_t *)ptr;
|
|
266
|
+
obj.Set(member.name, Napi::Number::New(env, d));
|
|
267
|
+
} break;
|
|
268
|
+
case PrimitiveKind::Int64: {
|
|
269
|
+
int64_t v = *(int64_t *)ptr;
|
|
270
|
+
obj.Set(member.name, Napi::BigInt::New(env, v));
|
|
271
|
+
} break;
|
|
272
|
+
case PrimitiveKind::UInt64: {
|
|
273
|
+
uint64_t v = *(uint64_t *)ptr;
|
|
274
|
+
obj.Set(member.name, Napi::BigInt::New(env, v));
|
|
275
|
+
} break;
|
|
276
|
+
case PrimitiveKind::String: {
|
|
277
|
+
const char *str = *(const char **)ptr;
|
|
278
|
+
obj.Set(member.name, Napi::String::New(env, str));
|
|
279
|
+
} break;
|
|
280
|
+
case PrimitiveKind::String16: {
|
|
281
|
+
const char16_t *str16 = *(const char16_t **)ptr;
|
|
282
|
+
obj.Set(member.name, Napi::String::New(env, str16));
|
|
283
|
+
} break;
|
|
284
|
+
case PrimitiveKind::Pointer: {
|
|
285
|
+
void *ptr2 = *(void **)ptr;
|
|
286
|
+
|
|
287
|
+
Napi::External<void> external = Napi::External<void>::New(env, ptr2);
|
|
288
|
+
SetValueTag(instance, external, member.type);
|
|
289
|
+
|
|
290
|
+
obj.Set(member.name, external);
|
|
291
|
+
} break;
|
|
292
|
+
case PrimitiveKind::Record: {
|
|
293
|
+
Napi::Object obj2 = PopObject(ptr, member.type);
|
|
294
|
+
obj.Set(member.name, obj2);
|
|
295
|
+
} break;
|
|
296
|
+
case PrimitiveKind::Float32: {
|
|
297
|
+
float f;
|
|
298
|
+
memcpy(&f, ptr, 4);
|
|
299
|
+
obj.Set(member.name, Napi::Number::New(env, (double)f));
|
|
300
|
+
} break;
|
|
301
|
+
case PrimitiveKind::Float64: {
|
|
302
|
+
double d;
|
|
303
|
+
memcpy(&d, ptr, 8);
|
|
304
|
+
obj.Set(member.name, Napi::Number::New(env, d));
|
|
305
|
+
} break;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
ptr += member.type->size;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
Napi::Object CallData::PopObject(const uint8_t *ptr, const TypeInfo *type)
|
|
313
|
+
{
|
|
314
|
+
Napi::Object obj = Napi::Object::New(env);
|
|
315
|
+
PopObject(obj, ptr, type);
|
|
316
|
+
return obj;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
static void DumpMemory(const char *type, Span<const uint8_t> bytes)
|
|
320
|
+
{
|
|
321
|
+
if (bytes.len) {
|
|
322
|
+
PrintLn(stderr, "%1 at 0x%2 (%3):", type, bytes.ptr, FmtMemSize(bytes.len));
|
|
323
|
+
|
|
324
|
+
for (const uint8_t *ptr = bytes.begin(); ptr < bytes.end();) {
|
|
325
|
+
Print(stderr, " [0x%1 %2 %3] ", FmtArg(ptr).Pad0(-16),
|
|
326
|
+
FmtArg((ptr - bytes.begin()) / sizeof(void *)).Pad(-4),
|
|
327
|
+
FmtArg(ptr - bytes.begin()).Pad(-4));
|
|
328
|
+
for (int i = 0; ptr < bytes.end() && i < (int)sizeof(void *); i++, ptr++) {
|
|
329
|
+
Print(stderr, " %1", FmtHex(*ptr).Pad0(-2));
|
|
330
|
+
}
|
|
331
|
+
PrintLn(stderr);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
void CallData::DumpDebug() const
|
|
337
|
+
{
|
|
338
|
+
PrintLn(stderr, "%!..+---- %1 (%2) ----%!0", func->name, CallConventionNames[(int)func->convention]);
|
|
339
|
+
|
|
340
|
+
if (func->parameters.len) {
|
|
341
|
+
PrintLn(stderr, "Parameters:");
|
|
342
|
+
for (Size i = 0; i < func->parameters.len; i++) {
|
|
343
|
+
const ParameterInfo ¶m = func->parameters[i];
|
|
344
|
+
PrintLn(stderr, " %1 = %2 (%3)", i, param.type->name, FmtMemSize(param.type->size));
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
PrintLn(stderr, "Return: %1 (%2)", func->ret.type->name, FmtMemSize(func->ret.type->size));
|
|
348
|
+
|
|
349
|
+
DumpMemory("Stack", GetStack());
|
|
350
|
+
DumpMemory("Heap", GetHeap());
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
}
|
package/src/call.hh
CHANGED
|
@@ -14,15 +14,143 @@
|
|
|
14
14
|
#pragma once
|
|
15
15
|
|
|
16
16
|
#include "vendor/libcc/libcc.hh"
|
|
17
|
+
#include "ffi.hh"
|
|
18
|
+
#include "util.hh"
|
|
17
19
|
|
|
18
20
|
#include <napi.h>
|
|
19
21
|
|
|
20
22
|
namespace RG {
|
|
21
23
|
|
|
22
|
-
struct InstanceData;
|
|
23
|
-
struct FunctionInfo;
|
|
24
|
-
|
|
25
24
|
bool AnalyseFunction(InstanceData *instance, FunctionInfo *func);
|
|
26
|
-
|
|
25
|
+
|
|
26
|
+
class CallData {
|
|
27
|
+
Napi::Env env;
|
|
28
|
+
InstanceData *instance;
|
|
29
|
+
const FunctionInfo *func;
|
|
30
|
+
|
|
31
|
+
struct OutObject {
|
|
32
|
+
Napi::Object obj;
|
|
33
|
+
const uint8_t *ptr;
|
|
34
|
+
const TypeInfo *type;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
Span<uint8_t> *stack_mem;
|
|
38
|
+
Span<uint8_t> *heap_mem;
|
|
39
|
+
LocalArray<OutObject, MaxOutParameters> out_objects;
|
|
40
|
+
BlockAllocator big_alloc;
|
|
41
|
+
|
|
42
|
+
Span<uint8_t> old_stack_mem;
|
|
43
|
+
Span<uint8_t> old_heap_mem;
|
|
44
|
+
|
|
45
|
+
union {
|
|
46
|
+
uint32_t u32;
|
|
47
|
+
uint64_t u64;
|
|
48
|
+
float f;
|
|
49
|
+
double d;
|
|
50
|
+
void *ptr;
|
|
51
|
+
uint8_t buf[32];
|
|
52
|
+
} result;
|
|
53
|
+
uint8_t *return_ptr = nullptr;
|
|
54
|
+
|
|
55
|
+
public:
|
|
56
|
+
CallData(Napi::Env env, InstanceData *instance, const FunctionInfo *func);
|
|
57
|
+
~CallData();
|
|
58
|
+
|
|
59
|
+
Span<uint8_t> GetStack() const
|
|
60
|
+
{
|
|
61
|
+
uint8_t *sp = stack_mem->end();
|
|
62
|
+
Size len = old_stack_mem.end() - sp;
|
|
63
|
+
|
|
64
|
+
return MakeSpan(sp, len);
|
|
65
|
+
}
|
|
66
|
+
uint8_t *GetSP() const { return stack_mem->end(); };
|
|
67
|
+
|
|
68
|
+
Span<uint8_t> GetHeap() const
|
|
69
|
+
{
|
|
70
|
+
uint8_t *ptr = old_heap_mem.ptr;
|
|
71
|
+
Size len = heap_mem->ptr - ptr;
|
|
72
|
+
|
|
73
|
+
return MakeSpan(ptr, len);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
bool Prepare(const Napi::CallbackInfo &info);
|
|
77
|
+
void Execute();
|
|
78
|
+
Napi::Value Complete();
|
|
79
|
+
|
|
80
|
+
Napi::Value Run(const Napi::CallbackInfo &info)
|
|
81
|
+
{
|
|
82
|
+
if (!RG_UNLIKELY(Prepare(info)))
|
|
83
|
+
return env.Null();
|
|
84
|
+
|
|
85
|
+
if (instance->debug) {
|
|
86
|
+
DumpDebug();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
Execute();
|
|
90
|
+
return Complete();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
void DumpDebug() const;
|
|
94
|
+
|
|
95
|
+
private:
|
|
96
|
+
template <typename T = void>
|
|
97
|
+
bool AllocStack(Size size, Size align, T **out_ptr = nullptr);
|
|
98
|
+
template <typename T = void>
|
|
99
|
+
bool AllocHeap(Size size, Size align, T **out_ptr = nullptr);
|
|
100
|
+
|
|
101
|
+
const char *PushString(const Napi::Value &value);
|
|
102
|
+
const char16_t *PushString16(const Napi::Value &value);
|
|
103
|
+
bool PushObject(const Napi::Object &obj, const TypeInfo *type, uint8_t *dest);
|
|
104
|
+
|
|
105
|
+
void PopObject(Napi::Object obj, const uint8_t *ptr, const TypeInfo *type);
|
|
106
|
+
Napi::Object PopObject(const uint8_t *ptr, const TypeInfo *type);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
template <typename T>
|
|
110
|
+
bool CallData::AllocStack(Size size, Size align, T **out_ptr)
|
|
111
|
+
{
|
|
112
|
+
uint8_t *ptr = AlignDown(stack_mem->end() - size, align);
|
|
113
|
+
Size delta = stack_mem->end() - ptr;
|
|
114
|
+
|
|
115
|
+
if (RG_UNLIKELY(stack_mem->len < delta)) {
|
|
116
|
+
ThrowError<Napi::Error>(env, "FFI call is taking up too much memory");
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (instance->debug) {
|
|
121
|
+
memset(ptr, 0, delta);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
stack_mem->len -= delta;
|
|
125
|
+
|
|
126
|
+
if (out_ptr) {
|
|
127
|
+
*out_ptr = (T *)ptr;
|
|
128
|
+
}
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
template <typename T>
|
|
133
|
+
bool CallData::AllocHeap(Size size, Size align, T **out_ptr)
|
|
134
|
+
{
|
|
135
|
+
uint8_t *ptr = AlignUp(heap_mem->ptr, align);
|
|
136
|
+
Size delta = size + (ptr - heap_mem->ptr);
|
|
137
|
+
|
|
138
|
+
if (RG_UNLIKELY(delta > heap_mem->len)) {
|
|
139
|
+
ThrowError<Napi::Error>(env, "FFI call is taking up too much memory");
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (instance->debug) {
|
|
144
|
+
memset(heap_mem->ptr, 0, (size_t)delta);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
heap_mem->ptr += delta;
|
|
148
|
+
heap_mem->len -= delta;
|
|
149
|
+
|
|
150
|
+
if (out_ptr) {
|
|
151
|
+
*out_ptr = (T *)ptr;
|
|
152
|
+
}
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
27
155
|
|
|
28
156
|
}
|
package/src/ffi.cc
CHANGED
|
@@ -247,7 +247,13 @@ static Napi::Value TranslateNormalCall(const Napi::CallbackInfo &info)
|
|
|
247
247
|
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
248
248
|
FunctionInfo *func = (FunctionInfo *)info.Data();
|
|
249
249
|
|
|
250
|
-
|
|
250
|
+
if (info.Length() < (uint32_t)func->parameters.len) {
|
|
251
|
+
ThrowError<Napi::TypeError>(env, "Expected %1 arguments, got %2", func->parameters.len, info.Length());
|
|
252
|
+
return env.Null();
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
CallData call(env, instance, func);
|
|
256
|
+
return call.Run(info);
|
|
251
257
|
}
|
|
252
258
|
|
|
253
259
|
static Napi::Value TranslateVariadicCall(const Napi::CallbackInfo &info)
|
|
@@ -265,6 +271,10 @@ static Napi::Value TranslateVariadicCall(const Napi::CallbackInfo &info)
|
|
|
265
271
|
func.parameters.Leak();
|
|
266
272
|
};
|
|
267
273
|
|
|
274
|
+
if (info.Length() < (uint32_t)func.parameters.len) {
|
|
275
|
+
ThrowError<Napi::TypeError>(env, "Expected %1 arguments or more, got %2", func.parameters.len, info.Length());
|
|
276
|
+
return env.Null();
|
|
277
|
+
}
|
|
268
278
|
if ((info.Length() - func.parameters.len) % 2) {
|
|
269
279
|
ThrowError<Napi::Error>(env, "Missing value argument for variadic call");
|
|
270
280
|
return env.Null();
|
|
@@ -299,7 +309,8 @@ static Napi::Value TranslateVariadicCall(const Napi::CallbackInfo &info)
|
|
|
299
309
|
if (!AnalyseFunction(instance, &func))
|
|
300
310
|
return env.Null();
|
|
301
311
|
|
|
302
|
-
|
|
312
|
+
CallData call(env, instance, &func);
|
|
313
|
+
return call.Run(info);
|
|
303
314
|
}
|
|
304
315
|
|
|
305
316
|
static bool ParseClassicFunction(Napi::Env env, Napi::String name, Napi::Value ret,
|
|
@@ -573,6 +584,8 @@ static Napi::Object InitBaseTypes(Napi::Env env)
|
|
|
573
584
|
RegisterPrimitiveType(instance, "char", PrimitiveKind::Int8, 1, 1);
|
|
574
585
|
RegisterPrimitiveType(instance, "uchar", PrimitiveKind::UInt8, 1, 1);
|
|
575
586
|
RegisterPrimitiveType(instance, "unsigned char", PrimitiveKind::UInt8, 1, 1);
|
|
587
|
+
RegisterPrimitiveType(instance, "char16", PrimitiveKind::Int16, 2, 2);
|
|
588
|
+
RegisterPrimitiveType(instance, "char16_t", PrimitiveKind::Int16, 2, 2);
|
|
576
589
|
RegisterPrimitiveType(instance, "int16", PrimitiveKind::Int16, 2, 2);
|
|
577
590
|
RegisterPrimitiveType(instance, "int16_t", PrimitiveKind::Int16, 2, 2);
|
|
578
591
|
RegisterPrimitiveType(instance, "uint16", PrimitiveKind::UInt16, 2, 2);
|
|
@@ -603,6 +616,7 @@ static Napi::Object InitBaseTypes(Napi::Env env)
|
|
|
603
616
|
RegisterPrimitiveType(instance, "float", PrimitiveKind::Float32, 4, alignof(float));
|
|
604
617
|
RegisterPrimitiveType(instance, "double", PrimitiveKind::Float64, 8, alignof(double));
|
|
605
618
|
RegisterPrimitiveType(instance, "string", PrimitiveKind::String, RG_SIZE(void *), alignof(void *));
|
|
619
|
+
RegisterPrimitiveType(instance, "string16", PrimitiveKind::String16, RG_SIZE(void *), alignof(void *));
|
|
606
620
|
|
|
607
621
|
Napi::Object types = Napi::Object::New(env);
|
|
608
622
|
for (TypeInfo &type: instance->types) {
|
package/src/ffi.hh
CHANGED
|
@@ -26,7 +26,6 @@ extern const int TypeInfoMarker;
|
|
|
26
26
|
|
|
27
27
|
enum class PrimitiveKind {
|
|
28
28
|
Void,
|
|
29
|
-
|
|
30
29
|
Bool,
|
|
31
30
|
Int8,
|
|
32
31
|
UInt8,
|
|
@@ -36,17 +35,15 @@ enum class PrimitiveKind {
|
|
|
36
35
|
UInt32,
|
|
37
36
|
Int64,
|
|
38
37
|
UInt64,
|
|
39
|
-
Float32,
|
|
40
|
-
Float64,
|
|
41
38
|
String,
|
|
42
|
-
|
|
39
|
+
String16,
|
|
43
40
|
Pointer,
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
Record,
|
|
42
|
+
Float32,
|
|
43
|
+
Float64
|
|
46
44
|
};
|
|
47
45
|
static const char *const PrimitiveKindNames[] = {
|
|
48
46
|
"Void",
|
|
49
|
-
|
|
50
47
|
"Bool",
|
|
51
48
|
"Int8",
|
|
52
49
|
"UInt8",
|
|
@@ -56,13 +53,12 @@ static const char *const PrimitiveKindNames[] = {
|
|
|
56
53
|
"UInt32",
|
|
57
54
|
"Int64",
|
|
58
55
|
"UInt64",
|
|
59
|
-
"Float32",
|
|
60
|
-
"Float64",
|
|
61
56
|
"String",
|
|
62
|
-
|
|
57
|
+
"String16",
|
|
63
58
|
"Pointer",
|
|
64
|
-
|
|
65
|
-
"
|
|
59
|
+
"Record",
|
|
60
|
+
"Float32",
|
|
61
|
+
"Float64"
|
|
66
62
|
};
|
|
67
63
|
|
|
68
64
|
struct TypeInfo;
|