koffi 2.15.2 → 2.15.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 (45) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/build/koffi/darwin_arm64/koffi.node +0 -0
  3. package/build/koffi/darwin_x64/koffi.node +0 -0
  4. package/build/koffi/freebsd_arm64/koffi.node +0 -0
  5. package/build/koffi/freebsd_ia32/koffi.node +0 -0
  6. package/build/koffi/freebsd_x64/koffi.node +0 -0
  7. package/build/koffi/linux_arm64/koffi.node +0 -0
  8. package/build/koffi/linux_armhf/koffi.node +0 -0
  9. package/build/koffi/linux_ia32/koffi.node +0 -0
  10. package/build/koffi/linux_loong64/koffi.node +0 -0
  11. package/build/koffi/linux_riscv64d/koffi.node +0 -0
  12. package/build/koffi/linux_x64/koffi.node +0 -0
  13. package/build/koffi/musl_arm64/koffi.node +0 -0
  14. package/build/koffi/musl_x64/koffi.node +0 -0
  15. package/build/koffi/openbsd_ia32/koffi.node +0 -0
  16. package/build/koffi/openbsd_x64/koffi.node +0 -0
  17. package/build/koffi/win32_arm64/koffi.node +0 -0
  18. package/build/koffi/win32_ia32/koffi.node +0 -0
  19. package/build/koffi/win32_x64/koffi.node +0 -0
  20. package/index.js +8 -8
  21. package/indirect.js +8 -8
  22. package/package.json +1 -1
  23. package/src/koffi/src/abi_arm32.cc +7 -14
  24. package/src/koffi/src/abi_arm32_asm.S +6 -10
  25. package/src/koffi/src/abi_arm64.cc +7 -14
  26. package/src/koffi/src/abi_arm64_asm.S +4 -7
  27. package/src/koffi/src/abi_arm64_asm.asm +5 -7
  28. package/src/koffi/src/abi_loong64_asm.S +4 -7
  29. package/src/koffi/src/abi_riscv64.cc +7 -14
  30. package/src/koffi/src/abi_riscv64_asm.S +4 -7
  31. package/src/koffi/src/abi_x64_sysv.cc +7 -14
  32. package/src/koffi/src/abi_x64_sysv_asm.S +4 -7
  33. package/src/koffi/src/abi_x64_win.cc +7 -14
  34. package/src/koffi/src/abi_x64_win_asm.S +29 -24
  35. package/src/koffi/src/abi_x64_win_asm.asm +28 -25
  36. package/src/koffi/src/abi_x86.cc +17 -15
  37. package/src/koffi/src/abi_x86_asm.S +11 -17
  38. package/src/koffi/src/abi_x86_asm.asm +6 -14
  39. package/src/koffi/src/call.cc +37 -46
  40. package/src/koffi/src/call.hh +7 -9
  41. package/src/koffi/src/ffi.cc +41 -20
  42. package/src/koffi/src/ffi.hh +1 -1
  43. package/src/koffi/src/util.cc +7 -11
  44. package/src/koffi/src/win32.cc +13 -0
  45. package/src/koffi/src/win32.hh +2 -0
@@ -13,6 +13,8 @@
13
13
  .global ForwardCallXF
14
14
  .global ForwardCallXD
15
15
 
16
+ .global SehHandler
17
+
16
18
  # Copy function pointer to RAX, in order to save it through argument forwarding.
17
19
  # Also make a copy of the SP to CallData::old_sp because the callback system might need it.
18
20
  # Save RSP in RBX (non-volatile), and use carefully assembled stack provided by caller.
@@ -25,16 +27,6 @@
25
27
  movq %rdx, %rsp
26
28
  .endm
27
29
 
28
- # Call native function.
29
- # Once done, restore normal stack pointer and return.
30
- # The return value is passed untouched through RAX or XMM0.
31
- .macro epilogue
32
- call *%rax
33
- movq %rbp, %rsp
34
- pop %rbp
35
- ret
36
- .endm
37
-
38
30
  # Prepare integer argument registers from array passed by caller.
39
31
  .macro forward_gpr
40
32
  movq 24(%rdx), %r9
@@ -51,44 +43,62 @@
51
43
  movsd 0(%rdx), %xmm0
