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.
Files changed (39) hide show
  1. package/CMakeLists.txt +4 -0
  2. package/README.md +54 -24
  3. package/build/qemu/1.1.0-beta.2/koffi_darwin_x64.tar.gz +0 -0
  4. package/build/qemu/1.1.0-beta.2/koffi_freebsd_arm64.tar.gz +0 -0
  5. package/build/qemu/1.1.0-beta.2/koffi_freebsd_ia32.tar.gz +0 -0
  6. package/build/qemu/1.1.0-beta.2/koffi_freebsd_x64.tar.gz +0 -0
  7. package/build/qemu/1.1.0-beta.2/koffi_linux_arm.tar.gz +0 -0
  8. package/build/qemu/1.1.0-beta.2/koffi_linux_arm64.tar.gz +0 -0
  9. package/build/qemu/1.1.0-beta.2/koffi_linux_ia32.tar.gz +0 -0
  10. package/build/qemu/1.1.0-beta.2/koffi_linux_x64.tar.gz +0 -0
  11. package/build/qemu/1.1.0-beta.2/koffi_win32_ia32.tar.gz +0 -0
  12. package/build/qemu/1.1.0-beta.2/koffi_win32_x64.tar.gz +0 -0
  13. package/package.json +1 -1
  14. package/qemu/qemu.js +12 -5
  15. package/qemu/registry/machines.json +20 -10
  16. package/src/abi_arm32.cc +40 -51
  17. package/src/abi_arm64.cc +71 -138
  18. package/src/abi_x64_sysv.cc +37 -13
  19. package/src/abi_x64_win.cc +16 -6
  20. package/src/abi_x86.cc +16 -6
  21. package/src/call.cc +564 -58
  22. package/src/call.hh +32 -44
  23. package/src/ffi.cc +218 -19
  24. package/src/ffi.hh +27 -11
  25. package/src/parser.cc +4 -0
  26. package/src/util.cc +72 -0
  27. package/src/util.hh +2 -0
  28. package/test/misc.c +16 -10
  29. package/build/qemu/1.0.5/koffi_darwin_x64.tar.gz +0 -0
  30. package/build/qemu/1.0.5/koffi_freebsd_arm64.tar.gz +0 -0
  31. package/build/qemu/1.0.5/koffi_freebsd_ia32.tar.gz +0 -0
  32. package/build/qemu/1.0.5/koffi_freebsd_x64.tar.gz +0 -0
  33. package/build/qemu/1.0.5/koffi_linux_arm.tar.gz +0 -0
  34. package/build/qemu/1.0.5/koffi_linux_arm64.tar.gz +0 -0
  35. package/build/qemu/1.0.5/koffi_linux_ia32.tar.gz +0 -0
  36. package/build/qemu/1.0.5/koffi_linux_x64.tar.gz +0 -0
  37. package/build/qemu/1.0.5/koffi_win32_ia32.tar.gz +0 -0
  38. package/build/qemu/1.0.5/koffi_win32_x64.tar.gz +0 -0
  39. 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::Object obj;
28
+ Napi::ObjectReference ref;
33
29
  const uint8_t *ptr;
34
30
  const TypeInfo *type;
35
31
  };
36
32
 
37
- Span<uint8_t> *stack_mem;
38
- Span<uint8_t> *heap_mem;
39
- LocalArray<OutObject, MaxOutParameters> out_objects;
40
- BlockAllocator big_alloc;
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, InstanceData *instance, const FunctionInfo *func);
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 (instance->debug) {
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 *ptr, const TypeInfo *type);
106
- Napi::Object PopObject(const uint8_t *ptr, const TypeInfo *type);
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(stack_mem->end() - size, align);
113
- Size delta = stack_mem->end() - ptr;
100
+ uint8_t *ptr = AlignDown(mem->stack.end() - size, align);
101
+ Size delta = mem->stack.end() - ptr;
114
102
 
115
- if (RG_UNLIKELY(stack_mem->len < delta)) {
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 (instance->debug) {
108
+ if (debug) {
121
109
  memset(ptr, 0, delta);
122
110
  }
123
111
 
124
- stack_mem->len -= delta;
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(heap_mem->ptr, align);
136
- Size delta = size + (ptr - heap_mem->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 > heap_mem->len)) {
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 (instance->debug) {
144
- memset(heap_mem->ptr, 0, (size_t)delta);
131
+ if (debug) {
132
+ memset(mem->heap.ptr, 0, (size_t)delta);
145
133
  }
146
134
 
147
- heap_mem->ptr += delta;
148
- heap_mem->len -= delta;
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
- CallData call(env, instance, func);
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], &param.directions);
287
380
  if (!param.type)
288
381
  return env.Null();
289
- if (param.type->primitive == PrimitiveKind::Void) {
290
- ThrowError<Napi::TypeError>(env, "Type void cannot be used as a parameter");
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
- CallData call(env, instance, &func);
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
- RG_DEFER_N(func_guard) { delete func; };
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) { delete func; }, func);
450
- func_guard.Disable();
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
- static Span<uint8_t> AllocateAndAlign16(Allocator *alloc, Size size)
838
+ const FunctionInfo *FunctionInfo::Ref() const
650
839
  {
651
- RG_ASSERT(AlignLen(size, 16) == size);
652
-
653
- uint8_t *ptr = (uint8_t *)Allocator::Allocate(alloc, size);
654
- uint8_t *aligned = AlignUp(ptr, 16);
655
- Size delta = AlignLen(aligned - ptr, 16);
840
+ refcount++;
841
+ return this;
842
+ }
656
843
 
657
- return MakeSpan(aligned, size - delta);
844
+ void FunctionInfo::Unref() const
845
+ {
846
+ if (!--refcount) {
847
+ delete this;
848
+ }
658
849
  }
659
850
 
660
851
  InstanceData::InstanceData()
661
852
  {
662
- stack_mem = AllocateAndAlign16(&mem_alloc, Mebibytes(2));
663
- heap_mem = AllocateAndAlign16(&mem_alloc, Mebibytes(4));
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 only
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
- ~FunctionInfo();
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
- BlockAllocator str_alloc;
167
-
168
- Span<uint8_t> stack_mem;
169
- Span<uint8_t> heap_mem;
185
+ LocalArray<InstanceMemory *, 8> memories;
170
186
 
171
- LinkedAllocator mem_alloc;
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
  }
package/src/util.hh CHANGED
@@ -89,4 +89,6 @@ T CopyNumber(const Napi::Value &value)
89
89
  RG_UNREACHABLE();
90
90
  }
91
91
 
92
+ bool IsHFA(const TypeInfo *type, int min, int max);
93
+
92
94
  }