koffi 1.1.5 → 1.2.0-alpha.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.
package/src/abi_x86_fwd.S CHANGED
@@ -1,15 +1,15 @@
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/.
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
13
 
14
14
  .global ForwardCallG
15
15
  .global ForwardCallF
@@ -18,17 +18,20 @@
18
18
  .global ForwardCallRF
19
19
  .global ForwardCallRD
20
20
 
21
- // Copy function pointer to EAX, in order to save it through argument forwarding.
22
- // Save ESP in EBX (non-volatile), and use carefully assembled stack provided by caller.
21
+ # Copy function pointer to EAX, in order to save it through argument forwarding.
22
+ # Also make a copy of the SP to CallData::old_sp because the callback system might need it.
23
+ # Save ESP in EBX (non-volatile), and use carefully assembled stack provided by caller.
23
24
  .macro prologue
24
25
  .cfi_startproc
25
26
  .cfi_def_cfa esp, 4
26
27
  endbr32
27
28
  push %ebx
28
29
  .cfi_def_cfa esp, 8
29
- movl 8(%esp), %eax
30
30
  movl %esp, %ebx
31
31
  .cfi_def_cfa ebx, 8
32
+ movl 16(%esp), %eax
33
+ movl %esp, 0(%eax)
34
+ movl 8(%esp), %eax
32
35
  movl 12(%esp), %esp
33
36
  .endm
34
37
 
@@ -38,9 +41,9 @@
38
41
  addl $16, %esp
39
42
  .endm
40
43
 
41
- // Call native function.
42
- // Once done, restore normal stack pointer and return.
43
- // The return value is passed back untouched.
44
+ # Call native function.
45
+ # Once done, restore normal stack pointer and return.
46
+ # The return value is passed back untouched.
44
47
  .macro epilogue
45
48
  call *%eax
46
49
  movl %ebx, %esp
@@ -76,3 +79,205 @@ ForwardCallRD:
76
79
  prologue
77
80
  fastcall
78
81
  epilogue