52
44
  .endm
53
45
 
46
+ # CallNative is a minimal wrapper around the native function call.
47
+ # Its frame lives entirely on the custom stack, so its EstablisherFrame passes
48
+ # the TEB stack bounds check. This ensures that SehHandler is found by
49
+ # RtlDispatchException before it tries to cross back to the original stack
50
+ # (through ForwardCall*), which would fail the bounds check and terminate.
51
+ # After the native call returns, CallNative restores the original stack via
52
+ # RBP (non-volatile, preserved by native) and returns directly to the C++ caller.
53
+ CallNative:
54
+ .seh_proc CallNative
55
+ .seh_handler SehHandler, @except
56
+ .seh_endprologue
57
+ call *%rax
58
+ movq %rbp, %rsp
59
+ pop %rbp
60
+ ret
61
+ .seh_endproc
62
+
54
63
  ForwardCallG:
55
64
  prologue
56
65
  forward_gpr
57
- epilogue
66
+ jmp CallNative
58
67
 
59
68
  ForwardCallF:
60
69
  prologue
61
70
  forward_gpr
62
- epilogue
71
+ jmp CallNative
63
72
 
64
73
  ForwardCallD:
65
74
  prologue
66
75
  forward_gpr
67
- epilogue
76
+ jmp CallNative
68
77
 
69
78
  ForwardCallXG:
70
79
  prologue
71
80
  forward_xmm
72
81
  forward_gpr
73
- epilogue
82
+ jmp CallNative
74
83
 
75
84
  ForwardCallXF:
76
85
  prologue
77
86
  forward_xmm
78
87
  forward_gpr
79
- epilogue
88
+ jmp CallNative
80
89
 
81
90
  ForwardCallXD:
82
91
  prologue
83
92
  forward_xmm
84
93
  forward_gpr
85
- epilogue
94
+ jmp CallNative
86
95
 
87
96
  # Callbacks
88
97
  # ----------------------------
89
98
 
90
99
  .global RelayCallback
91
- .global CallSwitchStack
100
+ .global SwitchAndRelay
101
+ .global RelayDirect
92
102
 
93
103
  # First, make a copy of the GPR argument registers (rcx, rdx, r8, r9).
94
104
  # Then call the C function RelayCallback with the following arguments:
@@ -104,8 +114,6 @@ ForwardCallXD:
104
114
  movq %r9, 56(%rsp)
105
115
  movq $\id, %rcx
106
116
  leaq 32(%rsp), %rdx
107
- leaq 160(%rsp), %r8
108
- leaq 96(%rsp), %r9
109
117
  call RelayCallback
110
118
  movq 96(%rsp), %rax
111
119
  addq $120, %rsp
@@ -126,8 +134,6 @@ ForwardCallXD:
126
134
  movsd %xmm3, 88(%rsp)
127
135
  movq $\id, %rcx
128
136
  leaq 32(%rsp), %rdx
129
- leaq 160(%rsp), %r8
130
- leaq 96(%rsp), %r9
131
137
  call RelayCallback
132
138
  movq 96(%rsp), %rax
133
139
  movsd 104(%rsp), %xmm0
@@ -140,18 +146,17 @@ ForwardCallXD:
140
146
  # probably misdetect this as a "stack overflow". We have to restore the old
141
147
  # stack pointer, call Node.js/V8 and go back to ours.
142
148
  # The first three parameters (rcx, rdx, r8) are passed through untouched.
143
- CallSwitchStack:
149
+ SwitchAndRelay:
144
150
  endbr64
145
151
  push %rbp
146
152
  movq %rsp, %rbp
147
- movq 56(%rsp), %rax
148
153
  movq %rsp, %r10
149
154
  movq 48(%rsp), %r11
150
155
  subq 0(%r11), %r10
151
156
  andq $-16, %r10
152
157
  movq %r10, 8(%r11)
153
158
  leaq -32(%r9), %rsp
154
- call *%rax
159
+ call RelayDirect
155
160
  movq %rbp, %rsp
156
161
  pop %rbp
157
162
  ret
@@ -15,6 +15,8 @@ public ForwardCallXG
15
15
  public ForwardCallXF
16
16
  public ForwardCallXD
