koffi 2.2.2-beta.5 → 2.2.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 (60) hide show
  1. package/ChangeLog.md +14 -0
  2. package/doc/callbacks.md +10 -3
  3. package/doc/contribute.md +1 -1
  4. package/package.json +2 -2
  5. package/src/koffi/build/2.2.2/koffi_darwin_arm64.tar.gz +0 -0
  6. package/src/koffi/build/2.2.2/koffi_darwin_x64.tar.gz +0 -0
  7. package/src/koffi/build/2.2.2/koffi_freebsd_arm64.tar.gz +0 -0
  8. package/src/koffi/build/2.2.2/koffi_freebsd_ia32.tar.gz +0 -0
  9. package/src/koffi/build/2.2.2/koffi_freebsd_x64.tar.gz +0 -0
  10. package/src/koffi/build/2.2.2/koffi_linux_arm32hf.tar.gz +0 -0
  11. package/src/koffi/build/2.2.2/koffi_linux_arm64.tar.gz +0 -0
  12. package/src/koffi/build/2.2.2/koffi_linux_ia32.tar.gz +0 -0
  13. package/src/koffi/build/2.2.2/koffi_linux_riscv64hf64.tar.gz +0 -0
  14. package/src/koffi/build/2.2.2/koffi_linux_x64.tar.gz +0 -0
  15. package/src/koffi/build/2.2.2/koffi_openbsd_ia32.tar.gz +0 -0
  16. package/src/koffi/build/2.2.2/koffi_openbsd_x64.tar.gz +0 -0
  17. package/src/koffi/build/2.2.2/koffi_win32_arm64.tar.gz +0 -0
  18. package/src/koffi/build/2.2.2/koffi_win32_ia32.tar.gz +0 -0
  19. package/src/koffi/build/2.2.2/koffi_win32_x64.tar.gz +0 -0
  20. package/src/koffi/src/abi_arm32.cc +3 -70
  21. package/src/koffi/src/abi_arm32_fwd.S +6054 -97
  22. package/src/koffi/src/abi_arm64.cc +3 -70
  23. package/src/koffi/src/abi_arm64_fwd.S +6050 -93
  24. package/src/koffi/src/abi_arm64_fwd.asm +8082 -141
  25. package/src/koffi/src/abi_riscv64.cc +3 -70
  26. package/src/koffi/src/abi_riscv64_fwd.S +6044 -87
  27. package/src/koffi/src/abi_trampolines.inc +2065 -0
  28. package/src/koffi/src/abi_x64_sysv.cc +3 -70
  29. package/src/koffi/src/abi_x64_sysv_fwd.S +6048 -91
  30. package/src/koffi/src/abi_x64_win.cc +4 -71
  31. package/src/koffi/src/abi_x64_win_fwd.asm +8018 -77
  32. package/src/koffi/src/abi_x86.cc +4 -71
  33. package/src/koffi/src/abi_x86_fwd.S +6056 -99
  34. package/src/koffi/src/abi_x86_fwd.asm +8027 -86
  35. package/src/koffi/src/call.cc +23 -16
  36. package/src/koffi/src/call.hh +2 -4
  37. package/src/koffi/src/ffi.cc +35 -33
  38. package/src/koffi/src/ffi.hh +16 -5
  39. package/src/koffi/test/callbacks.js +11 -10
  40. package/src/koffi/test/misc.c +30 -17
  41. package/src/koffi/{qemu → tools}/qemu/.gitkeep +0 -0
  42. package/src/koffi/{qemu → tools}/qemu.js +4 -1
  43. package/src/koffi/{qemu → tools}/registry/machines.json +0 -0
  44. package/src/koffi/{qemu → tools}/registry/sha256sum.txt +0 -0
  45. package/src/koffi/tools/write_trampolines.py +138 -0
  46. package/src/koffi/build/2.2.2-beta.5/koffi_darwin_arm64.tar.gz +0 -0
  47. package/src/koffi/build/2.2.2-beta.5/koffi_darwin_x64.tar.gz +0 -0
  48. package/src/koffi/build/2.2.2-beta.5/koffi_freebsd_arm64.tar.gz +0 -0
  49. package/src/koffi/build/2.2.2-beta.5/koffi_freebsd_ia32.tar.gz +0 -0
  50. package/src/koffi/build/2.2.2-beta.5/koffi_freebsd_x64.tar.gz +0 -0
  51. package/src/koffi/build/2.2.2-beta.5/koffi_linux_arm32hf.tar.gz +0 -0
  52. package/src/koffi/build/2.2.2-beta.5/koffi_linux_arm64.tar.gz +0 -0
  53. package/src/koffi/build/2.2.2-beta.5/koffi_linux_ia32.tar.gz +0 -0
  54. package/src/koffi/build/2.2.2-beta.5/koffi_linux_riscv64hf64.tar.gz +0 -0
  55. package/src/koffi/build/2.2.2-beta.5/koffi_linux_x64.tar.gz +0 -0
  56. package/src/koffi/build/2.2.2-beta.5/koffi_openbsd_ia32.tar.gz +0 -0
  57. package/src/koffi/build/2.2.2-beta.5/koffi_openbsd_x64.tar.gz +0 -0
  58. package/src/koffi/build/2.2.2-beta.5/koffi_win32_arm64.tar.gz +0 -0
  59. package/src/koffi/build/2.2.2-beta.5/koffi_win32_ia32.tar.gz +0 -0
  60. package/src/koffi/build/2.2.2-beta.5/koffi_win32_x64.tar.gz +0 -0