82
+
83
+ # Callback trampolines
84
+ # ----------------------------
85
+
86
+ .global Trampoline0
87
+ .global Trampoline1
88
+ .global Trampoline2
89
+ .global Trampoline3
90
+ .global Trampoline4
91
+ .global Trampoline5
92
+ .global Trampoline6
93
+ .global Trampoline7
94
+ .global Trampoline8
95
+ .global Trampoline9
96
+ .global Trampoline10
97
+ .global Trampoline11
98
+ .global Trampoline12
99
+ .global Trampoline13
100
+ .global Trampoline14
101
+ .global Trampoline15
102
+ .global TrampolineX0
103
+ .global TrampolineX1
104
+ .global TrampolineX2
105
+ .global TrampolineX3
106
+ .global TrampolineX4
107
+ .global TrampolineX5
108
+ .global TrampolineX6
109
+ .global TrampolineX7
110
+ .global TrampolineX8
111
+ .global TrampolineX9
112
+ .global TrampolineX10
113
+ .global TrampolineX11
114
+ .global TrampolineX12
115
+ .global TrampolineX13
116
+ .global TrampolineX14
117
+ .global TrampolineX15
118
+ .global RelayCallBack
119
+ .global CallSwitchStack
120
+
121
+ # Call the C function RelayCallBack with the following arguments:
122
+ # static trampoline ID, the current stack pointer, a pointer to the stack arguments of this call,
123
+ # and a pointer to a struct that will contain the result registers.
124
+ # After the call, simply load these registers from the output struct.
125
+ .macro trampoline id
126
+ .cfi_startproc
127
+ .cfi_def_cfa esp, 4
128
+ endbr32
129
+ sub $44, %esp
130
+ .cfi_def_cfa esp, 48
131
+ movl $\id, 0(%esp)
132
+ movl %esp, 4(%esp)
133
+ leal 48(%esp), %eax
134
+ movl %eax, 8(%esp)
135
+ leal 16(%esp), %eax
136
+ movl %eax, 12(%esp)
137
+ call GetEIP
138
+ addl $_GLOBAL_OFFSET_TABLE_, %ecx
139
+ call *RelayCallBack@GOT(%ecx)
140
+ movl 16(%esp), %eax
141
+ movl 20(%esp), %edx
142
+ addl $44, %esp
143
+ .cfi_def_cfa esp, 4
144
+ ret
145
+ .cfi_endproc
146
+ .endm
147
+
148
+ # This version also loads the x87 stack with the result, if need be.
149
+ # We have to branch to avoid x87 stack imbalance.
150
+ .macro trampoline_x87 id
151
+ .cfi_startproc
152
+ .cfi_def_cfa esp, 4
153
+ endbr32
154
+ sub $44, %esp
155
+ .cfi_def_cfa esp, 48
156
+ movl $\id, 0(%esp)
157
+ movl %esp, 4(%esp)
158
+ leal 48(%esp), %eax
159
+ movl %eax, 8(%esp)
160
+ leal 16(%esp), %eax
161
+ movl %eax, 12(%esp)
162
+ call GetEIP
163
+ addl $_GLOBAL_OFFSET_TABLE_, %ecx
164
+ call *RelayCallBack@GOT(%ecx)
165
+ cmpb $1, 36(%esp)
166
+ je 2f
167
+ 1:
168
+ flds 32(%esp)
169
+ jmp 3f
170
+ 2:
171
+ fldl 24(%esp)
172
+ 3:
173
+ movl 16(%esp), %eax
174
+ movl 20(%esp), %edx
175
+ addl $44, %esp
176
+ .cfi_def_cfa esp, 4
177
+ ret
178
+ .cfi_endproc
179
+ .endm
180
+
181
+ GetEIP:
182
+ movl (%esp), %ecx
183
+ ret
184
+
185
+ Trampoline0:
186
+ trampoline 0
187
+ Trampoline1:
188
+ trampoline 1
189
+ Trampoline2:
190
+ trampoline 2
191
+ Trampoline3:
192
+ trampoline 3
193
+ Trampoline4:
194
+ trampoline 4
195
+ Trampoline5:
196
+ trampoline 5
197
+ Trampoline6:
198
+ trampoline 6
199
+ Trampoline7:
200
+ trampoline 7
201
+ Trampoline8:
202
+ trampoline 8
203
+ Trampoline9:
204
+ trampoline 9
205
+ Trampoline10:
206
+ trampoline 10
207
+ Trampoline11:
208
+ trampoline 11
209
+ Trampoline12:
210
+ trampoline 12
211
+ Trampoline13:
212
+ trampoline 13
213
+ Trampoline14:
214
+ trampoline 14
215
+ Trampoline15:
216
+ trampoline 15
217
+
218
+ TrampolineX0:
219
+ trampoline_x87 0
220
+ TrampolineX1:
221
+ trampoline_x87 1
222
+ TrampolineX2:
223
+ trampoline_x87 2
224
+ TrampolineX3:
225
+ trampoline_x87 3
226
+ TrampolineX4:
227
+ trampoline_x87 4
228
+ TrampolineX5:
229
+ trampoline_x87 5
230
+ TrampolineX6:
231
+ trampoline_x87 6
232
+ TrampolineX7:
233
+ trampoline_x87 7
234
+ TrampolineX8:
235
+ trampoline_x87 8
236
+ TrampolineX9:
237
+ trampoline_x87 9
238
+ TrampolineX10:
239
+ trampoline_x87 10
240
+ TrampolineX11:
241
+ trampoline_x87 11
242
+ TrampolineX12:
243
+ trampoline_x87 12
244
+ TrampolineX13:
245
+ trampoline_x87 13
246
+ TrampolineX14:
247
+ trampoline_x87 14
248
+ TrampolineX15:
249
+ trampoline_x87 15
250
+
251
+ # When a callback is relayed, Koffi will call into Node.js and V8 to execute Javascript.
252
+ # The propblem is that we're still running on the separate Koffi stack, and V8 will
253
+ # preobably misdetect this as a "stack overflow". We have to restore the old
254
+ # stack pointer, call Node.js/V8 and go back to ours.
255
+ CallSwitchStack:
256
+ .cfi_startproc
257
+ .cfi_def_cfa esp, 4
258
+ endbr32
259
+ push %ebx
260
+ .cfi_def_cfa esp, 8
261
+ movl %esp, %ebx
262
+ .cfi_def_cfa ebx, 8
263
+ movl 28(%esp), %edx
264
+ movl 24(%esp), %ecx
265
+ movl %esp, %eax
266
+ subl 0(%ecx), %eax
267
+ andl $-16, %eax
268
+ movl %eax, 4(%ecx)
269
+ movl 20(%esp), %esp
270
+ subl $16, %esp
271
+ movl 8(%ebx), %eax
272
+ movl %eax, 0(%esp)
273
+ movl 12(%ebx), %eax
274
+ movl %eax, 4(%esp)
275
+ movl 16(%ebx), %eax
276
+ movl %eax, 8(%esp)
277
+ call *%edx
278
+ mov %ebx, %esp
279
+ .cfi_def_cfa esp, 8
280
+ pop %ebx
281
+ .cfi_def_cfa esp, 4
282
+ ret
283
+ .cfi_endproc
@@ -11,6 +11,9 @@
11
11
  ; You should have received a copy of the GNU Affero General Public License
