koffi 2.2.3-beta.3 → 2.2.3

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 (43) hide show
  1. package/ChangeLog.md +10 -0
  2. package/package.json +2 -2
  3. package/src/koffi/build/2.2.3/koffi_darwin_arm64.tar.gz +0 -0
  4. package/src/koffi/build/2.2.3/koffi_darwin_x64.tar.gz +0 -0
  5. package/src/koffi/build/2.2.3/koffi_freebsd_arm64.tar.gz +0 -0
  6. package/src/koffi/build/2.2.3/koffi_freebsd_ia32.tar.gz +0 -0
  7. package/src/koffi/build/2.2.3/koffi_freebsd_x64.tar.gz +0 -0
  8. package/src/koffi/build/2.2.3/koffi_linux_arm32hf.tar.gz +0 -0
  9. package/src/koffi/build/2.2.3/koffi_linux_arm64.tar.gz +0 -0
  10. package/src/koffi/build/2.2.3/koffi_linux_ia32.tar.gz +0 -0
  11. package/src/koffi/build/2.2.3/koffi_linux_riscv64hf64.tar.gz +0 -0
  12. package/src/koffi/build/2.2.3/koffi_linux_x64.tar.gz +0 -0
  13. package/src/koffi/build/2.2.3/koffi_openbsd_ia32.tar.gz +0 -0
  14. package/src/koffi/build/2.2.3/koffi_openbsd_x64.tar.gz +0 -0
  15. package/src/koffi/build/2.2.3/koffi_win32_arm64.tar.gz +0 -0
  16. package/src/koffi/build/2.2.3/koffi_win32_ia32.tar.gz +0 -0
  17. package/src/koffi/build/2.2.3/koffi_win32_x64.tar.gz +0 -0
  18. package/src/koffi/src/abi_arm64.cc +39 -0
  19. package/src/koffi/src/abi_x64_sysv_fwd.S +9 -9
  20. package/src/koffi/src/abi_x64_win_fwd.asm +12 -12
  21. package/src/koffi/src/abi_x86_fwd.S +13 -13
  22. package/src/koffi/src/abi_x86_fwd.asm +11 -11
  23. package/src/koffi/src/ffi.cc +27 -50
  24. package/src/koffi/test/CMakeLists.txt +7 -0
  25. package/src/koffi/test/win32.c +39 -0
  26. package/src/koffi/test/win32.js +70 -0
  27. package/src/koffi/tools/qemu.js +20 -9
  28. package/src/koffi/tools/registry/machines.json +10 -2
  29. package/src/koffi/build/2.2.3-beta.3/koffi_darwin_arm64.tar.gz +0 -0
  30. package/src/koffi/build/2.2.3-beta.3/koffi_darwin_x64.tar.gz +0 -0
  31. package/src/koffi/build/2.2.3-beta.3/koffi_freebsd_arm64.tar.gz +0 -0
  32. package/src/koffi/build/2.2.3-beta.3/koffi_freebsd_ia32.tar.gz +0 -0
  33. package/src/koffi/build/2.2.3-beta.3/koffi_freebsd_x64.tar.gz +0 -0
  34. package/src/koffi/build/2.2.3-beta.3/koffi_linux_arm32hf.tar.gz +0 -0
  35. package/src/koffi/build/2.2.3-beta.3/koffi_linux_arm64.tar.gz +0 -0
  36. package/src/koffi/build/2.2.3-beta.3/koffi_linux_ia32.tar.gz +0 -0
  37. package/src/koffi/build/2.2.3-beta.3/koffi_linux_riscv64hf64.tar.gz +0 -0
  38. package/src/koffi/build/2.2.3-beta.3/koffi_linux_x64.tar.gz +0 -0
  39. package/src/koffi/build/2.2.3-beta.3/koffi_openbsd_ia32.tar.gz +0 -0
  40. package/src/koffi/build/2.2.3-beta.3/koffi_openbsd_x64.tar.gz +0 -0
  41. package/src/koffi/build/2.2.3-beta.3/koffi_win32_arm64.tar.gz +0 -0
  42. package/src/koffi/build/2.2.3-beta.3/koffi_win32_ia32.tar.gz +0 -0
  43. package/src/koffi/build/2.2.3-beta.3/koffi_win32_x64.tar.gz +0 -0
