koffi 1.0.5 → 1.1.0-beta.2
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 +4 -0
- package/README.md +54 -24
- package/build/qemu/1.1.0-beta.2/koffi_darwin_x64.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.2/koffi_freebsd_arm64.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.2/koffi_freebsd_ia32.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.2/koffi_freebsd_x64.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.2/koffi_linux_arm.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.2/koffi_linux_arm64.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.2/koffi_linux_ia32.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.2/koffi_linux_x64.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.2/koffi_win32_ia32.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.2/koffi_win32_x64.tar.gz +0 -0
- package/package.json +1 -1
- package/qemu/qemu.js +12 -5
- package/qemu/registry/machines.json +20 -10
- package/src/abi_arm32.cc +40 -51
- package/src/abi_arm64.cc +71 -138
- package/src/abi_x64_sysv.cc +37 -13
- package/src/abi_x64_win.cc +16 -6
- package/src/abi_x86.cc +16 -6
- package/src/call.cc +564 -58
- package/src/call.hh +32 -44
- package/src/ffi.cc +218 -19
- package/src/ffi.hh +27 -11
- package/src/parser.cc +4 -0
- package/src/util.cc +72 -0
- package/src/util.hh +2 -0
- package/test/misc.c +16 -10
- package/build/qemu/1.0.5/koffi_darwin_x64.tar.gz +0 -0
- package/build/qemu/1.0.5/koffi_freebsd_arm64.tar.gz +0 -0
- package/build/qemu/1.0.5/koffi_freebsd_ia32.tar.gz +0 -0
- package/build/qemu/1.0.5/koffi_freebsd_x64.tar.gz +0 -0
- package/build/qemu/1.0.5/koffi_linux_arm.tar.gz +0 -0
- package/build/qemu/1.0.5/koffi_linux_arm64.tar.gz +0 -0
- package/build/qemu/1.0.5/koffi_linux_ia32.tar.gz +0 -0
- package/build/qemu/1.0.5/koffi_linux_x64.tar.gz +0 -0
- package/build/qemu/1.0.5/koffi_win32_ia32.tar.gz +0 -0
- package/build/qemu/1.0.5/koffi_win32_x64.tar.gz +0 -0
- package/test/misc.js +0 -227
package/src/call.hh
CHANGED
|
@@ -24,24 +24,27 @@ namespace RG {
|
|
|
24
24
|
bool AnalyseFunction(InstanceData *instance, FunctionInfo *func);
|
|
25
25
|
|
|
26
26
|
class CallData {
|
|
27
|
-
Napi::Env env;
|
|
28
|
-
InstanceData *instance;
|
|
29
|
-
const FunctionInfo *func;
|
|
30
|
-
|
|
31
27
|
struct OutObject {
|
|
32
|
-
Napi::
|
|
28
|
+
Napi::ObjectReference ref;
|
|
33
29
|
const uint8_t *ptr;
|
|
34
30
|
const TypeInfo *type;
|
|
35
31
|
};
|
|
36
32
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
Napi::Env env;
|
|
34
|
+
InstanceData *instance;
|
|
35
|
+
const FunctionInfo *func;
|
|
36
|
+
|
|
37
|
+
bool debug;
|
|
41
38
|
|
|
39
|
+
InstanceMemory *mem;
|
|
42
40
|
Span<uint8_t> old_stack_mem;
|
|
43
41
|
Span<uint8_t> old_heap_mem;
|
|
44
42
|
|
|
43
|
+
LocalArray<OutObject, MaxOutParameters> out_objects;
|
|
44
|
+
|
|
45
|
+
Span<uint8_t> heap;
|
|
46
|
+
Span<uint8_t> stack;
|
|
47
|
+
|
|
45
48
|
union {
|
|
46
49
|
uint32_t u32;
|
|
47
50
|
uint64_t u64;
|
|
@@ -53,26 +56,9 @@ class CallData {
|
|
|
53
56
|
uint8_t *return_ptr = nullptr;
|
|
54
57
|
|
|
55
58
|
public:
|
|
56
|
-
CallData(Napi::Env env,
|
|
59
|
+
CallData(Napi::Env env, const FunctionInfo *func, InstanceMemory *mem, bool debug);
|
|
57
60
|
~CallData();
|
|
58
61
|
|
|
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
62
|
bool Prepare(const Napi::CallbackInfo &info);
|
|
77
63
|
void Execute();
|
|
78
64
|
Napi::Value Complete();
|
|
@@ -82,11 +68,11 @@ public:
|
|
|
82
68
|
if (!RG_UNLIKELY(Prepare(info)))
|
|
83
69
|
return env.Null();
|
|
84
70
|
|
|
85
|
-
if (
|
|
71
|
+
if (debug) {
|
|
86
72
|
DumpDebug();
|
|
87
73
|
}
|
|
88
|
-
|
|
89
74
|
Execute();
|
|
75
|
+
|
|
90
76
|
return Complete();
|
|
91
77
|
}
|
|
92
78
|
|
|
@@ -100,28 +86,30 @@ private:
|
|
|
100
86
|
|
|
101
87
|
const char *PushString(const Napi::Value &value);
|
|
102
88
|
const char16_t *PushString16(const Napi::Value &value);
|
|
103
|
-
bool PushObject(const Napi::Object &obj, const TypeInfo *type, uint8_t *dest);
|
|
89
|
+
bool PushObject(const Napi::Object &obj, const TypeInfo *type, uint8_t *dest, int16_t realign = 0);
|
|
90
|
+
bool PushArray(const Napi::Object &obj, const TypeInfo *type, uint8_t *dest, int16_t realign = 0);
|
|
104
91
|
|
|
105
|
-
void PopObject(Napi::Object obj, const uint8_t *
|
|
106
|
-
Napi::Object PopObject(const uint8_t *
|
|
92
|
+
void PopObject(Napi::Object obj, const uint8_t *src, const TypeInfo *type, int16_t realign = 0);
|
|
93
|
+
Napi::Object PopObject(const uint8_t *src, const TypeInfo *type, int16_t realign = 0);
|
|
94
|
+
Napi::Object PopArray(const uint8_t *src, const TypeInfo *type, int16_t realign = 0);
|
|
107
95
|
};
|
|
108
96
|
|
|
109
97
|
template <typename T>
|
|
110
98
|
bool CallData::AllocStack(Size size, Size align, T **out_ptr)
|
|
111
99
|
{
|
|
112
|
-
uint8_t *ptr = AlignDown(
|
|
113
|
-
Size delta =
|
|
100
|
+
uint8_t *ptr = AlignDown(mem->stack.end() - size, align);
|
|
101
|
+
Size delta = mem->stack.end() - ptr;
|
|
114
102
|
|
|
115
|
-
if (RG_UNLIKELY(
|
|
103
|
+
if (RG_UNLIKELY(mem->stack.len < delta)) {
|
|
116
104
|
ThrowError<Napi::Error>(env, "FFI call is taking up too much memory");
|
|
117
105
|
return false;
|
|
118
106
|
}
|
|
119
107
|
|
|
120
|
-
if (
|
|
108
|
+
if (debug) {
|
|
121
109
|
memset(ptr, 0, delta);
|
|
122
110
|
}
|
|
123
111
|
|
|
124
|
-
|
|
112
|
+
mem->stack.len -= delta;
|
|
125
113
|
|
|
126
114
|
if (out_ptr) {
|
|
127
115
|
*out_ptr = (T *)ptr;
|
|
@@ -132,20 +120,20 @@ bool CallData::AllocStack(Size size, Size align, T **out_ptr)
|
|
|
132
120
|
template <typename T>
|
|
133
121
|
bool CallData::AllocHeap(Size size, Size align, T **out_ptr)
|
|
134
122
|
{
|
|
135
|
-
uint8_t *ptr = AlignUp(
|
|
136
|
-
Size delta = size + (ptr -
|
|
123
|
+
uint8_t *ptr = AlignUp(mem->heap.ptr, align);
|
|
124
|
+
Size delta = size + (ptr - mem->heap.ptr);
|
|
137
125
|
|
|
138
|
-
if (RG_UNLIKELY(delta >
|
|
126
|
+
if (RG_UNLIKELY(delta > mem->heap.len)) {
|
|
139
127
|
ThrowError<Napi::Error>(env, "FFI call is taking up too much memory");
|
|
140
128
|
return false;
|
|
141
129
|
}
|
|
142
130
|
|
|
143
|
-
if (
|
|
144
|
-
memset(
|
|
131
|
+
if (debug) {
|
|
132
|
+
memset(mem->heap.ptr, 0, (size_t)delta);
|
|
145
133
|
}
|
|
146
134
|
|
|
147
|
-
|
|
148
|
-
|
|
135
|
+
mem->heap.ptr += delta;
|
|
136
|
+
mem->heap.len -= delta;
|
|
149
137
|
|
|
150
138
|
if (out_ptr) {
|
|
151
139
|
*out_ptr = (T *)ptr;
|
package/src/ffi.cc
CHANGED
|
@@ -85,6 +85,10 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
|
|
|
85
85
|
member.type = ResolveType(instance, value);
|
|
86
86
|
if (!member.type)
|
|
87
87
|
return env.Null();
|
|
88
|
+
if (member.type->primitive == PrimitiveKind::Void) {
|
|
89
|
+
ThrowError<Napi::TypeError>(env, "Type Void cannot be used as a member");
|
|
90
|
+
return env.Null();
|
|
91
|
+
}
|
|
88
92
|
|
|
89
93
|
member.align = pad ? member.type->align : 1;
|
|
90
94
|
|
|
@@ -241,6 +245,93 @@ static Napi::Value MarkInOut(const Napi::CallbackInfo &info)
|
|
|
241
245
|
return EncodePointerDirection(info, 3);
|
|
242
246
|
}
|
|
243
247
|
|
|
248
|
+
static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
|
|
249
|
+
{
|
|
250
|
+
Napi::Env env = info.Env();
|
|
251
|
+
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
252
|
+
|
|
253
|
+
if (info.Length() < 2) {
|
|
254
|
+
ThrowError<Napi::TypeError>(env, "Expected 2 arguments, got %1", info.Length());
|
|
255
|
+
return env.Null();
|
|
256
|
+
}
|
|
257
|
+
if (!info[1].IsNumber()) {
|
|
258
|
+
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for length, expected integer", GetValueType(instance, info[1]));
|
|
259
|
+
return env.Null();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const TypeInfo *ref = ResolveType(instance, info[0]);
|
|
263
|
+
int64_t len = (uint16_t)info[1].As<Napi::Number>().Int64Value();
|
|
264
|
+
|
|
265
|
+
if (!ref)
|
|
266
|
+
return env.Null();
|
|
267
|
+
if (len <= 0) {
|
|
268
|
+
ThrowError<Napi::TypeError>(env, "Array length must be non-zero positive");
|
|
269
|
+
return env.Null();
|
|
270
|
+
}
|
|
271
|
+
if (len > INT16_MAX / ref->size) {
|
|
272
|
+
ThrowError<Napi::TypeError>(env, "Array length is too high (max = %1)", INT16_MAX / ref->size);
|
|
273
|
+
return env.Null();
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
TypeInfo *type = instance->types.AppendDefault();
|
|
277
|
+
RG_DEFER_N(err_guard) { instance->types.RemoveLast(1); };
|
|
278
|
+
|
|
279
|
+
type->name = Fmt(&instance->str_alloc, "%1[%2]", ref->name, len).ptr;
|
|
280
|
+
|
|
281
|
+
type->primitive = PrimitiveKind::Array;
|
|
282
|
+
type->align = ref->align;
|
|
283
|
+
type->size = (int16_t)(len * ref->size);
|
|
284
|
+
type->ref = ref;
|
|
285
|
+
|
|
286
|
+
// If the insert succeeds, we cannot fail anymore
|
|
287
|
+
if (!instance->types_map.TrySet(type).second) {
|
|
288
|
+
ThrowError<Napi::Error>(env, "Duplicate type name '%1'", type->name);
|
|
289
|
+
return env.Null();
|
|
290
|
+
}
|
|
291
|
+
err_guard.Disable();
|
|
292
|
+
|
|
293
|
+
Napi::External<TypeInfo> external = Napi::External<TypeInfo>::New(env, type);
|
|
294
|
+
SetValueTag(instance, external, &TypeInfoMarker);
|
|
295
|
+
|
|
296
|
+
return external;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
static Span<uint8_t> AllocateAndAlign16(Allocator *alloc, Size size)
|
|
300
|
+
{
|
|
301
|
+
RG_ASSERT(AlignLen(size, 16) == size);
|
|
302
|
+
RG_ASSERT(size >= Kibibytes(1));
|
|
303
|
+
|
|
304
|
+
// Account for allocator overhead
|
|
305
|
+
size -= 256;
|
|
306
|
+
|
|
307
|
+
uint8_t *ptr = (uint8_t *)Allocator::Allocate(alloc, size);
|
|
308
|
+
uint8_t *aligned = AlignUp(ptr, 16);
|
|
309
|
+
Size delta = AlignLen(aligned - ptr, 16);
|
|
310
|
+
|
|
311
|
+
return MakeSpan(aligned, size - delta);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
static InstanceMemory *AllocateCallMemory(InstanceData *instance)
|
|
315
|
+
{
|
|
316
|
+
for (InstanceMemory *mem: instance->memories) {
|
|
317
|
+
if (!mem->depth)
|
|
318
|
+
return mem;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
InstanceMemory *mem = new InstanceMemory();
|
|
322
|
+
|
|
323
|
+
mem->stack = AllocateAndAlign16(&mem->mem_alloc, Mebibytes(1));
|
|
324
|
+
mem->heap = AllocateAndAlign16(&mem->mem_alloc, Mebibytes(2));
|
|
325
|
+
|
|
326
|
+
if (instance->memories.Available()) {
|
|
327
|
+
instance->memories.Append(mem);
|
|
328
|
+
} else {
|
|
329
|
+
mem->temporary = true;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return mem;
|
|
333
|
+
}
|
|
334
|
+
|
|
244
335
|
static Napi::Value TranslateNormalCall(const Napi::CallbackInfo &info)
|
|
245
336
|
{
|
|
246
337
|
Napi::Env env = info.Env();
|
|
@@ -252,7 +343,9 @@ static Napi::Value TranslateNormalCall(const Napi::CallbackInfo &info)
|
|
|
252
343
|
return env.Null();
|
|
253
344
|
}
|
|
254
345
|
|
|
255
|
-
|
|
346
|
+
InstanceMemory *mem = AllocateCallMemory(instance);
|
|
347
|
+
CallData call(env, func, mem, instance->debug);
|
|
348
|
+
|
|
256
349
|
return call.Run(info);
|
|
257
350
|
}
|
|
258
351
|
|
|
@@ -286,8 +379,9 @@ static Napi::Value TranslateVariadicCall(const Napi::CallbackInfo &info)
|
|
|
286
379
|
param.type = ResolveType(instance, info[i], ¶m.directions);
|
|
287
380
|
if (!param.type)
|
|
288
381
|
return env.Null();
|
|
289
|
-
if (param.type->primitive == PrimitiveKind::Void
|
|
290
|
-
|
|
382
|
+
if (param.type->primitive == PrimitiveKind::Void ||
|
|
383
|
+
param.type->primitive == PrimitiveKind::Array) {
|
|
384
|
+
ThrowError<Napi::TypeError>(env, "Type %1 cannot be used as a parameter", PrimitiveKindNames[(int)param.type->primitive]);
|
|
291
385
|
return env.Null();
|
|
292
386
|
}
|
|
293
387
|
|
|
@@ -309,10 +403,93 @@ static Napi::Value TranslateVariadicCall(const Napi::CallbackInfo &info)
|
|
|
309
403
|
if (!AnalyseFunction(instance, &func))
|
|
310
404
|
return env.Null();
|
|
311
405
|
|
|
312
|
-
|
|
406
|
+
InstanceMemory *mem = AllocateCallMemory(instance);
|
|
407
|
+
CallData call(env, &func, mem, instance->debug);
|
|
408
|
+
|
|
313
409
|
return call.Run(info);
|
|
314
410
|
}
|
|
315
411
|
|
|
412
|
+
class AsyncCall: public Napi::AsyncWorker {
|
|
413
|
+
Napi::Env env;
|
|
414
|
+
const FunctionInfo *func;
|
|
415
|
+
|
|
416
|
+
CallData call;
|
|
417
|
+
bool prepared = false;
|
|
418
|
+
|
|
419
|
+
public:
|
|
420
|
+
AsyncCall(Napi::Env env, InstanceMemory *mem, FunctionInfo *func, bool debug,
|
|
421
|
+
Napi::Function &callback)
|
|
422
|
+
: Napi::AsyncWorker(callback), env(env), func(func->Ref()),
|
|
423
|
+
call(env, func, mem, debug) {}
|
|
424
|
+
~AsyncCall() { func->Unref(); }
|
|
425
|
+
|
|
426
|
+
bool Prepare(const Napi::CallbackInfo &info) {
|
|
427
|
+
prepared = call.Prepare(info);
|
|
428
|
+
|
|
429
|
+
if (!prepared) {
|
|
430
|
+
Napi::Error err = env.GetAndClearPendingException();
|
|
431
|
+
SetError(err.Message());
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return prepared;
|
|
435
|
+
}
|
|
436
|
+
void DumpDebug() { call.DumpDebug(); }
|
|
437
|
+
|
|
438
|
+
void Execute() override;
|
|
439
|
+
void OnOK() override;
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
void AsyncCall::Execute()
|
|
443
|
+
{
|
|
444
|
+
if (prepared) {
|
|
445
|
+
call.Execute();
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
void AsyncCall::OnOK()
|
|
450
|
+
{
|
|
451
|
+
RG_ASSERT(prepared);
|
|
452
|
+
|
|
453
|
+
Napi::FunctionReference &callback = Callback();
|
|
454
|
+
|
|
455
|
+
Napi::Value self = env.Null();
|
|
456
|
+
napi_value args[] = {
|
|
457
|
+
env.Null(),
|
|
458
|
+
call.Complete()
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
callback.Call(self, RG_LEN(args), args);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
static Napi::Value TranslateAsyncCall(const Napi::CallbackInfo &info)
|
|
465
|
+
{
|
|
466
|
+
Napi::Env env = info.Env();
|
|
467
|
+
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
468
|
+
FunctionInfo *func = (FunctionInfo *)info.Data();
|
|
469
|
+
|
|
470
|
+
if (info.Length() <= (uint32_t)func->parameters.len) {
|
|
471
|
+
ThrowError<Napi::TypeError>(env, "Expected %1 arguments, got %2", func->parameters.len + 1, info.Length());
|
|
472
|
+
return env.Null();
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
Napi::Function callback = info[(uint32_t)func->parameters.len].As<Napi::Function>();
|
|
476
|
+
|
|
477
|
+
if (!callback.IsFunction()) {
|
|
478
|
+
ThrowError<Napi::TypeError>(env, "Expected callback function as last arguments, got %1", GetValueType(instance, callback));
|
|
479
|
+
return env.Null();
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
InstanceMemory *mem = AllocateCallMemory(instance);
|
|
483
|
+
AsyncCall *async = new AsyncCall(env, mem, func, instance->debug, callback);
|
|
484
|
+
|
|
485
|
+
if (async->Prepare(info) && instance->debug) {
|
|
486
|
+
async->DumpDebug();
|
|
487
|
+
}
|
|
488
|
+
async->Queue();
|
|
489
|
+
|
|
490
|
+
return env.Null();
|
|
491
|
+
}
|
|
492
|
+
|
|
316
493
|
static bool ParseClassicFunction(Napi::Env env, Napi::String name, Napi::Value ret,
|
|
317
494
|
Napi::Array parameters, FunctionInfo *func)
|
|
318
495
|
{
|
|
@@ -335,6 +512,11 @@ static bool ParseClassicFunction(Napi::Env env, Napi::String name, Napi::Value r
|
|
|
335
512
|
func->ret.type = ResolveType(instance, ret);
|
|
336
513
|
if (!func->ret.type)
|
|
337
514
|
return false;
|
|
515
|
+
if (func->ret.type->primitive == PrimitiveKind::Array) {
|
|
516
|
+
ThrowError<Napi::Error>(env, "You are not allowed to directly return fixed-size arrays");
|
|
517
|
+
return false;
|
|
518
|
+
}
|
|
519
|
+
|
|
338
520
|
if (!parameters.IsArray()) {
|
|
339
521
|
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for parameters of '%2', expected an array", GetValueType(instance, parameters), func->name);
|
|
340
522
|
return false;
|
|
@@ -386,7 +568,7 @@ static Napi::Value FindLibraryFunction(const Napi::CallbackInfo &info, CallConve
|
|
|
386
568
|
LibraryHolder *lib = (LibraryHolder *)info.Data();
|
|
387
569
|
|
|
388
570
|
FunctionInfo *func = new FunctionInfo();
|
|
389
|
-
|
|
571
|
+
RG_DEFER { func->Unref(); };
|
|
390
572
|
|
|
391
573
|
func->lib = lib->Ref();
|
|
392
574
|
func->convention = convention;
|
|
@@ -445,9 +627,14 @@ static Napi::Value FindLibraryFunction(const Napi::CallbackInfo &info, CallConve
|
|
|
445
627
|
}
|
|
446
628
|
|
|
447
629
|
Napi::Function::Callback call = func->variadic ? TranslateVariadicCall : TranslateNormalCall;
|
|
448
|
-
Napi::Function wrapper = Napi::Function::New(env, call, func->name, (void *)func);
|
|
449
|
-
wrapper.AddFinalizer([](Napi::Env, FunctionInfo *func) {
|
|
450
|
-
|
|
630
|
+
Napi::Function wrapper = Napi::Function::New(env, call, func->name, (void *)func->Ref());
|
|
631
|
+
wrapper.AddFinalizer([](Napi::Env, FunctionInfo *func) { func->Unref(); }, func);
|
|
632
|
+
|
|
633
|
+
if (!func->variadic) {
|
|
634
|
+
Napi::Function async = Napi::Function::New(env, TranslateAsyncCall, func->name, (void *)func->Ref());
|
|
635
|
+
async.AddFinalizer([](Napi::Env, FunctionInfo *func) { func->Unref(); }, func);
|
|
636
|
+
wrapper.Set("async", async);
|
|
637
|
+
}
|
|
451
638
|
|
|
452
639
|
return wrapper;
|
|
453
640
|
}
|
|
@@ -540,13 +727,13 @@ LibraryHolder::~LibraryHolder()
|
|
|
540
727
|
#endif
|
|
541
728
|
}
|
|
542
729
|
|
|
543
|
-
LibraryHolder *LibraryHolder::Ref()
|
|
730
|
+
const LibraryHolder *LibraryHolder::Ref() const
|
|
544
731
|
{
|
|
545
732
|
refcount++;
|
|
546
733
|
return this;
|
|
547
734
|
}
|
|
548
735
|
|
|
549
|
-
void LibraryHolder::Unref()
|
|
736
|
+
void LibraryHolder::Unref() const
|
|
550
737
|
{
|
|
551
738
|
if (!--refcount) {
|
|
552
739
|
delete this;
|
|
@@ -556,6 +743,8 @@ void LibraryHolder::Unref()
|
|
|
556
743
|
static void RegisterPrimitiveType(InstanceData *instance, const char *name, PrimitiveKind primitive,
|
|
557
744
|
int16_t size, int16_t align)
|
|
558
745
|
{
|
|
746
|
+
RG_ASSERT(align <= size);
|
|
747
|
+
|
|
559
748
|
TypeInfo *type = instance->types.AppendDefault();
|
|
560
749
|
|
|
561
750
|
type->name = name;
|
|
@@ -646,21 +835,30 @@ FunctionInfo::~FunctionInfo()
|
|
|
646
835
|
}
|
|
647
836
|
}
|
|
648
837
|
|
|
649
|
-
|
|
838
|
+
const FunctionInfo *FunctionInfo::Ref() const
|
|
650
839
|
{
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
uint8_t *aligned = AlignUp(ptr, 16);
|
|
655
|
-
Size delta = AlignLen(aligned - ptr, 16);
|
|
840
|
+
refcount++;
|
|
841
|
+
return this;
|
|
842
|
+
}
|
|
656
843
|
|
|
657
|
-
|
|
844
|
+
void FunctionInfo::Unref() const
|
|
845
|
+
{
|
|
846
|
+
if (!--refcount) {
|
|
847
|
+
delete this;
|
|
848
|
+
}
|
|
658
849
|
}
|
|
659
850
|
|
|
660
851
|
InstanceData::InstanceData()
|
|
661
852
|
{
|
|
662
|
-
|
|
663
|
-
|
|
853
|
+
AllocateCallMemory(this);
|
|
854
|
+
RG_ASSERT(memories.len == 1);
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
InstanceData::~InstanceData()
|
|
858
|
+
{
|
|
859
|
+
for (InstanceMemory *mem: memories) {
|
|
860
|
+
delete mem;
|
|
861
|
+
}
|
|
664
862
|
}
|
|
665
863
|
|
|
666
864
|
template <typename Func>
|
|
@@ -670,6 +868,7 @@ static void SetExports(Napi::Env env, Func func)
|
|
|
670
868
|
func("pack", Napi::Function::New(env, CreatePackedStructType));
|
|
671
869
|
func("handle", Napi::Function::New(env, CreateHandleType));
|
|
672
870
|
func("pointer", Napi::Function::New(env, CreatePointerType));
|
|
871
|
+
func("array", Napi::Function::New(env, CreateArrayType));
|
|
673
872
|
func("load", Napi::Function::New(env, LoadSharedLibrary));
|
|
674
873
|
func("in", Napi::Function::New(env, MarkIn));
|
|
675
874
|
func("out", Napi::Function::New(env, MarkOut));
|
package/src/ffi.hh
CHANGED
|
@@ -39,6 +39,7 @@ enum class PrimitiveKind {
|
|
|
39
39
|
String16,
|
|
40
40
|
Pointer,
|
|
41
41
|
Record,
|
|
42
|
+
Array,
|
|
42
43
|
Float32,
|
|
43
44
|
Float64
|
|
44
45
|
};
|
|
@@ -57,6 +58,7 @@ static const char *const PrimitiveKindNames[] = {
|
|
|
57
58
|
"String16",
|
|
58
59
|
"Pointer",
|
|
59
60
|
"Record",
|
|
61
|
+
"Array",
|
|
60
62
|
"Float32",
|
|
61
63
|
"Float64"
|
|
62
64
|
};
|
|
@@ -73,7 +75,7 @@ struct TypeInfo {
|
|
|
73
75
|
int16_t align;
|
|
74
76
|
|
|
75
77
|
HeapArray<RecordMember> members; // Record only
|
|
76
|
-
const TypeInfo *ref; // Pointer
|
|
78
|
+
const TypeInfo *ref; // Pointer or array
|
|
77
79
|
|
|
78
80
|
RG_HASHTABLE_HANDLER(TypeInfo, name);
|
|
79
81
|
};
|
|
@@ -86,13 +88,13 @@ struct RecordMember {
|
|
|
86
88
|
|
|
87
89
|
struct LibraryHolder {
|
|
88
90
|
void *module = nullptr; // HMODULE on Windows
|
|
89
|
-
std::atomic_int refcount {1};
|
|
91
|
+
mutable std::atomic_int refcount {1};
|
|
90
92
|
|
|
91
93
|
LibraryHolder(void *module) : module(module) {}
|
|
92
94
|
~LibraryHolder();
|
|
93
95
|
|
|
94
|
-
LibraryHolder *Ref();
|
|
95
|
-
void Unref();
|
|
96
|
+
const LibraryHolder *Ref() const;
|
|
97
|
+
void Unref() const;
|
|
96
98
|
};
|
|
97
99
|
|
|
98
100
|
enum class CallConvention {
|
|
@@ -132,11 +134,11 @@ struct ParameterInfo {
|
|
|
132
134
|
};
|
|
133
135
|
|
|
134
136
|
struct FunctionInfo {
|
|
135
|
-
|
|
137
|
+
mutable std::atomic_int refcount {1};
|
|
136
138
|
|
|
137
139
|
const char *name;
|
|
138
140
|
const char *decorated_name;
|
|
139
|
-
LibraryHolder *lib = nullptr;
|
|
141
|
+
const LibraryHolder *lib = nullptr;
|
|
140
142
|
|
|
141
143
|
void *func;
|
|
142
144
|
CallConvention convention;
|
|
@@ -152,10 +154,27 @@ struct FunctionInfo {
|
|
|
152
154
|
#if defined(__arm__) || defined(__aarch64__) || defined(__x86_64__) || defined(_WIN64)
|
|
153
155
|
bool forward_fp;
|
|
154
156
|
#endif
|
|
157
|
+
|
|
158
|
+
~FunctionInfo();
|
|
159
|
+
|
|
160
|
+
const FunctionInfo *Ref() const;
|
|
161
|
+
void Unref() const;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
struct InstanceMemory {
|
|
165
|
+
LinkedAllocator mem_alloc;
|
|
166
|
+
|
|
167
|
+
Span<uint8_t> stack;
|
|
168
|
+
Span<uint8_t> heap;
|
|
169
|
+
IndirectBlockAllocator big_alloc { &mem_alloc };
|
|
170
|
+
|
|
171
|
+
int depth;
|
|
172
|
+
bool temporary;
|
|
155
173
|
};
|
|
156
174
|
|
|
157
175
|
struct InstanceData {
|
|
158
176
|
InstanceData();
|
|
177
|
+
~InstanceData();
|
|
159
178
|
|
|
160
179
|
BucketArray<TypeInfo> types;
|
|
161
180
|
HashTable<const char *, TypeInfo *> types_map;
|
|
@@ -163,12 +182,9 @@ struct InstanceData {
|
|
|
163
182
|
bool debug;
|
|
164
183
|
uint64_t tag_lower;
|
|
165
184
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
Span<uint8_t> stack_mem;
|
|
169
|
-
Span<uint8_t> heap_mem;
|
|
185
|
+
LocalArray<InstanceMemory *, 8> memories;
|
|
170
186
|
|
|
171
|
-
|
|
187
|
+
BlockAllocator str_alloc;
|
|
172
188
|
};
|
|
173
189
|
|
|
174
190
|
}
|
package/src/parser.cc
CHANGED
|
@@ -35,6 +35,10 @@ bool PrototypeParser::Parse(Napi::String proto, FunctionInfo *func)
|
|
|
35
35
|
Tokenize(hold.c_str());
|
|
36
36
|
|
|
37
37
|
func->ret.type = ParseType();
|
|
38
|
+
if (func->ret.type->primitive == PrimitiveKind::Array) {
|
|
39
|
+
MarkError("You are not allowed to directly return fixed-size arrays");
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
38
42
|
if (Match("__cdecl")) {
|
|
39
43
|
func->convention = CallConvention::Cdecl;
|
|
40
44
|
} else if (Match("__stdcall")) {
|
package/src/util.cc
CHANGED
|
@@ -126,4 +126,76 @@ bool CheckValueTag(const InstanceData *instance, Napi::Value value, const void *
|
|
|
126
126
|
return match;
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
class HfaAnalyser {
|
|
130
|
+
uint32_t primitives;
|
|
131
|
+
Size count;
|
|
132
|
+
|
|
133
|
+
public:
|
|
134
|
+
bool Analyse(const TypeInfo *type, int min, int max);
|
|
135
|
+
|
|
136
|
+
private:
|
|
137
|
+
void AnalyseStruct(const TypeInfo *type);
|
|
138
|
+
void AnalyseArray(const TypeInfo *type);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
bool HfaAnalyser::Analyse(const TypeInfo *type, int min, int max)
|
|
142
|
+
{
|
|
143
|
+
primitives = 0;
|
|
144
|
+
count = 0;
|
|
145
|
+
|
|
146
|
+
if (type->primitive == PrimitiveKind::Record) {
|
|
147
|
+
AnalyseStruct(type);
|
|
148
|
+
} else if (type->primitive == PrimitiveKind::Array) {
|
|
149
|
+
AnalyseArray(type);
|
|
150
|
+
} else {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
bool hfa = (count >= min && count <= max && PopCount(primitives) == 1);
|
|
155
|
+
return hfa;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
void HfaAnalyser::AnalyseStruct(const TypeInfo *type)
|
|
159
|
+
{
|
|
160
|
+
RG_ASSERT(type->primitive == PrimitiveKind::Record);
|
|
161
|
+
|
|
162
|
+
for (const RecordMember &member: type->members) {
|
|
163
|
+
if (member.type->primitive == PrimitiveKind::Record) {
|
|
164
|
+
AnalyseStruct(member.type);
|
|
165
|
+
} else if (member.type->primitive == PrimitiveKind::Array) {
|
|
166
|
+
AnalyseArray(member.type);
|
|
167
|
+
} else if (member.type->primitive == PrimitiveKind::Float32 ||
|
|
168
|
+
member.type->primitive == PrimitiveKind::Float64) {
|
|
169
|
+
primitives |= 1u << (int)member.type->primitive;
|
|
170
|
+
count++;
|
|
171
|
+
} else {
|
|
172
|
+
primitives = UINT_MAX;
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
void HfaAnalyser::AnalyseArray(const TypeInfo *type)
|
|
179
|
+
{
|
|
180
|
+
RG_ASSERT(type->primitive == PrimitiveKind::Array);
|
|
181
|
+
|
|
182
|
+
if (type->ref->primitive == PrimitiveKind::Record) {
|
|
183
|
+
AnalyseStruct(type->ref);
|
|
184
|
+
} else if (type->ref->primitive == PrimitiveKind::Array) {
|
|
185
|
+
AnalyseArray(type->ref);
|
|
186
|
+
} else if (type->ref->primitive == PrimitiveKind::Float32 ||
|
|
187
|
+
type->ref->primitive == PrimitiveKind::Float64) {
|
|
188
|
+
primitives |= 1u << (int)type->ref->primitive;
|
|
189
|
+
count += type->size / type->ref->size;;
|
|
190
|
+
} else {
|
|
191
|
+
primitives = UINT_MAX;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
bool IsHFA(const TypeInfo *type, int min, int max)
|
|
196
|
+
{
|
|
197
|
+
HfaAnalyser analyser;
|
|
198
|
+
return analyser.Analyse(type, min, max);
|
|
199
|
+
}
|
|
200
|
+
|
|
129
201
|
}
|