12
12
  ; along with this program. If not, see https://www.gnu.org/licenses/.
13
13
 
14
+ ; Forward
15
+ ; ----------------------------
16
+
14
17
  public ForwardCallG
15
18
  public ForwardCallF
16
19
  public ForwardCallD
@@ -22,11 +25,14 @@ public ForwardCallRD
22
25
  .code
23
26
 
24
27
  ; Copy function pointer to EAX, in order to save it through argument forwarding.
28
+ ; Also make a copy of the SP to CallData::old_sp because the callback system might need it.
25
29
  ; Save ESP in EBX (non-volatile), and use carefully assembled stack provided by caller.
26
30
  prologue macro
27
31
  endbr32
28
32
  push ebx
29
33
  mov ebx, esp
34
+ mov eax, dword ptr [esp+16]
35
+ mov dword ptr [eax+0], esp
30
36
  mov eax, dword ptr [esp+8]
31
37
  mov esp, dword ptr [esp+12]
32
38
  endm
@@ -80,4 +86,216 @@ ForwardCallRD proc
80
86
  epilogue
81
87
  ForwardCallRD endp
82
88
 
89
+ ; Callback trampolines
90
+ ; ----------------------------
91
+
92
+ public Trampoline0
93
+ public Trampoline1
94
+ public Trampoline2
95
+ public Trampoline3
96
+ public Trampoline4
97
+ public Trampoline5
98
+ public Trampoline6
99
+ public Trampoline7
100
+ public Trampoline8
101
+ public Trampoline9
102
+ public Trampoline10
103
+ public Trampoline11
104
+ public Trampoline12
105
+ public Trampoline13
106
+ public Trampoline14
107
+ public Trampoline15
108
+ public TrampolineX0
109
+ public TrampolineX1
110
+ public TrampolineX2
111
+ public TrampolineX3
112
+ public TrampolineX4
113
+ public TrampolineX5
114
+ public TrampolineX6
115
+ public TrampolineX7
116
+ public TrampolineX8
117
+ public TrampolineX9
118
+ public TrampolineX10
119
+ public TrampolineX11
120
+ public TrampolineX12
121
+ public TrampolineX13
122
+ public TrampolineX14
123
+ public TrampolineX15
124
+ extern RelayCallBack : PROC
125
+ public CallSwitchStack
126
+
127
+ ; Call the C function RelayCallBack with the following arguments:
128
+ ; static trampoline ID, the current stack pointer, a pointer to the stack arguments of this call,
129
+ ; and a pointer to a struct that will contain the result registers.
130
+ ; After the call, simply load these registers from the output struct.
131
+ trampoline macro ID
132
+ endbr32
133
+ sub esp, 44
134
+ mov dword ptr [esp+0], ID
135
+ mov dword ptr [esp+4], esp
136
+ lea eax, dword ptr [esp+48]
137
+ mov dword ptr [esp+8], eax
138
+ lea eax, dword ptr [esp+16]
139
+ mov dword ptr [esp+12], eax
140
+ call RelayCallBack
141
+ mov eax, dword ptr[esp+16]
142
+ mov edx, dword ptr[esp+20]
143
+ add esp, 44
144
+ ret
145
+ endm
146
+
147
+ ; This version also loads the x87 stack with the result, if need be.
148
+ ; We have to branch to avoid x87 stack imbalance.
149
+ trampoline_x87 macro ID
150
+ local l1, l2, l3
151
+
152
+ endbr32
153
+ sub esp, 44
154
+ mov dword ptr [esp+0], ID
155
+ mov dword ptr [esp+4], esp
156
+ lea eax, dword ptr [esp+48]
157
+ mov dword ptr [esp+8], eax
158
+ lea eax, dword ptr [esp+16]
159
+ mov dword ptr [esp+12], eax
160
+ call RelayCallBack
161
+ cmp byte ptr[esp+36], 1
162
+ je l2
163
+ l1:
164
+ fld dword ptr [esp+32]
165
+ jmp l3
166
+ l2:
167
+ fld qword ptr [esp+24]
168
+ l3:
169
+ mov eax, dword ptr[esp+16]
170
+ mov edx, dword ptr[esp+20]
171
+ add esp, 44
172
+ ret
173
+ endm
174
+
175
+ Trampoline0 proc
176
+ trampoline 0
177
+ Trampoline0 endp
178
+ Trampoline1 proc
179
+ trampoline 1
180
+ Trampoline1 endp
181
+ Trampoline2 proc
182
+ trampoline 2
183
+ Trampoline2 endp
184
+ Trampoline3 proc
185
+ trampoline 3
186
+ Trampoline3 endp
187
+ Trampoline4 proc
188
+ trampoline 4
189
+ Trampoline4 endp
190
+ Trampoline5 proc
191
+ trampoline 5
192
+ Trampoline5 endp
193
+ Trampoline6 proc
194
+ trampoline 6
195
+ Trampoline6 endp
196
+ Trampoline7 proc
197
+ trampoline 7
198
+ Trampoline7 endp
199
+ Trampoline8 proc
200
+ trampoline 8
201
+ Trampoline8 endp
202
+ Trampoline9 proc
203
+ trampoline 9
204
+ Trampoline9 endp
205
+ Trampoline10 proc
206
+ trampoline 10
207
+ Trampoline10 endp
208
+ Trampoline11 proc
209
+ trampoline 11
210
+ Trampoline11 endp
211
+ Trampoline12 proc
212
+ trampoline 12
213
+ Trampoline12 endp
214
+ Trampoline13 proc
215
+ trampoline 13
216
+ Trampoline13 endp
217
+ Trampoline14 proc
218
+ trampoline 14
219
+ Trampoline14 endp
220
+ Trampoline15 proc
221
+ trampoline 15
222
+ Trampoline15 endp
223
+
224
+ TrampolineX0 proc
225
+ trampoline_x87 0
226
+ TrampolineX0 endp
227
+ TrampolineX1 proc
228
+ trampoline_x87 1
229
+ TrampolineX1 endp
230
+ TrampolineX2 proc
231
+ trampoline_x87 2
232
+ TrampolineX2 endp
233
+ TrampolineX3 proc
234
+ trampoline_x87 3
235
+ TrampolineX3 endp
236
+ TrampolineX4 proc
237
+ trampoline_x87 4
238
+ TrampolineX4 endp
239
+ TrampolineX5 proc
240
+ trampoline_x87 5
241
+ TrampolineX5 endp
242
+ TrampolineX6 proc
243
+ trampoline_x87 6
244
+ TrampolineX6 endp
245
+ TrampolineX7 proc
246
+ trampoline_x87 7
247
+ TrampolineX7 endp
248
+ TrampolineX8 proc
249
+ trampoline_x87 8
250
+ TrampolineX8 endp
251
+ TrampolineX9 proc
252
+ trampoline_x87 9
253
+ TrampolineX9 endp
254
+ TrampolineX10 proc
255
+ trampoline_x87 10
256
+ TrampolineX10 endp
257
+ TrampolineX11 proc
258
+ trampoline_x87 11
259
+ TrampolineX11 endp
260
+ TrampolineX12 proc
261
+ trampoline_x87 12
262
+ TrampolineX12 endp
263
+ TrampolineX13 proc
264
+ trampoline_x87 13
265
+ TrampolineX13 endp
266
+ TrampolineX14 proc
267
+ trampoline_x87 14
268
+ TrampolineX14 endp
269
+ TrampolineX15 proc
270
+ trampoline_x87 15
271
+ TrampolineX15 endp
272
+
273
+ ; When a callback is relayed, Koffi will call into Node.js and V8 to execute Javascript.
274
+ ; The propblem is that we're still running on the separate Koffi stack, and V8 will
275
+ ; preobably misdetect this as a "stack overflow". We have to restore the old
276
+ ; stack pointer, call Node.js/V8 and go back to ours.
277
+ CallSwitchStack proc
278
+ endbr32
279
+ push ebx
280
+ mov ebx, esp
281
+ mov edx, dword ptr [esp+28]
282
+ mov ecx, dword ptr [esp+24]
283
+ mov eax, esp
284
+ sub eax, dword ptr [ecx+0]
285
+ and eax, -16
286
+ mov dword ptr [ecx+4], eax
287
+ mov esp, dword ptr [esp+20]
288
+ sub esp, 28
289
+ mov eax, dword ptr [ebx+8]
290
+ mov dword ptr [esp+0], eax
291
+ mov eax, dword ptr [ebx+12]
292
+ mov dword ptr [esp+4], eax
293
+ mov eax, dword ptr [ebx+16]
294
+ mov dword ptr [esp+8], eax
295
+ call edx
296
+ mov esp, ebx
297
+ pop ebx
298
+ ret
299
+ CallSwitchStack endp
300
+
83
301
  end