17
17
 
18
+ extern SehHandler : PROC
19
+
18
20
  .code
19
21
 
20
22
  ; Copy function pointer to RAX, in order to save it through argument forwarding.
@@ -32,16 +34,6 @@ prologue macro
32
34
  mov rsp, rdx
33
35
  endm
34
36
 
35
- ; Call native function.
36
- ; Once done, restore normal stack pointer and return.
37
- ; The return value is passed untouched through RAX or XMM0.
38
- epilogue macro
39
- call rax
40
- mov rsp, rbp
41
- pop rbp
42
- ret
43
- endm
44
-
45
37
  ; Prepare integer argument registers from array passed by caller.
46
38
  forward_gpr macro
47
39
  mov r9, qword ptr [rdx+24]
@@ -58,50 +50,66 @@ forward_xmm macro
58
50
  movsd xmm0, qword ptr [rdx+0]
59
51
  endm
60
52
 
53
+ ; CallNative is a minimal wrapper around the native function call.
54
+ ; Its frame lives entirely on the custom stack, so its EstablisherFrame passes
55
+ ; the TEB stack bounds check. This ensures that SehHandler is found by
56
+ ; RtlDispatchException before it tries to cross back to the original stack
57
+ ; (through ForwardCall*), which would fail the bounds check and terminate.
58
+ ; After the native call returns, CallNative restores the original stack via
59
+ ; RBP (non-volatile, preserved by native) and returns directly to the C++ caller.
60
+ CallNative proc frame:SehHandler
61
+ .endprolog
62
+ call rax
63
+ mov rsp, rbp
64
+ pop rbp
65
+ ret
66
+ CallNative endp
67
+
61
68
  ForwardCallG proc frame
62
69
  prologue
63
70
  forward_gpr
64
- epilogue
71
+ jmp CallNative
65
72
  ForwardCallG endp
66
73
 
67
74
  ForwardCallF proc frame
68
75
  prologue
69
76
  forward_gpr
70
- epilogue
77
+ jmp CallNative
71
78
  ForwardCallF endp
72
79
 
73
80
  ForwardCallD proc frame
74
81
  prologue
75
82
  forward_gpr
76
- epilogue
83
+ jmp CallNative
77
84
  ForwardCallD endp
78
85
 
79
86
  ForwardCallXG proc frame
80
87
  prologue
81
88
  forward_xmm
82
89
  forward_gpr
83
- epilogue
90
+ jmp CallNative
84
91
  ForwardCallXG endp
85
92
 
86
93
  ForwardCallXF proc frame
87
94
  prologue
88
95
  forward_xmm
89
96
  forward_gpr
90
- epilogue
97
+ jmp CallNative
91
98
  ForwardCallXF endp
92
99
 
93
100
  ForwardCallXD proc frame
94
101
  prologue
95
102
  forward_xmm
96
103
  forward_gpr
97
- epilogue
104
+ jmp CallNative
98
105
  ForwardCallXD endp
99
106
 
100
107
  ; Callbacks
101
108
  ; ----------------------------
102
109
 
103
110
  extern RelayCallback : PROC
104
- public CallSwitchStack
111
+ public SwitchAndRelay
112
+ extern RelayDirect : PROC
105
113
 
106
114
  ; First, make a copy of the GPR argument registers (rcx, rdx, r8, r9).
107
115
  ; Then call the C function RelayCallback with the following arguments:
@@ -119,8 +127,6 @@ trampoline macro ID
119
127
  mov qword ptr [rsp+56], r9
120
128
  mov rcx, ID
121
129
  lea rdx, qword ptr [rsp+32]
122
- lea r8, qword ptr [rsp+160]
123
- lea r9, qword ptr [rsp+96]
124
130
  call RelayCallback
125
131
  mov rax, qword ptr [rsp+96]
126
132
  add rsp, 120
@@ -143,8 +149,6 @@ trampoline_vec macro ID
143
149
  movsd qword ptr [rsp+88], xmm3
144
150
  mov rcx, ID
145
151
  lea rdx, qword ptr [rsp+32]
146
- lea r8, qword ptr [rsp+160]
147
- lea r9, qword ptr [rsp+96]
148
152
  call RelayCallback