@@ -53,19 +53,20 @@ CallData::~CallData()
53
53
  mem->stack = old_stack_mem;
54
54
  mem->heap = old_heap_mem;
55
55
 
56
- if (used_trampolines) {
56
+ if (used_trampolines.len) {
57
57
  std::lock_guard<std::mutex> lock(shared.mutex);
58
58
 
59
- for (Size i = 0; i < MaxTrampolines; i++) {
60
- if (used_trampolines & (1u << i)) {
61
- TrampolineInfo *trampoline = &shared.trampolines[i];
59
+ for (Size i = used_trampolines.len - 1; i >= 0; i--) {
60
+ int16_t idx = used_trampolines[i];
61
+ TrampolineInfo *trampoline = &shared.trampolines[idx];
62
62
 
63
- trampoline->func.Reset();
64
- trampoline->recv.Reset();
65
- }
66
- }
63
+ RG_ASSERT(!trampoline->func.IsEmpty());
64
+
65
+ trampoline->func.Reset();
66
+ trampoline->recv.Reset();
67
67
 
68
- shared.temp_trampolines ^= used_trampolines;
68
+ shared.available.Append(idx);
69
+ }
69
70
  }
70
71
 
71
72
  instance->temporaries -= mem->temporary;
@@ -80,6 +81,9 @@ CallData::~CallData()
80
81
  void CallData::RelaySafe(Size idx, uint8_t *own_sp, uint8_t *caller_sp, BackRegisters *out_reg)
81
82
  {
82
83
  if (std::this_thread::get_id() != instance->main_thread_id) {
84
+ // JS/V8 is single-threaded, and runs on main_thread_id. Forward the call
85
+ // to the JS event loop.
86
+
83
87
  RelayContext ctx;
84
88
 
85
89
  ctx.call = this;
@@ -1025,19 +1029,21 @@ void CallData::PopOutArguments()
1025
1029
 
1026
1030
  void *CallData::ReserveTrampoline(const FunctionInfo *proto, Napi::Function func)
1027
1031
  {
1028
- int idx;
1032
+ int16_t idx;
1029
1033
  {
1030
1034
  std::lock_guard<std::mutex> lock(shared.mutex);
1031
1035
 
1032
- idx = CountTrailingZeros(~shared.temp_trampolines);
1033
-
1034
- if (RG_UNLIKELY(idx >= MaxTrampolines)) {
1035
- ThrowError<Napi::Error>(env, "Too many temporary callbacks are in use (max = %1)", MaxTrampolines);
1036
+ if (RG_UNLIKELY(!shared.available.len)) {
1037
+ ThrowError<Napi::Error>(env, "Too many callbacks are in use (max = %1)", MaxTrampolines);
1038
+ return env.Null();
1039
+ }
1040
+ if (RG_UNLIKELY(!used_trampolines.Available())) {
1041
+ ThrowError<Napi::Error>(env, "This call uses too many temporary callbacks (max = %1)", RG_LEN(used_trampolines.data));
1036
1042
  return env.Null();
1037
1043
  }
1038
1044
 
1039
- shared.temp_trampolines |= 1u << idx;
1040
- used_trampolines |= 1u << idx;
1045
+ idx = shared.available.data[--shared.available.len];
1046
+ used_trampolines.Append(idx);
1041
1047
  }
1042
1048
 
1043
1049
  TrampolineInfo *trampoline = &shared.trampolines[idx];
@@ -1048,6 +1054,7 @@ void *CallData::ReserveTrampoline(const FunctionInfo *proto, Napi::Function func
1048
1054
  trampoline->generation = (int32_t)mem->generation;
1049
1055
 
1050
1056
  void *ptr = GetTrampoline(idx, proto);
1057
+
1051
1058
  return ptr;
1052
1059
  }
1053
1060
 
@@ -41,8 +41,7 @@ class alignas(8) CallData {
41
41
  Span<uint8_t> old_stack_mem;
42
42
  Span<uint8_t> old_heap_mem;
43
43
 
44
- uint32_t used_trampolines = 0;
45
-
44
+ LocalArray<int16_t, 16> used_trampolines;
46
45
  LocalArray<OutArgument, MaxOutParameters> out_arguments;
47
46
 
48
47
  uint8_t *new_sp;
@@ -112,7 +111,6 @@ private:
112
111
 
113
112
  void *ReserveTrampoline(const FunctionInfo *proto, Napi::Function func);
114
113
  };
115
- RG_STATIC_ASSERT(MaxTrampolines <= 32);
116
114
 
117
115
  template <typename T>
118
116
  inline bool CallData::AllocStack(Size size, Size align, T **out_ptr)
@@ -165,6 +163,6 @@ inline T *CallData::AllocHeap(Size size, Size align)
165
163
  }
166
164
  }
167
165
 
168
- void *GetTrampoline(Size idx, const FunctionInfo *proto);
166
+ void *GetTrampoline(int16_t idx, const FunctionInfo *proto);
169
167
 
170
168
  }
@@ -1001,6 +1001,8 @@ static Napi::Value GetTypeDefinition(const Napi::CallbackInfo &info)
1001
1001
 
1002
1002
  static InstanceMemory *AllocateMemory(InstanceData *instance, Size stack_size, Size heap_size)
1003
1003
  {
1004
+ std::lock_guard<std::mutex> lock(instance->memories_mutex);
1005
+
1004
1006
  for (Size i = 1; i < instance->memories.len; i++) {
1005
1007
  InstanceMemory *mem = instance->memories[i];
1006
1008
 
@@ -1063,9 +1065,6 @@ static Napi::Value TranslateNormalCall(const Napi::CallbackInfo &info)
1063
1065
  InstanceMemory *mem = instance->memories[0];
1064
1066
  CallData call(env, instance, mem);
1065
1067
 
1066
- RG_DEFER_C(prev_call = exec_call) { exec_call = prev_call; };
1067
- exec_call = &call;
1068
-
1069
1068
  if (!RG_UNLIKELY(call.Prepare(func, info)))
1070
1069
  return env.Null();
1071
1070
 
@@ -1111,7 +1110,7 @@ static Napi::Value TranslateVariadicCall(const Napi::CallbackInfo &info)
1111
1110
  for (Size i = func.parameters.len; i < (Size)info.Length(); i += 2) {
1112
1111
  ParameterInfo param = {};
1113
1112
 
1114
- param.type = ResolveType(info[i], &param.directions);
1113
+ param.type = ResolveType(info[(uint32_t)i], &param.directions);
1115
1114
 
1116
1115
  if (RG_UNLIKELY(!param.type))
1117
1116
  return env.Null();
@@ -1436,19 +1435,16 @@ static Napi::Value RegisterCallback(const Napi::CallbackInfo &info)
1436
1435
  return env.Null();
1437
1436
  }
1438
1437
 
1439
- int idx;
1438
+ int16_t idx;
1440
1439
  {
1441
1440
  std::lock_guard<std::mutex> lock(shared.mutex);
1442
1441
 
1443
- idx = CountTrailingZeros(~shared.registered_trampolines);
1444
-
1445
- if (RG_UNLIKELY(idx >= MaxTrampolines)) {
1446
- ThrowError<Napi::Error>(env, "Too many registered callbacks are in use (max = %1)", MaxTrampolines);
1442
+ if (RG_UNLIKELY(!shared.available.len)) {
1443
+ ThrowError<Napi::Error>(env, "Too many callbacks are in use (max = %1)", MaxTrampolines);
1447
1444
  return env.Null();
1448
1445
  }
1449
1446
 
1450
- shared.registered_trampolines |= 1u << idx;
1451
- idx += MaxTrampolines;
1447
+ idx = shared.available.data[--shared.available.len];
1452
1448
  }
1453
1449
 
1454
1450
  TrampolineInfo *trampoline = &shared.trampolines[idx];
@@ -1467,6 +1463,9 @@ static Napi::Value RegisterCallback(const Napi::CallbackInfo &info)
1467
1463
  Napi::External<void> external = Napi::External<void>::New(env, ptr);
1468
1464
  SetValueTag(instance, external, type->ref.marker);
1469
1465
 
1466
+ // Cache index for fast unregistration
1467
+ instance->trampolines_map.Set(ptr, idx);
1468
+
1470
1469
  return external;
1471
1470
  }
1472
1471
 
@@ -1487,31 +1486,33 @@ static Napi::Value UnregisterCallback(const Napi::CallbackInfo &info)
1487
1486
  Napi::External<void> external = info[0].As<Napi::External<void>>();
1488
1487
  void *ptr = external.Data();
1489
1488
 
1490
- // Release shared trampoline safely
1489
+ int16_t idx;
1491
1490
  {
1492
- std::lock_guard<std::mutex> lock(shared.mutex);
1491
+ int16_t *it = instance->trampolines_map.Find(ptr);
1493
1492
 
1494
- for (Size i = 0; i < MaxTrampolines; i++) {
1495
- Size idx = i + MaxTrampolines;
1493
+ if (RG_UNLIKELY(!it)) {
1494
+ ThrowError<Napi::Error>(env, "Could not find matching registered callback");
1495
+ return env.Null();
1496
+ }
1496
1497
 
1497
- if (!(shared.registered_trampolines & (1u << i)))
1498
- continue;
1498
+ idx = *it;
1499
+ instance->trampolines_map.Remove(it);
1500
+ }
1499
1501
 
1500
- TrampolineInfo *trampoline = &shared.trampolines[idx];
1502
+ // Release shared trampoline safely
1503
+ {
1504
+ std::lock_guard<std::mutex> lock(shared.mutex);
1501
1505
 
1502
- if (GetTrampoline(idx, trampoline->proto) == ptr) {
1503
- shared.registered_trampolines &= ~(1u << i);
1506
+ TrampolineInfo *trampoline = &shared.trampolines[idx];
1507
+ RG_ASSERT(!trampoline->func.IsEmpty());
1504
1508
 
1505
- trampoline->func.Reset();
1506
- trampoline->recv.Reset();
1509
+ trampoline->func.Reset();
1510
+ trampoline->recv.Reset();
1507
1511
 
1508
- return env.Undefined();
1509
- }
1510
- }
1512
+ shared.available.Append(idx);
1511
1513
  }
1512
1514
 
1513
- ThrowError<Napi::Error>(env, "Could not find matching registered callback");
1514
- return env.Null();
1515
+ return env.Undefined();
1515
1516
  }
1516
1517
 
1517
1518
  LibraryHolder::~LibraryHolder()
@@ -1709,18 +1710,16 @@ InstanceData::~InstanceData()
1709
1710
  delete mem;
1710
1711
  }
1711
1712
 
1712
- // Clean-up leftover registered trampolines
1713
+ // Clean-up leftover trampoline references
1713
1714
  {
1714
1715
  std::lock_guard<std::mutex> lock(shared.mutex);
1715
1716
 
1716
- for (Size i = 0; i < MaxTrampolines; i++) {
1717
- Size idx = i + MaxTrampolines;
1717
+ for (int16_t idx = 0; idx < MaxTrampolines; idx++) {
1718
+ TrampolineInfo *trampoline = &shared.trampolines[idx];
1718
1719
 
1719
- if (!(shared.registered_trampolines & (1u << i)))
1720
+ if (trampoline->func.IsEmpty())
1720
1721
  continue;
1721
1722
 
1722
- TrampolineInfo *trampoline = &shared.trampolines[idx];
1723
-
1724
1723
  if (trampoline->func.Env().GetInstanceData<InstanceData>() == this) {
1725
1724
  trampoline->func.Reset();
1726
1725
  trampoline->recv.Reset();
@@ -1891,6 +1890,9 @@ extern "C" void RelayCallback(Size idx, uint8_t *own_sp, uint8_t *caller_sp, Bac
1891
1890
  if (RG_LIKELY(exec_call)) {
1892
1891
  exec_call->RelaySafe(idx, own_sp, caller_sp, out_reg);
1893
1892
  } else {
1893
+ // This happens if the callback pointer is called from a different thread
1894
+ // than the one that runs the FFI call (sync or async).
1895
+
1894
1896
  TrampolineInfo *trampoline = &shared.trampolines[idx];
1895
1897
 
1896
1898
  Napi::Env env = trampoline->func.Env();
@@ -30,7 +30,7 @@ static const Size DefaultMaxTypeSize = Mebibytes(64);
30
30
  static const int MaxAsyncCalls = 256;
31
31
  static const Size MaxParameters = 32;
32
32
  static const Size MaxOutParameters = 16;
33
- static const Size MaxTrampolines = 16;
33
+ static const Size MaxTrampolines = 1024;
34
34
 
35
35
  extern const int TypeInfoMarker;
36
36
  extern const int CastMarker;
@@ -247,12 +247,15 @@ struct InstanceData {
247
247
  const TypeInfo *char_type;
248
248
  const TypeInfo *char16_type;
249
249
 
250
+ std::mutex memories_mutex;
250
251
  LocalArray<InstanceMemory *, 9> memories;
251
252
  int temporaries = 0;
252
253
 
253
254
  std::thread::id main_thread_id;
254
255
  napi_threadsafe_function broker = nullptr;
255
256
 
257
+ HashMap<void *, int16_t> trampolines_map;
258
+
256
259
  BlockAllocator str_alloc;
257
260
 
258
261
  Size sync_stack_size = DefaultSyncStackSize;
@@ -266,7 +269,6 @@ struct InstanceData {
266
269
  RG_STATIC_ASSERT(DefaultResidentAsyncPools <= RG_LEN(InstanceData::memories.data) - 1);
267
270
  RG_STATIC_ASSERT(DefaultMaxAsyncCalls >= DefaultResidentAsyncPools);
268
271
  RG_STATIC_ASSERT(MaxAsyncCalls >= DefaultMaxAsyncCalls);
269
- RG_STATIC_ASSERT(MaxTrampolines <= 16);
270
272
 
271
273
  struct TrampolineInfo {
272
274
  const FunctionInfo *proto;
@@ -279,10 +281,19 @@ struct TrampolineInfo {
279
281
  struct SharedData {
280
282
  std::mutex mutex;
281
283
 
282
- TrampolineInfo trampolines[MaxTrampolines * 2];
283
- uint32_t temp_trampolines = 0;
284
- uint32_t registered_trampolines = 0;
284
+ TrampolineInfo trampolines[MaxTrampolines];
285
+ LocalArray<int16_t, MaxTrampolines> available;
286
+
287
+ SharedData()
288
+ {
289
+ available.len = MaxTrampolines;
290
+
291
+ for (int16_t i = 0; i < MaxTrampolines; i++) {
292
+ available[i] = i;
293
+ }
294
+ }
285
295
  };
296
+ RG_STATIC_ASSERT(MaxTrampolines <= INT16_MAX);
286
297
 
287
298
  extern SharedData shared;
288
299
 
@@ -75,8 +75,7 @@ async function test() {
75
75
  const ApplyStruct = lib.func('int ApplyStruct(int x, StructCallbacks callbacks)');
76
76
  const SetIndirect = lib.func('void SetIndirect(IntCallback *func)');
77
77
  const CallIndirect = lib.func('int CallIndirect(int x)');
78
- const CallIndirectThreaded = lib.func('int CallIndirectThreaded(int x)');
79
- const CallDirectThreaded = lib.func('int CallDirectThreaded(IntCallback *func, int x)');
78
+ const CallThreaded = lib.func('int CallThreaded(IntCallback *func, int x)');
80
79
  const MakeVectors = lib.func('int MakeVectors(int len, VectorCallback *func)');
81
80
  const CallQSort = lib.func('void CallQSort(_Inout_ void *base, size_t nmemb, size_t size, SortCallback *cb)');
82
81
  const CallMeChar = lib.func('int CallMeChar(CharCallback *func)');
@@ -263,16 +262,18 @@ async function test() {
263
262
  assert.equal(ret, 97 + 2 * 98);
264
263
  }
265
264
 
266
- // Use temporary callback from secondary thread
267
- for (let i = 0; i < 128; i++)
268
- assert.equal(await util.promisify(CallDirectThreaded.async)(x => -x - 2, 27), -29);
269
-
270
- // Use registered callback from secondary thread
271
- for (let i = 0; i < 128; i++) {
265
+ // Use callbacks from secondary threads
266
+ for (let i = 0; i < 1024; i++) {
272
267
  let cb = koffi.register(x => -x - 2, koffi.pointer(IntCallback));
273
-
274
268
  SetIndirect(cb);
275
- assert.equal(await util.promisify(CallIndirectThreaded.async)(27), -29);
269
+
270
+ let results = await Promise.all([
271
+ util.promisify(CallThreaded.async)(x => -x - 4, 27),
272
+ util.promisify(CallThreaded.async)(null, 27)
273
+ ]);
274
+
275
+ assert.equal(results[0], -31);
276
+ assert.equal(results[1], -29);
276
277
 
277
278
  koffi.unregister(cb);
278
279
  }
@@ -681,17 +681,27 @@ EXPORT int CallIndirect(int x)
681
681
 
682
682
  #ifdef _WIN32
683
683
 
684
- static DWORD WINAPI CallIndirectThreadedFunc(void *udata)
684
+ typedef struct CallContext {
685
+ IntCallback *callback;
686
+ int *ptr;
687
+ } CallContext;
688
+
689
+ static DWORD WINAPI CallThreadedFunc(void *udata)
685
690
  {
686
- int *ptr = (int *)udata;
687
- *ptr = callback(*ptr);
691
+ CallContext *ctx = (CallContext *)udata;
692
+ *ctx->ptr = ctx->callback(*ctx->ptr);
688
693
 
689
694
  return 0;
690
695
  }
691
696
 
692
- EXPORT int CallIndirectThreaded(int x)
697
+ EXPORT int CallThreaded(IntCallback *func, int x)
693
698
  {
694
- HANDLE h = CreateThread(NULL, 0, CallIndirectThreadedFunc, &x, 0, NULL);
699
+ CallContext ctx;
700
+
701
+ ctx.callback = func ? func : callback;
702
+ ctx.ptr = &x;
703
+
704
+ HANDLE h = CreateThread(NULL, 0, CallThreadedFunc, &ctx, 0, NULL);
695
705
  if (!h) {
696
706
  perror("CreateThread");
697
707
  exit(1);
@@ -705,19 +715,28 @@ EXPORT int CallIndirectThreaded(int x)
705
715
 
706
716
  #else
707
717
 
708
- static void *CallIndirectThreadedFunc(void *udata)
718
+ typedef struct CallContext {
719
+ IntCallback *callback;
720
+ int *ptr;
721
+ } CallContext;
722
+
723
+ static void *CallThreadedFunc(void *udata)
709
724
  {
710
- int *ptr = (int *)udata;
711
- *ptr = callback(*ptr);
725
+ CallContext *ctx = (CallContext *)udata;
726
+ *ctx->ptr = ctx->callback(*ctx->ptr);
712
727
 
713
728
  return NULL;
714
729
  }
715
730
 
716
- EXPORT int CallIndirectThreaded(int x)
731
+ EXPORT int CallThreaded(IntCallback *func, int x)
717
732
  {
718
- pthread_t thread;
733
+ CallContext ctx;
719
734
 
720
- if (pthread_create(&thread, NULL, CallIndirectThreadedFunc, &x)) {
735
+ ctx.callback = func ? func : callback;
736
+ ctx.ptr = &x;
737
+
738
+ pthread_t thread;
739
+ if (pthread_create(&thread, NULL, CallThreadedFunc, &ctx)) {
721
740
  perror("pthread_create");
722
741
  exit(1);
723
742
  }
@@ -729,12 +748,6 @@ EXPORT int CallIndirectThreaded(int x)
729
748
 
730
749
  #endif
731
750
 
732
- EXPORT int CallDirectThreaded(IntCallback *func, int x)
733
- {
734
- callback = func;
735
- return CallIndirectThreaded(x);
736
- }
737
-
738
751
  EXPORT void ReverseBytes(void *p, int len)
739
752
  {
740
753
  uint8_t *bytes = (uint8_t *)p;
File without changes
@@ -342,7 +342,7 @@ async function prepare() {
342
342
  log(machine, `${suite} > Status`, chalk.bold.red(`[manual]`));
343
343
  needed = true;
344
344
  } else {
345
- log(machine, `${suite} > Status`, chalk.bold.red(`[build]`));
345
+ log(machine, `${suite} > Status`, chalk.bold.red(`[missing]`));
346
346
  needed = true;
347
347
  }
348
348
  }
@@ -494,6 +494,7 @@ async function prepare() {
494
494
  fs.renameSync(dist_dir + '/src/koffi/LICENSE.txt', dist_dir + '/LICENSE.txt');
495
495
  fs.renameSync(dist_dir + '/src/koffi/ChangeLog.md', dist_dir + '/ChangeLog.md');
496
496
  fs.renameSync(dist_dir + '/web/koffi.dev', dist_dir + '/doc');
497
+ fs.rmdirSync(dist_dir + '/web');
497
498
  }
498
499
 
499
500
  return dist_dir;
@@ -514,6 +515,8 @@ function snapshot() {
514
515
  } else if (parts[0] == 'src') {
515
516
  return parts[1] == null || parts[1] == 'cnoke' ||
516
517
  parts[1] == 'koffi';
518
+ } else if (parts[0] == 'tools') {
519
+ return parts[1] == null || parts[1] != 'qemu';
517
520
  } else if (parts[0] == 'vendor') {
518
521
  return parts[1] == null || parts[1] == 'brotli' ||
519
522
  parts[1] == 'dragonbox' ||
File without changes
File without changes
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env python3
2
+
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Affero General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Affero General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Affero General Public License
14
+ # along with this program. If not, see https://www.gnu.org/licenses/.
15
+
16
+ import argparse
17
+ import os
18
+
19
+ def write_asm_trampolines(filename, comment_char, n,
20
+ fmt_export, fmt_export_x, fmt_proc, fmt_proc_x, end = None):
21
+ with open(filename) as f:
22
+ lines = f.readlines()
23
+
24
+ with open(filename, 'w') as f:
25
+ for line in lines:
26
+ if line.rstrip() == comment_char + ' Trampolines':
27
+ break
28
+ f.write(line)
29
+
30
+ print(comment_char + ' Trampolines', file = f)
31
+ print(comment_char + ' ----------------------------', file = f)
32
+ print('', file = f)
33
+
34
+ for i in range(0, n):
35
+ print(fmt_export.format(i), file = f)
36
+ print('', file = f)
37
+ for i in range(0, n):
38
+ print(fmt_export_x.format(i), file = f)
39
+ print('', file = f)
40
+
41
+ for i in range(0, n):
42
+ print(fmt_proc.format(i), file = f)
43
+ print('', file = f)
44
+ for i in range(0, n):
45
+ print(fmt_proc_x.format(i), file = f)
46
+
47
+ if end is not None:
48
+ print('', file = f)
49
+ print(end, file = f)
50
+
51
+ def write_cxx_trampolines(filename, n):
52
+ with open(filename) as f:
53
+ lines = f.readlines()
54
+
55
+ with open(filename, 'w') as f:
56
+ for line in lines:
57
+ if not line.startswith('//'):
58
+ break
59
+ f.write(line)
60
+
61
+ print('', file = f)
62
+ for i in range(0, n):
63
+ print('extern "C" int Trampoline{0}; extern "C" int TrampolineX{0};'.format(i), file = f)
64
+
65
+ print('', file = f)
66
+ print('static void *const Trampolines[][2] = {', file = f)
67
+ for i in range(0, n):
68
+ if i + 1 < n:
69
+ print(' {{ &Trampoline{0}, &TrampolineX{0} }},'.format(i), file = f)
70
+ else:
71
+ print(' {{ &Trampoline{0}, &TrampolineX{0} }}'.format(i), file = f)
72
+ print('};', file = f)
73
+ print('RG_STATIC_ASSERT(RG_LEN(Trampolines) == MaxTrampolines);', file = f)
74
+
75
+ if __name__ == "__main__":
76
+ parser = argparse.ArgumentParser(description = 'Generate static trampolines')
77
+ parser.add_argument('n', metavar = 'n', type = int, help = 'Number of trampolines')
78
+ args = parser.parse_args()
79
+
80
+ src_dir = os.path.dirname(__file__) + '/../src'
81
+
82
+ write_asm_trampolines(src_dir + '/abi_arm32_fwd.S', '#', args.n,
83
+ '.global Trampoline{0}',
84
+ '.global TrampolineX{0}',
85
+ 'Trampoline{0}:\n trampoline {0}',
86
+ 'TrampolineX{0}:\n trampoline_vec {0}'
87
+ )
88
+
89
+ write_asm_trampolines(src_dir + '/abi_arm64_fwd.asm', ';', args.n,
90
+ ' EXPORT Trampoline{0}',
91
+ ' EXPORT TrampolineX{0}',
92
+ 'Trampoline{0} PROC\n trampoline {0}\n ENDP',
93
+ 'TrampolineX{0} PROC\n trampoline_vec {0}\n ENDP',
94
+ ' END'
95
+ )
96
+ write_asm_trampolines(src_dir + '/abi_arm64_fwd.S', '#', args.n,
97
+ '.global SYMBOL(Trampoline{0})',
98
+ '.global SYMBOL(TrampolineX{0})',
99
+ 'SYMBOL(Trampoline{0}):\n trampoline {0}',
100
+ 'SYMBOL(TrampolineX{0}):\n trampoline_vec {0}'
101
+ )
102
+
103
+ write_asm_trampolines(src_dir + '/abi_riscv64_fwd.S', '#', args.n,
104
+ '.global Trampoline{0}',
105
+ '.global TrampolineX{0}',
106
+ 'Trampoline{0}:\n trampoline {0}',
107
+ 'TrampolineX{0}:\n trampoline_vec {0}'
108
+ )
109
+
110
+ write_asm_trampolines(src_dir + '/abi_x64_sysv_fwd.S', '#', args.n,
111
+ '.global SYMBOL(Trampoline{0})',
112
+ '.global SYMBOL(TrampolineX{0})',
113
+ 'SYMBOL(Trampoline{0}):\n trampoline {0}',
114
+ 'SYMBOL(TrampolineX{0}):\n trampoline_xmm {0}'
115
+ )
116
+ write_asm_trampolines(src_dir + '/abi_x64_win_fwd.asm', ';', args.n,
117
+ 'public Trampoline{0}',
118
+ 'public TrampolineX{0}',
119
+ 'Trampoline{0} proc frame\n trampoline {0}\nTrampoline{0} endp',
120
+ 'TrampolineX{0} proc frame\n trampoline_xmm {0}\nTrampolineX{0} endp',
121
+ 'end'
122
+ )
123
+
124
+ write_asm_trampolines(src_dir + '/abi_x86_fwd.asm', ';', args.n,
125
+ 'public Trampoline{0}',
126
+ 'public TrampolineX{0}',
127
+ 'Trampoline{0} proc\n trampoline {0}\nTrampoline{0} endp',
128
+ 'TrampolineX{0} proc\n trampoline_x87 {0}\nTrampolineX{0} endp',
129
+ 'end'
130
+ )
131
+ write_asm_trampolines(src_dir + '/abi_x86_fwd.S', '#', args.n,
132
+ '.global Trampoline{0}',
133
+ '.global TrampolineX{0}',
134
+ 'Trampoline{0}:\n trampoline {0}',
135
+ 'TrampolineX{0}:\n trampoline_x87 {0}'
136
+ )
137
+
138
+ write_cxx_trampolines(src_dir + '/abi_trampolines.inc', args.n)