package/src/call.cc CHANGED
@@ -35,7 +35,9 @@ CallData::~CallData()
35
35
  mem->stack = old_stack_mem;
36
36
  mem->heap = old_heap_mem;
37
37
 
38
- if (!--mem->depth && mem->temporary) {
38
+ instance->free_trampolines |= used_trampolines;
39
+
40
+ if (--mem->depth && mem->temporary) {
39
41
  delete mem;
40
42
  }
41
43
  }
@@ -237,14 +239,16 @@ bool CallData::PushObject(const Napi::Object &obj, const TypeInfo *type, uint8_t
237
239
  *(const char16_t **)dest = str16;
238
240
  } break;
239
241
  case PrimitiveKind::Pointer: {
240
- if (RG_UNLIKELY(!CheckValueTag(instance, value, member.type))) {
242
+ if (CheckValueTag(instance, value, member.type)) {
243
+ Napi::External external = value.As<Napi::External<void>>();
244
+ void *ptr = external.Data();
245
+ *(void **)dest = ptr;
246
+ } else if (IsNullOrUndefined(value)) {
247
+ *(void **)dest = nullptr;
248
+ } else {
241
249
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for member '%2', expected %3", GetValueType(instance, value), member.name, member.type->name);
242
250
  return false;
243
251
  }
244
-
245
- Napi::External external = value.As<Napi::External<void>>();
246
- void *ptr = external.Data();
247
- *(void **)dest = ptr;
248
252
  } break;
249
253
  case PrimitiveKind::Record: {
250
254
  if (RG_UNLIKELY(!IsObject(value))) {
@@ -284,6 +288,29 @@ bool CallData::PushObject(const Napi::Object &obj, const TypeInfo *type, uint8_t
284
288
  double d = CopyNumber<double>(value);
285
289
  *(double *)dest = d;
286
290
  } break;
291
+ case PrimitiveKind::Callback: {
292
+ void *ptr;
293
+
294
+ if (value.IsFunction()) {
295
+ Napi::Function func = value.As<Napi::Function>();
296
+
297
+ Size idx = ReserveTrampoline(member.type->proto, func);
298
+ if (RG_UNLIKELY(idx < 0))
299
+ return false;
300
+
301
+ ptr = GetTrampoline(idx, member.type->proto);
302
+ } else if (CheckValueTag(instance, value, member.type)) {
303
+ Napi::External external = value.As<Napi::External<void>>();
304
+ ptr = external.Data();
305
+ } else if (IsNullOrUndefined(value)) {
306
+ ptr = nullptr;
307
+ } else {
308
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for member '%2', expected %3", GetValueType(instance, value), member.name, member.type->name);
309
+ return false;
310
+ }
311
+
312
+ *(void **)dest = ptr;
313
+ } break;
287
314
  }
288
315
 
289
316
  dest += member.type->size;
@@ -400,9 +427,13 @@ bool CallData::PushArray(const Napi::Value &obj, const TypeInfo *type, uint8_t *
400
427
  });
401
428
  } break;