149
153
  mov rax, qword ptr [rsp+96]
150
154
  movsd xmm0, qword ptr [rsp+104]
@@ -157,25 +161,24 @@ endm
157
161
  ; probably misdetect this as a "stack overflow". We have to restore the old
158
162
  ; stack pointer, call Node.js/V8 and go back to ours.
159
163
  ; The first three parameters (rcx, rdx, r8) are passed through untouched.
160
- CallSwitchStack proc frame
164
+ SwitchAndRelay proc frame
161
165
  endbr64
162
166
  push rbp
163
167
  .pushreg rbp
164
168
  mov rbp, rsp
165
169
  .setframe rbp, 0
166
170
  .endprolog
167
- mov rax, qword ptr [rsp+56]
168
171
  mov r10, rsp
169
172
  mov r11, qword ptr [rsp+48]
170
173
  sub r10, qword ptr [r11+0]
171
174
  and r10, -16
172
175
  mov qword ptr [r11+8], r10
173
176
  lea rsp, [r9-32]
174
- call rax
177
+ call RelayDirect
175
178
  mov rsp, rbp
176
179
  pop rbp
177
180
  ret
178
- CallSwitchStack endp
181
+ SwitchAndRelay endp
179
182
 
180
183
  ; Trampolines
181
184
  ; ----------------------------
@@ -26,6 +26,13 @@ struct BackRegisters {
26
26
  int ret_pop;
27
27
  };
28
28
 
29
+ #if defined(_WIN32)
30
+ struct SehFrame {
31
+ void *Next;
32
+ void *Handler;
33
+ };
34
+ #endif
35
+
29
36
  extern "C" uint64_t ForwardCallG(const void *func, uint8_t *sp, uint8_t **out_old_sp);
30
37
  extern "C" float ForwardCallF(const void *func, uint8_t *sp, uint8_t **out_old_sp);
31
38
  extern "C" double ForwardCallD(const void *func, uint8_t *sp, uint8_t **out_old_sp);