package/ChangeLog.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  ## History
4
4
 
5
+ ### Koffi 2.2.3
6
+
7
+ **Main fixes:**
8
+
9
+ - Support native code that uses [Structured Exception Handling (SEH)](https://learn.microsoft.com/en-us/cpp/cpp/structured-exception-handling-c-cpp) on Windows (x86, x64 and ARM64)
10
+
11
+ **Other changes:**
12
+
13
+ - Try to use ebp/rbp as frame pointer in x86/x64 ASM code
14
+
5
15
  ### Koffi 2.2.2
6
16
 
7
17
  **Main fixes:**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koffi",
3
- "version": "2.2.3-beta.3",
4
- "stable": "2.2.2",
3
+ "version": "2.2.3",
4
+ "stable": "2.2.3",
5
5
  "description": "Fast and simple C FFI (foreign function interface) for Node.js",
6
6
  "keywords": [
7
7
  "foreign",
@@ -17,6 +17,9 @@
17
17
  #include "ffi.hh"
18
18
  #include "call.hh"
19
19
  #include "util.hh"
20
+ #ifdef _WIN32
21
+ #include "win32.hh"
22
+ #endif
20
23
 
21
24
  #include <napi.h>
22
25
 
@@ -494,6 +497,24 @@ bool CallData::Prepare(const FunctionInfo *func, const Napi::CallbackInfo &info)
494
497
 
495
498
  void CallData::Execute(const FunctionInfo *func)
496
499
  {
500
+ #ifdef _WIN32
501
+ TEB *teb = GetTEB();
502
+
503
+ // Restore previous stack limits at the end
504
+ RG_DEFER_C(base = teb->StackBase,
505
+ limit = teb->StackLimit,
506
+ dealloc = teb->DeallocationStack) {
507
+ teb->StackBase = base;
508
+ teb->StackLimit = limit;
509
+ teb->DeallocationStack = dealloc;
510
+ };
511
+
512
+ // Adjust stack limits so SEH works correctly
513
+ teb->StackBase = mem->stack0.end();
514
+ teb->StackLimit = mem->stack0.ptr;
515
+ teb->DeallocationStack = mem->stack0.ptr;
516
+ #endif
517
+
497
518
  #define PERFORM_CALL(Suffix) \
498
519
  ([&]() { \
499
520
  auto ret = (func->forward_fp ? ForwardCallX ## Suffix(func->func, new_sp, &old_sp) \
@@ -611,6 +632,24 @@ void CallData::Relay(Size idx, uint8_t *own_sp, uint8_t *caller_sp, bool async,
611
632
  if (RG_UNLIKELY(env.IsExceptionPending()))
612
633
  return;
613
634
 
635
+ #ifdef _WIN32
636
+ TEB *teb = GetTEB();
637
+
638
+ // Restore previous stack limits at the end
639
+ RG_DEFER_C(base = teb->StackBase,
640
+ limit = teb->StackLimit,
641
+ dealloc = teb->DeallocationStack) {
642
+ teb->StackBase = base;
643
+ teb->StackLimit = limit;
644
+ teb->DeallocationStack = dealloc;
645
+ };
646
+
647
+ // Adjust stack limits so SEH works correctly
648
+ teb->StackBase = instance->main_stack_max;
649
+ teb->StackLimit = instance->main_stack_min;
650
+ teb->DeallocationStack = instance->main_stack_min;
651
+ #endif
652
+
614
653
  const TrampolineInfo &trampoline = shared.trampolines[idx];
615
654
 
616
655
  const FunctionInfo *proto = trampoline.proto;
@@ -45,11 +45,11 @@
45
45
  .cfi_def_cfa rsp, 8
46
46
  ENDBR64
47
47
  movq %rdi, %r11
48
- pushq %rbx
48
+ pushq %rbp
49
49
  .cfi_def_cfa rsp, 16
50
50
  movq %rsp, (%rdx)
51
- movq %rsp, %rbx
52
- .cfi_def_cfa rbx, 16
51
+ movq %rsp, %rbp
52
+ .cfi_def_cfa rbp, 16
53
53
  leaq 112(%rsi), %rsp
54
54
  .endm
55
55
 
@@ -58,8 +58,8 @@
58
58
  # The return value is passed untouched through RAX or XMM0.
59
59
  .macro epilogue
60
60
  call *%r11
61
- movq %rbx, %rsp
62
- popq %rbx
61
+ movq %rbp, %rsp
62
+ popq %rbp
63
63
  .cfi_def_cfa rsp, 8
64
64
  ret
65
65
  .cfi_endproc
@@ -241,9 +241,9 @@ SYMBOL(CallSwitchStack):
241
241
  .cfi_startproc
242
242
  .cfi_def_cfa rsp, 8
243
243
  ENDBR64
244
- push %rbx
244
+ push %rbp
245
245
  .cfi_def_cfa rsp, 16
246
- movq %rsp, %rbx
246
+ movq %rsp, %rbp
247
247
  movq %rsp, %r10
248
248
  subq 0(%r8), %r10
249
249
  andq $-16, %r10
@@ -251,9 +251,9 @@ SYMBOL(CallSwitchStack):
251
251
  movq %rcx, %rsp
252
252
  .cfi_def_cfa rsp, 16
253
253
  call *%r9
254
- mov %rbx, %rsp
254
+ mov %rbp, %rsp
255
255
  .cfi_def_cfa rsp, 16
256
- pop %rbx
256
+ pop %rbp
257
257
  .cfi_def_cfa rsp, 8
258
258
  ret
259
259
  .cfi_endproc
@@ -33,11 +33,11 @@ public ForwardCallXD
33
33
  prologue macro
34
34
  endbr64
35
35
  mov rax, rcx
36
- push rbx
37
- .pushreg rbx
38
- mov rbx, rsp
36
+ push rbp
37
+ .pushreg rbp
38
+ mov rbp, rsp
39
39
  mov qword ptr [r8+0], rsp
40
- .setframe rbx, 0
40
+ .setframe rbp, 0
41
41
  .endprolog
42
42
  mov rsp, rdx
43
43
  endm
@@ -47,8 +47,8 @@ endm
47
47
  ; The return value is passed untouched through RAX or XMM0.
48
48
  epilogue macro
49
49
  call rax
50
- mov rsp, rbx
51
- pop rbx
50
+ mov rsp, rbp
51
+ pop rbp
52
52
  ret
53
53
  endm
54
54
 
@@ -169,10 +169,10 @@ endm
169
169
  ; The first three parameters (rcx, rdx, r8) are passed through untouched.
170
170
  CallSwitchStack proc frame
171
171
  endbr64
172
- push rbx
173
- .pushreg rbx
174
- mov rbx, rsp
175
- .setframe rbx, 0
172
+ push rbp
173
+ .pushreg rbp
174
+ mov rbp, rsp
175
+ .setframe rbp, 0
176
176
  .endprolog
177
177
  mov rax, qword ptr [rsp+56]
178
178
  mov r10, rsp
@@ -182,8 +182,8 @@ CallSwitchStack proc frame
182
182
  mov qword ptr [r11+8], r10
183
183
  lea rsp, [r9-32]
184
184
  call rax
185
- mov rsp, rbx
186
- pop rbx
185
+ mov rsp, rbp
186
+ pop rbp
187
187
  ret
188
188
  CallSwitchStack endp
189
189
 
@@ -27,10 +27,10 @@
27
27
  .cfi_startproc
28
28
  .cfi_def_cfa esp, 4
29
29
  ENDBR32
30
- push %ebx
30
+ push %ebp
31
31
  .cfi_def_cfa esp, 8
32
- movl %esp, %ebx
33
- .cfi_def_cfa ebx, 8
32
+ movl %esp, %ebp
33
+ .cfi_def_cfa ebp, 8
34
34
  movl 16(%esp), %eax
35
35
  movl %esp, 0(%eax)
36
36
  movl 8(%esp), %eax
@@ -48,8 +48,8 @@
48
48
  # The return value is passed back untouched.
49
49
  .macro epilogue
50
50
  call *%eax
51
- movl %ebx, %esp
52
- pop %ebx
51
+ movl %ebp, %esp
52
+ pop %ebp
53
53
  .cfi_def_cfa esp, 4
54
54
  ret
55
55
  .cfi_endproc
@@ -168,10 +168,10 @@ CallSwitchStack:
168
168
  .cfi_startproc
169
169
  .cfi_def_cfa esp, 4
170
170
  ENDBR32
171
- push %ebx
171
+ push %ebp
172
172
  .cfi_def_cfa esp, 8
173
- movl %esp, %ebx
174
- .cfi_def_cfa ebx, 8
173
+ movl %esp, %ebp
174
+ .cfi_def_cfa ebp, 8
175
175
  movl 28(%esp), %edx
176
176
  movl 24(%esp), %ecx
177
177
  movl %esp, %eax
@@ -180,16 +180,16 @@ CallSwitchStack:
180
180
  movl %eax, 4(%ecx)
181
181
  movl 20(%esp), %esp
182
182
  subl $28, %esp
183
- movl 8(%ebx), %eax
183
+ movl 8(%ebp), %eax
184
184
  movl %eax, 0(%esp)
185
- movl 12(%ebx), %eax
185
+ movl 12(%ebp), %eax
186
186
  movl %eax, 4(%esp)
187
- movl 16(%ebx), %eax
187
+ movl 16(%ebp), %eax
188
188
  movl %eax, 8(%esp)
189
189
  call *%edx
190
- mov %ebx, %esp
190
+ mov %ebp, %esp
191
191
  .cfi_def_cfa esp, 8
192
- pop %ebx
192
+ pop %ebp
193
193
  .cfi_def_cfa esp, 4
194
194
  ret
195
195
  .cfi_endproc
@@ -29,8 +29,8 @@ public ForwardCallRD
29
29
  ; Save ESP in EBX (non-volatile), and use carefully assembled stack provided by caller.
30
30
  prologue macro
31
31
  endbr32
32
- push ebx
33
- mov ebx, esp
32
+ push ebp
33
+ mov ebp, esp
34
34
  mov eax, dword ptr [esp+16]
35
35
  mov dword ptr [eax+0], esp
36
36
  mov eax, dword ptr [esp+8]
@@ -48,8 +48,8 @@ endm
48
48
  ; The return value is passed back untouched.
49
49
  epilogue macro
50
50
  call eax
51
- mov esp, ebx
52
- pop ebx
51
+ mov esp, ebp
52
+ pop ebp
53
53
  ret
54
54
  endm
55
55
 
@@ -153,8 +153,8 @@ endm
153
153
  ; stack pointer, call Node.js/V8 and go back to ours.
154
154
  CallSwitchStack proc
155
155
  endbr32
156
- push ebx
157
- mov ebx, esp
156
+ push ebp
157
+ mov ebp, esp
158
158
  mov edx, dword ptr [esp+28]
159
159
  mov ecx, dword ptr [esp+24]
160
160
  mov eax, esp
@@ -163,15 +163,15 @@ CallSwitchStack proc
163
163
  mov dword ptr [ecx+4], eax
164
164
  mov esp, dword ptr [esp+20]
165
165
  sub esp, 28
166
- mov eax, dword ptr [ebx+8]
166
+ mov eax, dword ptr [ebp+8]
167
167
  mov dword ptr [esp+0], eax
168
- mov eax, dword ptr [ebx+12]
168
+ mov eax, dword ptr [ebp+12]
169
169
  mov dword ptr [esp+4], eax
170
- mov eax, dword ptr [ebx+16]
170
+ mov eax, dword ptr [ebp+16]
171
171
  mov dword ptr [esp+8], eax
172
172
  call edx
173
- mov esp, ebx
174
- pop ebx
173
+ mov esp, ebp
174
+ pop ebp
175
175
  ret
176
176
  CallSwitchStack endp
177
177
 
@@ -33,6 +33,9 @@
33
33
  #include <dlfcn.h>
34
34
  #include <unistd.h>
35
35
  #include <sys/mman.h>
36
+ #ifndef MAP_STACK
37
+ #define MAP_STACK 0
38
+ #endif
36
39
  #endif
37
40
 
38
41
  #include <napi.h>
@@ -1019,60 +1022,39 @@ static InstanceMemory *AllocateMemory(InstanceData *instance, Size stack_size, S
1019
1022
  InstanceMemory *mem = new InstanceMemory();
1020
1023
  RG_DEFER_N(mem_guard) { delete mem; };
1021
1024
 
1025
+ stack_size = AlignLen(stack_size, Kibibytes(64));
1026
+
1022
1027
  #if defined(_WIN32)
1023
- {
1024
- struct FiberContext {
1025
- InstanceMemory *mem;
1026
- void *self;
1027
- bool was_fiber;
1028
- };
1028
+ static const int PageSize = ([]() {
1029
+ SYSTEM_INFO info = {};
1029
1030
 
1030
- FiberContext ctx;
1031
- bool is_fiber = IsThreadAFiber();
1031
+ GetNativeSystemInfo(&info);
1032
+ RG_ASSERT(info.dwPageSize);
1032
1033
 
1033
- ctx.mem = mem;
1034
- ctx.self = is_fiber ? GetCurrentFiber() : ConvertThreadToFiber(nullptr);
1035
- if (!ctx.self) {
1036
- LogError("Failed to make initial fiber: %1", GetWin32ErrorString());
1037
- return nullptr;
1038
- }
1039
- RG_DEFER {
1040
- if (!is_fiber) {
1041
- ConvertFiberToThread();
1042
- }
1043
- };
1034
+ return (int)info.dwPageSize;
1035
+ })();
1044
1036
 
1045
- // Work around issue with CreateFiber() API and stack size
1046
- // See here: https://github.com/google/marl/issues/12
1047
- mem->fiber = CreateFiberEx(stack_size - 1, stack_size,
1048
- FIBER_FLAG_FLOAT_SWITCH, [](void *udata) {
1049
- FiberContext *ctx = (FiberContext *)udata;
1050
-
1051
- TEB *teb = GetTEB();
1037
+ // Allocate stack memory
1038
+ {
1039
+ mem->stack.len = stack_size;
1040
+ mem->stack.ptr = (uint8_t *)VirtualAlloc(nullptr, mem->stack.len + 2 * PageSize, MEM_RESERVE, PAGE_NOACCESS);
1052
1041
 
1053
- ctx->mem->stack.ptr = (uint8_t *)teb->DeallocationStack;
1054
- ctx->mem->stack.len = (uint8_t *)teb->StackBase - ctx->mem->stack.ptr;
1042
+ RG_CRITICAL(mem->stack.ptr, "Failed to allocate %1 of memory", mem->stack.len);
1055
1043
 
1056
- SwitchToFiber(ctx->self);
1057
- }, &ctx);
1044
+ bool success = true;
1058
1045
 
1059
- if (!mem->fiber) {
1060
- LogError("Failed to create Win32 fiber: %1", GetWin32ErrorString());
1061
- return nullptr;
1062
- }
1046
+ success &= !!VirtualAlloc(mem->stack.ptr + 0 * PageSize, PageSize, MEM_COMMIT, PAGE_NOACCESS);
1047
+ success &= !!VirtualAlloc(mem->stack.ptr + 1 * PageSize, PageSize, MEM_COMMIT, PAGE_READWRITE | PAGE_GUARD);
1048
+ success &= !!VirtualAlloc(mem->stack.ptr + 2 * PageSize, mem->stack.len, MEM_COMMIT, PAGE_READWRITE);
1063
1049
 
1064
- SwitchToFiber(mem->fiber);
1050
+ RG_CRITICAL(success, "Cannot commit stack memory: %1", GetWin32ErrorString());
1065
1051
  }
1066
-
1067
- RG_ASSERT(mem->stack.ptr);
1068
- #elif defined(__APPLE__)
1069
- mem->stack.len = stack_size;
1070
- mem->stack.ptr = (uint8_t *)mmap(nullptr, mem->stack.len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
1071
1052
  #else
1072
1053
  mem->stack.len = stack_size;
1073
1054
  mem->stack.ptr = (uint8_t *)mmap(nullptr, mem->stack.len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_STACK, -1, 0);
1074
- #endif
1055
+
1075
1056
  RG_CRITICAL(mem->stack.ptr, "Failed to allocate %1 of memory", mem->stack.len);
1057
+ #endif
1076
1058
 
1077
1059
  #ifdef __OpenBSD__
1078
1060
  // Make sure the SP points inside the MAP_STACK area, or (void) functions may crash on OpenBSD i386
@@ -1773,16 +1755,11 @@ static InstanceData *CreateInstance(Napi::Env env)
1773
1755
  }
1774
1756
  napi_unref_threadsafe_function(env, instance->broker);
1775
1757
 
1776
- #if defined(_WIN32) && (defined(__x86_64__) || defined(_M_AMD64))
1777
- void *teb = (void *)__readgsqword(0x30);
1778
-
1779
- instance->main_stack_max = *(void **)((uint8_t *)teb + 0x08); // StackBase
1780
- instance->main_stack_min = *(void **)((uint8_t *)teb + 0x1478); // DeallocationStack
1781
- #elif defined(_WIN32) && (defined(__i386__) || defined(_M_IX86))
1782
- void *teb = (void *)__readfsdword(0x18);
1758
+ #ifdef _WIN32
1759
+ TEB *teb = GetTEB();
1783
1760
 
1784
- instance->main_stack_max = *(void **)((uint8_t *)teb + 0x04); // StackBase
1785
- instance->main_stack_min = *(void **)((uint8_t *)teb + 0xE0C); // DeallocationStack
1761
+ instance->main_stack_max = teb->StackBase;
1762
+ instance->main_stack_min = teb->DeallocationStack;
1786
1763
  #endif
1787
1764
 
1788
1765
  err_guard.Disable();
@@ -116,3 +116,10 @@ if(MSVC)
116
116
  target_compile_options(misc PRIVATE /wd4116)
117
117
  target_link_options(misc PRIVATE "/DEF:${CMAKE_CURRENT_SOURCE_DIR}/misc.def")
118
118
  endif()
119
+
120
+ # ---- Win32 ----
121
+
122
+ if(WIN32)
123
+ add_library(win32 SHARED win32.c)
124
+ set_target_properties(win32 PROPERTIES PREFIX "")
125
+ endif()
@@ -0,0 +1,39 @@
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 <stdlib.h>
15
+ #include <stdio.h>
16
+ #include <inttypes.h>
17
+ #include <string.h>
18
+ #include <stdarg.h>
19
+
20
+ #define NOMINMAX
21
+ #define WIN32_LEAN_AND_MEAN
22
+ #include <windows.h>
23
+
24
+ #define EXPORT __declspec(dllexport)
25
+
26
+ EXPORT int DivideBySafe(int a, int b)
27
+ {
28
+ __try {
29
+ return a / b;
30
+ } __except (GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO) {
31
+ return -42;
32
+ }
33
+ }
34
+
35
+ EXPORT int CallThrough(int (*func)(void))
36
+ {
37
+ int ret = func();
38
+ return ret;
39
+ }
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env node
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
+ const koffi = require('./build/koffi.node');
17
+ const assert = require('assert');
18
+ const util = require('util');
19
+
20
+ const CallThroughFunc = koffi.callback('int CallThroughFunc()');
21
+
22
+ main();
23
+
24
+ async function main() {
25
+ try {
26
+ await test();
27
+ console.log('Success!');
28
+ } catch (err) {
29
+ console.error(err);
30
+ process.exit(1);
31
+ }
32
+ }
33
+
34
+ async function test() {
35
+ let lib_filename = __dirname + '/build/win32.dll';
36
+ let lib = koffi.load(lib_filename);
37
+
38
+ const DivideBySafe = lib.func('int DivideBySafe(int a, int b)');
39
+ const CallThrough = lib.func('int CallThrough(CallThroughFunc *func)');
40
+
41
+ // Sync SEH support
42
+ assert.equal(DivideBySafe(12, 3), 4);
43
+ assert.equal(DivideBySafe(84, 0), -42);
44
+
45
+ // Async SEH support
46
+ {
47
+ let results = await Promise.all([
48
+ util.promisify(DivideBySafe.async)(90, -9),
49
+ util.promisify(DivideBySafe.async)(227, 0)
50
+ ]);
51
+
52
+ assert.equal(results[0], -10);
53
+ assert.equal(results[1], -42);
54
+ }
55
+
56
+ // Test SEH inside callback
57
+ assert.equal(CallThrough(() => DivideBySafe(16, 2)), 8);
58
+ assert.equal(CallThrough(() => DivideBySafe(16 * 2, 0)), -42);
59
+
60
+ // Test SEH inside callback running async
61
+ {
62
+ let results = await Promise.all([
63
+ util.promisify(CallThrough.async)(() => DivideBySafe(75, -5)),
64
+ util.promisify(CallThrough.async)(() => DivideBySafe(7, 5 - 5))
65
+ ]);
66
+
67
+ assert.equal(results[0], -15);
68
+ assert.equal(results[1], -42);
69
+ }
70
+ }
@@ -571,7 +571,7 @@ async function upload(snapshot_dir, func) {
571
571
  }
572
572
 
573
573
  let status = copied ? chalk.bold.green('[ok]') : chalk.bold.red('[error]');
574
- log(machine, 'Copy', status);
574
+ log(machine, 'Upload', status);
575
575
  }));
576
576
 
577
577
  return success;
@@ -605,7 +605,7 @@ async function test() {
605
605
  let ret = await exec_remote(machine, cmd, cwd);
606
606
  let time = Number((process.hrtime.bigint() - start) / 1000000n);
607
607
 
608
- if (ret.code == 0) {
608
+ if (ret.code === 0) {
609
609
  log(machine, `${suite} > ${name}`, chalk.bold.green(`[${(time / 1000).toFixed(2)}s]`));
610
610
  } else {
611
611
  log(machine, `${suite} > ${name}`, chalk.bold.red('[error]'));
@@ -654,8 +654,6 @@ async function stop(all = true) {
654
654
 
655
655
  console.log('>> Sending shutdown commands...');
656
656
  await Promise.all(machines.map(async machine => {
657
- if (ignore.has(machine))
658
- return;
659
657
  if (machine.qemu == null)
660
658
  return;
661
659
  if (!machine.started && !all)
@@ -916,19 +914,32 @@ function log(machine, action, status) {
916
914
  }
917
915
 
918
916
  async function exec_remote(machine, cmd, cwd = null) {
917
+ if (typeof cmd == 'string') {
918
+ cmd = {
919
+ command: cmd,
920
+ repeat: 1
921
+ };
922
+ }
923
+
919
924
  try {
925
+ let ret = { code: 0 };
926
+
920
927
  if (machine.platform == 'win32') {
928
+ let cmd_line = cmd.command;
929
+
921
930
  if (cwd != null) {
922
931
  cwd = cwd.replaceAll('/', '\\');
923
- cmd = `cd "${cwd}" && ${cmd}`;
932
+ cmd_line = `cd "${cwd}" && ${cmd_line}`;
924
933
  }
925
934
 
926
- let ret = await machine.ssh.execCommand(cmd);
927
- return ret;
935
+ for (let i = 0; ret.code === 0 && i < cmd.repeat; i++)
936
+ ret = await machine.ssh.execCommand(cmd_line);
928
937
  } else {
929
- let ret = await machine.ssh.execCommand(cmd, { cwd: cwd });
930
- return ret;
938
+ for (let i = 0; ret.code === 0 && i < cmd.repeat; i++)
939
+ ret = await machine.ssh.execCommand(cmd.command, { cwd: cwd });
931
940
  }
941
+
942
+ return ret;
932
943
  } catch (err) {
933
944
  console.log(err);
934
945
  return err;
@@ -230,7 +230,11 @@
230
230
  "Test Async": "C:\\Node32\\node32.cmd node test/async.js",
231
231
  "Test Callbacks": "C:\\Node32\\node32.cmd node test/callbacks.js",
232
232
  "Test Raylib": "seatsh C:\\Node32\\node32.cmd node test/raylib.js",
233
- "Test SQLite": "C:\\Node32\\node32.cmd node test/sqlite.js"
233
+ "Test SQLite": "C:\\Node32\\node32.cmd node test/sqlite.js",
234
+ "Test Win32": {
235
+ "command": "C:\\Node32\\node32.cmd node test/win32.js",
236
+ "repeat": 8
237
+ }
234
238
  }
235
239
  },
236
240
 
@@ -244,7 +248,11 @@
244
248
  "Test Async": "C:\\Node64\\node64.cmd node test/async.js",
245
249
  "Test Callbacks": "C:\\Node64\\node64.cmd node test/callbacks.js",
246
250
  "Test Raylib": "seatsh C:\\Node64\\node64.cmd node test/raylib.js",
247
- "Test SQLite": "C:\\Node64\\node64.cmd node test/sqlite.js"
251
+ "Test SQLite": "C:\\Node64\\node64.cmd node test/sqlite.js",
252
+ "Test Win32": {
253
+ "command": "C:\\Node64\\node64.cmd node test/win32.js",
254
+ "repeat": 8
255
+ }
248
256
  }
249
257
  }
250
258
  }