402
429
  case PrimitiveKind::Pointer: {
403
- PUSH_ARRAY(CheckValueTag(instance, value, type->ref), type->ref->name, {
404
- Napi::External external = value.As<Napi::External<void>>();
405
- *(void **)dest = external.Data();
430
+ PUSH_ARRAY(CheckValueTag(instance, value, type->ref) || IsNullOrUndefined(value), type->ref->name, {
431
+ if (!IsNullOrUndefined(value)) {
432
+ Napi::External external = value.As<Napi::External<void>>();
433
+ *(void **)dest = external.Data();
434
+ } else {
435
+ *(void **)dest = nullptr;
436
+ }
406
437
  });
407
438
  } break;
408
439
  case PrimitiveKind::Record: {
@@ -431,6 +462,38 @@ bool CallData::PushArray(const Napi::Value &obj, const TypeInfo *type, uint8_t *
431
462
  *(double *)dest = d;
432
463
  });
433
464
  } break;
465
+ case PrimitiveKind::Callback: {
466
+ for (uint32_t i = 0; i < len; i++) {
467
+ Napi::Value value = array[i];
468
+
469
+ int16_t align = std::max(type->ref->align, realign);
470
+ dest = AlignUp(dest, align);
471
+
472
+ void *ptr;
473
+
474
+ if (value.IsFunction()) {
475
+ Napi::Function func = value.As<Napi::Function>();
476
+
477
+ Size idx = ReserveTrampoline(type->ref->proto, func);
478
+ if (RG_UNLIKELY(idx < 0))
479
+ return false;
480
+
481
+ ptr = GetTrampoline(idx, type->ref->proto);
482
+ } else if (CheckValueTag(instance, value, type->ref)) {
483
+ Napi::External external = value.As<Napi::External<void>>();
484
+ ptr = external.Data();
485
+ } else if (IsNullOrUndefined(value)) {
486
+ ptr = nullptr;
487
+ } else {
488
+ ThrowError<Napi::TypeError>(env, "Unexpected value %1 in array, expected %2", GetValueType(instance, value), type->ref->name);
489
+ return false;
490
+ }
491
+
492
+ *(void **)dest = ptr;
493
+
494
+ dest += type->ref->size;
495
+ }
496
+ } break;
434
497
  }