@@ -33,10 +40,6 @@ extern "C" uint64_t ForwardCallRG(const void *func, uint8_t *sp, uint8_t **out_o
33
40
  extern "C" float ForwardCallRF(const void *func, uint8_t *sp, uint8_t **out_old_sp);
34
41
  extern "C" double ForwardCallRD(const void *func, uint8_t *sp, uint8_t **out_old_sp);
35
42
 
36
- extern "C" napi_value CallSwitchStack(Napi::Function *func, size_t argc, napi_value *argv,
37
- uint8_t *old_sp, Span<uint8_t> *new_stack,
38
- napi_value (*call)(Napi::Function *func, size_t argc, napi_value *argv));
39
-
40
43
  #include "trampolines/prototypes.inc"
41
44
 
42
45
  bool AnalyseFunction(Napi::Env env, InstanceData *instance, FunctionInfo *func)
@@ -300,6 +303,7 @@ void CallData::Execute(const FunctionInfo *func, void *native)
300
303
  {
301
304
  #if defined(_WIN32)
302
305
  TEB *teb = GetTEB();
306
+ SehFrame *seh = (SehFrame *)mem->stack.ptr;
303
307
 
304
308
  // Restore previous stack limits at the end
305
309
  K_DEFER_C(exception_list = teb->ExceptionList,
@@ -319,7 +323,9 @@ void CallData::Execute(const FunctionInfo *func, void *native)
319
323
  };
320
324
 
321
325
  // Adjust stack limits so SEH works correctly
322
- teb->ExceptionList = (void *)-1; // EXCEPTION_CHAIN_END
326
+ seh->Next = (void *)-1; \
327
+ seh->Handler = (void *)SehHandler; \
328
+ teb->ExceptionList = seh; \
323
329
  teb->StackBase = mem->stack0.end();
324
330
  teb->StackLimit = mem->stack0.ptr;
325
331
  teb->DeallocationStack = mem->stack0.ptr;
@@ -445,8 +451,11 @@ Napi::Value CallData::Complete(const FunctionInfo *func)
445
451
  K_UNREACHABLE();
446
452
  }
447
453
 
448
- void CallData::Relay(Size idx, uint8_t *, uint8_t *caller_sp, bool switch_stack, BackRegisters *out_reg)
454
+ void CallData::Relay(Size idx, uint8_t *sp)
449
455
  {
456
+ uint8_t *caller_sp = sp + 48;
457
+ BackRegisters *out_reg = (BackRegisters *)(sp + 16);
458
+
450
459
  if (env.IsExceptionPending()) [[unlikely]]
451
460
  return;
452
461
 
@@ -685,15 +694,8 @@ void CallData::Relay(Size idx, uint8_t *, uint8_t *caller_sp, bool switch_stack,
685
694
 
686
695
  const TypeInfo *type = proto->ret.type;
687
696
 
688
- // Make the call
689
- napi_value ret;
690
- if (switch_stack) {
691
- ret = CallSwitchStack(&func, (size_t)arguments.len, arguments.data, old_sp, &mem->stack,
692
- [](Napi::Function *func, size_t argc, napi_value *argv) { return (napi_value)func->Call(argv[0], argc - 1, argv + 1); });
693
- } else {
694
- ret = (napi_value)func.Call(arguments[0], arguments.len - 1, arguments.data + 1);
695
- }
696
- Napi::Value value(env, ret);
697
+ // Make the call!
698
+ Napi::Value value = func.Call(arguments[0], arguments.len - 1, arguments.data + 1);
697
699
 
698
700
  if (env.IsExceptionPending()) [[unlikely]]
699
701
  return;
@@ -81,7 +81,8 @@ ForwardCallRD:
81
81
  # ----------------------------
82
82
 
83
83
  .global RelayCallback
84
- .global CallSwitchStack
84
+ .global SwitchAndRelay
85
+ .global RelayDirect
85
86
 
86
87
  # Call the C function RelayCallback with the following arguments:
87
88
  # static trampoline ID, the current stack pointer, a pointer to the stack arguments of this call,
@@ -98,10 +99,6 @@ ForwardCallRD:
98
99
  .cfi_def_cfa esp, 48
99
100
  movl $\id, 0(%esp)
100
101
  movl %esp, 4(%esp)
101
- leal 48(%esp), %eax
102
- movl %eax, 8(%esp)
103
- leal 16(%esp), %eax
104
- movl %eax, 12(%esp)
105
102
  call GetEIP
106
103
  addl $_GLOBAL_OFFSET_TABLE_, %ecx
107
104
  call *RelayCallback@GOT(%ecx)
@@ -126,10 +123,6 @@ ForwardCallRD:
126
123
  .cfi_def_cfa esp, 48
127
124
  movl $\id, 0(%esp)
128
125
  movl %esp, 4(%esp)
129
- leal 48(%esp), %eax
130
- movl %eax, 8(%esp)
131
- leal 16(%esp), %eax
132
- movl %eax, 12(%esp)
133
126
  call GetEIP
134
127
  addl $_GLOBAL_OFFSET_TABLE_, %ecx
135
128
  call *RelayCallback@GOT(%ecx)
@@ -151,15 +144,11 @@ ForwardCallRD:
151
144
  .cfi_endproc
152
145
  .endm
153
146
 
154
- GetEIP:
155
- movl (%esp), %ecx
156
- ret
157
-
158
147
  # When a callback is relayed, Koffi will call into Node.js and V8 to execute Javascript.
159
148
  # The problem is that we're still running on the separate Koffi stack, and V8 will
160
149
  # probably misdetect this as a "stack overflow". We have to restore the old
161
150
  # stack pointer, call Node.js/V8 and go back to ours.
162
- CallSwitchStack:
151
+ SwitchAndRelay:
163
152
  .cfi_startproc
164
153
  .cfi_def_cfa esp, 4
165
154
  ENDBR32
@@ -167,21 +156,22 @@ CallSwitchStack:
167
156
  .cfi_def_cfa esp, 8
168
157
  movl %esp, %ebp
169
158
  .cfi_def_cfa ebp, 8
170
- movl 28(%esp), %edx
171
159
  movl 24(%esp), %ecx
172
160
  movl %esp, %eax
173
161
  subl 0(%ecx), %eax
174
162
  andl $-16, %eax
175
163
  movl %eax, 4(%ecx)
176
164
  movl 20(%esp), %esp
177
- subl $28, %esp
165
+ subl $24, %esp
178
166
  movl 8(%ebp), %eax
179
167
  movl %eax, 0(%esp)
180
168
  movl 12(%ebp), %eax
181
169
  movl %eax, 4(%esp)
182
170
  movl 16(%ebp), %eax
183
171
  movl %eax, 8(%esp)
184
- call *%edx
172
+ call GetEIP
173
+ addl $_GLOBAL_OFFSET_TABLE_, %ecx
174
+ call *RelayDirect@GOT(%ecx)
185
175
  mov %ebp, %esp
186
176
  .cfi_def_cfa esp, 8
187
177
  pop %ebp
@@ -189,6 +179,10 @@ CallSwitchStack:
189
179
  ret
190
180
  .cfi_endproc
191
181
 
182
+ GetEIP:
183
+ movl (%esp), %ecx
184
+ ret
185
+
192
186
  # Trampolines
193
187
  # ----------------------------
194
188
 
@@ -80,7 +80,8 @@ ForwardCallRD endp
80
80
  ; ----------------------------
81
81
 
82
82
  extern RelayCallback : PROC
83
- public CallSwitchStack
83
+ public SwitchAndRelay
84
+ extern RelayDirect : PROC
84
85
 
85
86
  ; Call the C function RelayCallback with the following arguments:
86
87
  ; static trampoline ID, the current stack pointer, a pointer to the stack arguments of this call,
@@ -94,10 +95,6 @@ trampoline macro ID
94
95
  sub esp, 44
95
96
  mov dword ptr [esp+0], ID
96
97
  mov dword ptr [esp+4], esp
97
- lea eax, dword ptr [esp+48]
98
- mov dword ptr [esp+8], eax
99
- lea eax, dword ptr [esp+16]
100
- mov dword ptr [esp+12], eax
101
98
  call RelayCallback
102
99
  mov edx, dword ptr [esp+44]
103
100
  mov ecx, dword ptr [esp+36]
@@ -117,10 +114,6 @@ trampoline_vec macro ID
117
114
  sub esp, 44
118
115
  mov dword ptr [esp+0], ID
119
116
  mov dword ptr [esp+4], esp
120
- lea eax, dword ptr [esp+48]
121
- mov dword ptr [esp+8], eax
122
- lea eax, dword ptr [esp+16]
123
- mov dword ptr [esp+12], eax
124
117
  call RelayCallback
125
118
  mov edx, dword ptr [esp+44]
126
119
  mov ecx, dword ptr [esp+36]
@@ -141,29 +134,28 @@ endm
141
134
  ; The problem is that we're still running on the separate Koffi stack, and V8 will
142
135
  ; probably misdetect this as a "stack overflow". We have to restore the old
143
136
  ; stack pointer, call Node.js/V8 and go back to ours.
144
- CallSwitchStack proc
137
+ SwitchAndRelay proc
145
138
  endbr32
146
139
  push ebp
147
140
  mov ebp, esp
148
- mov edx, dword ptr [esp+28]
149
141
  mov ecx, dword ptr [esp+24]
150
142
  mov eax, esp
151
143
  sub eax, dword ptr [ecx+0]
152
144
  and eax, -16
153
145
  mov dword ptr [ecx+4], eax
154
146
  mov esp, dword ptr [esp+20]
155
- sub esp, 28
147
+ sub esp, 24
156
148
  mov eax, dword ptr [ebp+8]
157
149
  mov dword ptr [esp+0], eax
158
150
  mov eax, dword ptr [ebp+12]
159
151
  mov dword ptr [esp+4], eax
160
152
  mov eax, dword ptr [ebp+16]
161
153
  mov dword ptr [esp+8], eax
162
- call edx
154
+ call RelayDirect
163
155
  mov esp, ebp
164
156
  pop ebp
165
157
  ret
166
- CallSwitchStack endp
158
+ SwitchAndRelay endp
167
159
 
168
160
  ; Trampolines
169
161
  ; ----------------------------
@@ -12,15 +12,12 @@ namespace K {
12
12
 
13
13
  struct RelayContext {
14
14
  CallData *call;
15
- bool dispose_call;
16
15
 
17
16
  Size idx;
18
- uint8_t *own_sp;
19
- uint8_t *caller_sp;
20
- BackRegisters *out_reg;
17
+ uint8_t *sp;
21
18
 
22
- std::mutex mutex;
23
- std::condition_variable cv;
19
+ std::mutex mutex = {};
20
+ std::condition_variable cv = {};
24
21
  bool done = false;
25
22
  };
26
23
 
@@ -75,50 +72,26 @@ void CallData::Dispose()
75
72
  instance = nullptr;
76
73
  }
77
74
 
78
- void CallData::RelaySafe(Size idx, uint8_t *own_sp, uint8_t *caller_sp, bool outside_call, BackRegisters *out_reg)
75
+ void CallData::RelayAsync(Size idx, uint8_t *sp)
79
76
  {
80
- if (std::this_thread::get_id() != instance->main_thread_id) {
81
- // JS/V8 is single-threaded, and runs on main_thread_id. Forward the call
82
- // to the JS event loop.
77
+ // JS/V8 is single-threaded, and runs on main_thread_id. Forward the call
78
+ // to the JS event loop.
83
79
 
84
- RelayContext ctx;
80
+ RelayContext ctx = {
81
+ .call = this,
82
+ .idx = idx,
83
+ .sp = sp
84
+ };
85
85
 
86
- ctx.call = this;
87
- ctx.dispose_call = outside_call;
88
- ctx.idx = idx;
89
- ctx.own_sp = own_sp;
90
- ctx.caller_sp = caller_sp;
91
- ctx.out_reg = out_reg;
86
+ napi_call_threadsafe_function(instance->broker, &ctx, napi_tsfn_blocking);
92
87
 
93
- napi_call_threadsafe_function(instance->broker, &ctx, napi_tsfn_blocking);
94
-
95
- // Wait until it executes
96
- std::unique_lock<std::mutex> lock(ctx.mutex);
97
- while (!ctx.done) {
98
- ctx.cv.wait(lock);
99
- }
100
- } else {
101
- Napi::HandleScope scope(env);
102
- Relay(idx, own_sp, caller_sp, !outside_call, out_reg);
88
+ // Wait until it executes
89
+ std::unique_lock<std::mutex> lock(ctx.mutex);
90
+ while (!ctx.done) {
91
+ ctx.cv.wait(lock);
103
92
  }
104
93
  }
105
94
 
106
- void CallData::RelayAsync(napi_env, napi_value, void *, void *udata)
107
- {
108
- RelayContext *ctx = (RelayContext *)udata;
109
-
110
- ctx->call->Relay(ctx->idx, ctx->own_sp, ctx->caller_sp, false, ctx->out_reg);
111
-
112
- if (ctx->dispose_call) {
113
- ctx->call->Dispose();
114
- }
115
-
116
- // We're done!
117
- std::lock_guard<std::mutex> lock(ctx->mutex);
118
- ctx->done = true;
119
- ctx->cv.notify_one();
120
- }
121
-
122
95
  bool CallData::PushString(Napi::Value value, int directions, const char **out_str)
123
96
  {
124
97
  // Fast path
@@ -189,7 +162,7 @@ Size CallData::PushStringValue(Napi::Value value, const char **out_str)
189
162
  K_ASSERT(status == napi_ok);
190
163
 
191
164
  len++;
192
- buf = AllocateSpan<char>(&call_alloc, (Size)len);
165
+ buf = AllocateSpan<char>(&alloc, (Size)len);
193
166
 
194
167
  status = napi_get_value_string_utf8(env, value, buf.ptr, (size_t)buf.len, &len);
195
168
  K_ASSERT(status == napi_ok);
@@ -224,7 +197,7 @@ Size CallData::PushString16Value(Napi::Value value, const char16_t **out_str16)
224
197
  K_ASSERT(status == napi_ok);
225
198
 
226
199
  len++;
227
- buf = AllocateSpan<char16_t>(&call_alloc, (Size)len);
200
+ buf = AllocateSpan<char16_t>(&alloc, (Size)len);
228
201
 
229
202
  status = napi_get_value_string_utf16(env, value, buf.ptr, (size_t)buf.len, &len);
230
203
  K_ASSERT(status == napi_ok);
@@ -255,7 +228,7 @@ Size CallData::PushString32Value(Napi::Value value, const char32_t **out_str32)
255
228
  mem->heap.ptr += buf16.len * 4;
256
229
  mem->heap.len -= buf16.len * 4;
257
230
  } else {
258
- buf = AllocateSpan<char32_t>(&call_alloc, buf16.len);
231
+ buf = AllocateSpan<char32_t>(&alloc, buf16.len);
259
232
  }
260
233
 
261
234
  Size j = 0;
@@ -1315,4 +1288,22 @@ void CallData::PopOutArguments()
1315
1288
  }
1316
1289
  }
1317
1290
 
1291
+ void PerformAsyncRelay(napi_env, napi_value, void *, void *udata)
1292
+ {
1293
+ RelayContext *ctx = (RelayContext *)udata;
1294
+ CallData *call = ctx->call;
1295
+
1296
+ call->Relay(ctx->idx, ctx->sp);
1297
+
1298
+ // This CallData was created artificially just to perform the callback. Which means the
1299
+ // creator may not run on the main thread, and cannot properly destroy it, because some
1300
+ // members are managed by Node.
1301
+ call->Dispose();
1302
+
1303
+ // We're done!
1304
+ std::lock_guard<std::mutex> lock(ctx->mutex);
1305
+ ctx->done = true;
1306
+ ctx->cv.notify_one();
1307
+ }
1308
+
1318
1309
  }
@@ -17,7 +17,7 @@ struct BackRegisters;
17
17
 
18
18
  // I'm not sure why the alignas(8), because alignof(CallData) is 8 without it.
19
19
  // But on Windows i386, without it, the alignment may not be correct (compiler bug?).
20
- class alignas(8) CallData {
20
+ struct alignas(8) CallData {
21
21
  struct OutArgument {
22
22
  enum class Kind {
23
23
  Array,
@@ -66,7 +66,7 @@ class alignas(8) CallData {
66
66
  LocalArray<int16_t, 16> used_trampolines;
67
67
  HeapArray<OutArgument> out_arguments;
68
68
 
69
- BlockAllocator call_alloc;
69
+ BlockAllocator alloc;
70
70
 
71
71
  public:
72
72
  CallData(Napi::Env env, InstanceData *instance, InstanceMemory *mem);
@@ -90,9 +90,8 @@ public:
90
90
 
91
91
  #undef INLINE_IF_UNITY
92
92
 
93
- void Relay(Size idx, uint8_t *own_sp, uint8_t *caller_sp, bool switch_stack, BackRegisters *out_reg);
94
- void RelaySafe(Size idx, uint8_t *own_sp, uint8_t *caller_sp, bool outside_call, BackRegisters *out_reg);
95
- static void RelayAsync(napi_env, napi_value, void *, void *udata);
93
+ void Relay(Size idx, uint8_t *sp);
94
+ void RelayAsync(Size idx, uint8_t *sp);
96
95
 
97
96
  void DumpForward(const FunctionInfo *func) const;
98
97
 
@@ -112,9 +111,6 @@ public:
112
111
 
113
112
  void *ReserveTrampoline(const FunctionInfo *proto, Napi::Function func);
114
113
 
115
- BlockAllocator *GetAllocator() { return &call_alloc; }
116
-
117
- private:
118
114
  template <typename T>
119
115
  bool AllocStack(Size size, Size align, T **out_ptr);
120
116
  template <typename T = uint8_t>
@@ -169,13 +165,15 @@ inline T *CallData::AllocHeap(Size size, Size align)
169
165
  int flags = 0;
170
166
  #endif
171
167
 
172
- ptr = (uint8_t *)AllocateRaw(&call_alloc, size + align, flags);
168
+ ptr = (uint8_t *)AllocateRaw(&alloc, size + align, flags);
173
169
  ptr = AlignUp(ptr, align);
174
170
 
175
171
  return ptr;
176
172
  }
177
173
  }
178
174
 
175
+ void PerformAsyncRelay(napi_env env, napi_value callback, void *ctx, void *udata);
176
+
179
177
  void *GetTrampoline(int16_t idx, const FunctionInfo *proto);
180
178
 
181
179
  }