435
498
 
436
499
  #undef PUSH_ARRAY
@@ -493,8 +556,29 @@ bool CallData::PushArray(const Napi::Value &obj, const TypeInfo *type, uint8_t *
493
556
  return true;
494
557
  }
495
558
 
559
+ Size CallData::ReserveTrampoline(const FunctionInfo *proto, Napi::Function func)
560
+ {
561
+ uint32_t idx = CountTrailingZeros(instance->free_trampolines);
562
+
563
+ if (RG_UNLIKELY(idx >= MaxTrampolines)) {
564
+ ThrowError<Napi::Error>(env, "Too many callbacks are in use (max = %1)", MaxTrampolines);
565
+ return -1;
566
+ }
567
+
568
+ instance->free_trampolines &= ~(1u << idx);
569
+ used_trampolines |= 1u << idx;
570
+
571
+ instance->trampolines[idx].proto = proto;
572
+ instance->trampolines[idx].func = func;
573
+
574
+ return idx;
575
+ }
576
+
496
577
  void CallData::PopObject(Napi::Object obj, const uint8_t *src, const TypeInfo *type, int16_t realign)
497
578
  {
579
+ Napi::Env env = obj.Env();
580
+ InstanceData *instance = env.GetInstanceData<InstanceData>();
581
+
498
582
  RG_ASSERT(type->primitive == PrimitiveKind::Record);
499
583
 
500
584
  for (const RecordMember &member: type->members) {
@@ -548,13 +632,18 @@ void CallData::PopObject(Napi::Object obj, const uint8_t *src, const TypeInfo *t
548
632
  const char16_t *str16 = *(const char16_t **)src;
549
633
  obj.Set(member.name, Napi::String::New(env, str16));
550
634
  } break;
551
- case PrimitiveKind::Pointer: {
635
+ case PrimitiveKind::Pointer:
636
+ case PrimitiveKind::Callback: {
552
637
  void *ptr2 = *(void **)src;
553
638
 
554
- Napi::External<void> external = Napi::External<void>::New(env, ptr2);
555
- SetValueTag(instance, external, member.type);
639
+ if (ptr2) {
640
+ Napi::External<void> external = Napi::External<void>::New(env, ptr2);
641
+ SetValueTag(instance, external, member.type);
556
642
 
557
- obj.Set(member.name, external);
643
+ obj.Set(member.name, external);
644
+ } else {
645
+ obj.Set(member.name, env.Null());
646
+ }
558
647
  } break;
559
648
  case PrimitiveKind::Record: {
560
649
  Napi::Object obj2 = PopObject(src, member.type, realign);
@@ -598,6 +687,8 @@ Size WideStringLength(const char16_t *str16, Size max)
598
687
 
599
688
  Napi::Value CallData::PopArray(const uint8_t *src, const TypeInfo *type, int16_t realign)
600
689
  {
690
+ InstanceData *instance = env.GetInstanceData<InstanceData>();
691
+
601
692
  RG_ASSERT(type->primitive == PrimitiveKind::Array);
602
693
 
603
694
  uint32_t len = type->size / type->ref->size;
@@ -728,14 +819,19 @@ Napi::Value CallData::PopArray(const uint8_t *src, const TypeInfo *type, int16_t
728
819
  array.Set(i, Napi::String::New(env, str16));
729
820
  });
730
821
  } break;
731
- case PrimitiveKind::Pointer: {
822
+ case PrimitiveKind::Pointer:
823
+ case PrimitiveKind::Callback: {
732
824
  POP_ARRAY({
733
825
  void *ptr2 = *(void **)src;
734
826
 
735
- Napi::External<void> external = Napi::External<void>::New(env, ptr2);
736
- SetValueTag(instance, external, type->ref);
827
+ if (ptr2) {
828
+ Napi::External<void> external = Napi::External<void>::New(env, ptr2);
829
+ SetValueTag(instance, external, type->ref);
737
830
 
738
- array.Set(i, external);
831
+ array.Set(i, external);
832
+ } else {
833
+ array.Set(i, env.Null());
834
+ }
739
835
  });
740
836
  } break;
741
837
  case PrimitiveKind::Record: {