koffi 3.0.0-alpha.11 → 3.0.0-alpha.12

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 (38) hide show
  1. package/build/koffi/darwin_arm64/koffi.node +0 -0
  2. package/build/koffi/darwin_x64/koffi.node +0 -0
  3. package/build/koffi/freebsd_arm64/koffi.node +0 -0
  4. package/build/koffi/freebsd_ia32/koffi.node +0 -0
  5. package/build/koffi/freebsd_x64/koffi.node +0 -0
  6. package/build/koffi/linux_arm64/koffi.node +0 -0
  7. package/build/koffi/linux_ia32/koffi.node +0 -0
  8. package/build/koffi/linux_loong64/koffi.node +0 -0
  9. package/build/koffi/linux_riscv64d/koffi.node +0 -0
  10. package/build/koffi/linux_x64/koffi.node +0 -0
  11. package/build/koffi/musl_arm64/koffi.node +0 -0
  12. package/build/koffi/musl_x64/koffi.node +0 -0
  13. package/build/koffi/openbsd_ia32/koffi.node +0 -0
  14. package/build/koffi/openbsd_x64/koffi.node +0 -0
  15. package/build/koffi/win32_ia32/koffi.exp +0 -0
  16. package/build/koffi/win32_ia32/koffi.lib +0 -0
  17. package/build/koffi/win32_ia32/koffi.node +0 -0
  18. package/build/koffi/win32_x64/koffi.exp +0 -0
  19. package/build/koffi/win32_x64/koffi.lib +0 -0
  20. package/build/koffi/win32_x64/koffi.node +0 -0
  21. package/index.d.ts +36 -5
  22. package/lib/native/base/base.cc +3 -0
  23. package/package.json +1 -1
  24. package/src/koffi/index.cjs +1 -1
  25. package/src/koffi/index.js +1 -1
  26. package/src/koffi/indirect.cjs +1 -1
  27. package/src/koffi/indirect.js +1 -1
  28. package/src/koffi/src/abi/arm64.cc +4 -4
  29. package/src/koffi/src/abi/riscv64.cc +11 -8
  30. package/src/koffi/src/abi/x64sysv.cc +27 -20
  31. package/src/koffi/src/abi/x64win.cc +10 -7
  32. package/src/koffi/src/abi/x86.cc +12 -6
  33. package/src/koffi/src/call.cc +67 -20
  34. package/src/koffi/src/call.hh +1 -0
  35. package/src/koffi/src/ffi.cc +45 -15
  36. package/src/koffi/src/ffi.hh +2 -1
  37. package/src/koffi/src/util.cc +9 -0
  38. package/src/koffi/src/util.hh +12 -29
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/index.d.ts CHANGED
@@ -121,18 +121,49 @@ export function register(callback: Function, type: TypeSpec): bigint;
121
121
  export function unregister(callback: bigint): void;
122
122
 
123
123
  export function as(value: any, type: TypeSpec): IKoffiPointerCast;
124
- export function decode(value: any, type: TypeSpec): any;
125
- export function decode(value: any, type: TypeSpec, len: number): any;
126
- export function decode(value: any, offset: number, type: TypeSpec): any;
127
- export function decode(value: any, offset: number, type: TypeSpec, len: number): any;
128
124
  export function address(value: any): bigint;
129
125
  export function call(value: any, type: TypeSpec, ...args: any[]): any;
126
+ export function view(ref: any, len: number): ArrayBuffer;
127
+
128
+ export const decode: {
129
+ (value: any, type: TypeSpec): any;
130
+ (value: any, type: TypeSpec, len: number): any;
131
+ (value: any, offset: number, type: TypeSpec): any;
132
+ (value: any, offset: number, type: TypeSpec, len: number): any;
133
+
134
+ char(ptr: any): number;
135
+ short(ptr: any): number;
136
+ int(ptr: any): number;
137
+ long(ptr: any): number | bigint;
138
+ longlong(ptr: any): number | bigint;
139
+ uchar(ptr: any): number;
140
+ ushort(ptr: any): number;
141
+ uint(ptr: any): number;
142
+ ulong(ptr: any): number | bigint;
143
+ ulonglong(ptr: any): number | bigint;
144
+
145
+ int8(ptr: any): number;
146
+ int16(ptr: any): number;
147
+ int32(ptr: any): number;
148
+ int64(ptr: any): number | bigint;
149
+ uint8(ptr: any): number;
150
+ uint16(ptr: any): number;
151
+ uint32(ptr: any): number;
152
+ uint64(ptr: any): number | bigint;
153
+
154
+ float(ptr: any): number;
155
+ double(ptr: any): number;
156
+
157
+ string(ptr: any, length?: number | bigint | null): string;
158
+ string16(ptr: any, length?: number | bigint | null): string;
159
+ string32(ptr: any, length?: number | bigint | null): string;
160
+ };
161
+
130
162
  export function encode(ref: any, type: TypeSpec, value: any): void;
131
163
  export function encode(ref: any, type: TypeSpec, value: any, len: number): void;
132
164
  export function encode(ref: any, offset: number, type: TypeSpec): void;
133
165
  export function encode(ref: any, offset: number, type: TypeSpec, value: any): void;
134
166
  export function encode(ref: any, offset: number, type: TypeSpec, value: any, len: number): void;
135
- export function view(ref: any, len: number): ArrayBuffer;
136
167
 
137
168
  export function type(type: TypeSpec): TypeObject;
138
169
  export function sizeof(type: TypeSpec): number;
@@ -2082,6 +2082,9 @@ bool GetDebugFlag(const char *name)
2082
2082
  if (!ParseBool(debug, &ret, K_DEFAULT_PARSE_FLAGS & ~(int)ParseFlag::Log)) {
2083
2083
  LogError("Environment variable '%1' is not a boolean", name);
2084
2084
  }
2085
+ if (ret) {
2086
+ LogWarning("Debug flag '%1' is in effect", name);
2087
+ }
2085
2088
  return ret;
2086
2089
  } else {
2087
2090
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koffi",
3
- "version": "3.0.0-alpha.11",
3
+ "version": "3.0.0-alpha.12",
4
4
  "description": "Fast and simple C FFI (foreign function interface) for Node.js",
5
5
  "keywords": [
6
6
  "foreign",
@@ -131,7 +131,7 @@ var init_abi = __esm({
131
131
  var package_default;
132
132
  var init_package = __esm({
133
133
  "package.json"() {
134
- package_default = { name: "koffi", version: "3.0.0-alpha.11", cnoke: { api: "../../vendor/node-api-headers", output: "../../bin/Koffi/{{ toolchain }}", node: 16, napi: 8 } };
134
+ package_default = { name: "koffi", version: "3.0.0-alpha.12", cnoke: { api: "../../vendor/node-api-headers", output: "../../bin/Koffi/{{ toolchain }}", node: 16, napi: 8 } };
135
135
  }
136
136
  });
137
137
 
@@ -94,7 +94,7 @@ function decodeElfHeader(buf) {
94
94
  }
95
95
 
96
96
  // package.json
97
- var package_default = { name: "koffi", version: "3.0.0-alpha.11", cnoke: { api: "../../vendor/node-api-headers", output: "../../bin/Koffi/{{ toolchain }}", node: 16, napi: 8 } };
97
+ var package_default = { name: "koffi", version: "3.0.0-alpha.12", cnoke: { api: "../../vendor/node-api-headers", output: "../../bin/Koffi/{{ toolchain }}", node: 16, napi: 8 } };
98
98
 
99
99
  // src/init.js
100
100
  var requireNative = createRequire(import.meta.url);
@@ -131,7 +131,7 @@ var init_abi = __esm({
131
131
  var package_default;
132
132
  var init_package = __esm({
133
133
  "package.json"() {
134
- package_default = { name: "koffi", version: "3.0.0-alpha.11", cnoke: { api: "../../vendor/node-api-headers", output: "../../bin/Koffi/{{ toolchain }}", node: 16, napi: 8 } };
134
+ package_default = { name: "koffi", version: "3.0.0-alpha.12", cnoke: { api: "../../vendor/node-api-headers", output: "../../bin/Koffi/{{ toolchain }}", node: 16, napi: 8 } };
135
135
  }
136
136
  });
137
137
 
@@ -94,7 +94,7 @@ function decodeElfHeader(buf) {
94
94
  }
95
95
 
96
96
  // package.json
97
- var package_default = { name: "koffi", version: "3.0.0-alpha.11", cnoke: { api: "../../vendor/node-api-headers", output: "../../bin/Koffi/{{ toolchain }}", node: 16, napi: 8 } };
97
+ var package_default = { name: "koffi", version: "3.0.0-alpha.12", cnoke: { api: "../../vendor/node-api-headers", output: "../../bin/Koffi/{{ toolchain }}", node: 16, napi: 8 } };
98
98
 
99
99
  // src/init.js
100
100
  var requireNative = createRequire(import.meta.url);
@@ -948,8 +948,8 @@ namespace {
948
948
  OP(CallStack) {
949
949
  uint8_t *ptr = call->AllocHeap(inst->a);
950
950
  *(uint8_t **)(base + 8 * 8) = ptr; // x8
951
- CALL(GG);
952
- *(uint8_t **)base = ptr; // Store pointer for ReturnAggregateStack
951
+ WRAP(ForwardCallGG(call->native, base, &call->saved_sp));
952
+ *(uint8_t **)base = ptr;
953
953
  return nullptr;
954
954
  }
955
955
  OP(CallGGX) {
@@ -962,8 +962,8 @@ namespace {
962
962
  OP(CallStackX) {
963
963
  uint8_t *ptr = call->AllocHeap(inst->a);
964
964
  *(uint8_t **)(base + 8 * 8) = ptr; // x8
965
- CALL(GGX);
966
- *(uint8_t **)base = ptr; // Store pointer for ReturnAggregateStack
965
+ WRAP(ForwardCallGGX(call->native, base, &call->saved_sp));
966
+ *(uint8_t **)base = ptr;
967
967
  return nullptr;
968
968
  }
969
969
 
@@ -82,7 +82,8 @@ enum class AbiOpcode {
82
82
  CallStackX,
83
83
  #define PRIMITIVE(Name) Return ## Name,
84
84
  #include "../primitives.inc"
85
- ReturnAggregate
85
+ ReturnAggregateReg,
86
+ ReturnAggregateStack
86
87
  };
87
88
 
88
89
  enum class AbiMethod {
@@ -411,7 +412,7 @@ bool AnalyseFunction(Napi::Env, InstanceData *instance, FunctionInfo *func)
411
412
 
412
413
  func->sync.Append({ .op = Code2Op(run), .a = (int32_t)func->ret.type->size, .type = func->ret.type });
413
414
  func->async.Append({ .op = Code2Op(call), .a = (int32_t)func->ret.type->size });
414
- func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregate), .type = func->ret.type });
415
+ func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregateStack), .type = func->ret.type });
415
416
  } break;
416
417
 
417
418
  case AbiMethod::Gpr:
@@ -422,7 +423,7 @@ bool AnalyseFunction(Napi::Env, InstanceData *instance, FunctionInfo *func)
422
423
 
423
424
  func->sync.Append({ .op = Code2Op(run), .type = func->ret.type });
424
425
  func->async.Append({ .op = Code2Op(call) });
425
- func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregate), .type = func->ret.type });
426
+ func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregateReg), .type = func->ret.type });
426
427
  } break;
427
428
  case AbiMethod::Vec:
428
429
  case AbiMethod::VecVec: {
@@ -431,7 +432,7 @@ bool AnalyseFunction(Napi::Env, InstanceData *instance, FunctionInfo *func)
431
432
 
432
433
  func->sync.Append({ .op = Code2Op(run), .type = func->ret.type });
433
434
  func->async.Append({ .op = Code2Op(call) });
434
- func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregate), .type = func->ret.type });
435
+ func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregateReg), .type = func->ret.type });
435
436
  } break;
436
437
  case AbiMethod::GprVec: {
437
438
  AbiOpcode run = func->forward_fp ? AbiOpcode::RunAggregateGDX : AbiOpcode::RunAggregateGD;
@@ -439,7 +440,7 @@ bool AnalyseFunction(Napi::Env, InstanceData *instance, FunctionInfo *func)
439
440
 
440
441
  func->sync.Append({ .op = Code2Op(run), .type = func->ret.type });
441
442
  func->async.Append({ .op = Code2Op(call) });
442
- func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregate), .type = func->ret.type });
443
+ func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregateReg), .type = func->ret.type });
443
444
  } break;
444
445
  case AbiMethod::VecGpr: {
445
446
  AbiOpcode run = func->forward_fp ? AbiOpcode::RunAggregateDGX : AbiOpcode::RunAggregateDG;
@@ -447,7 +448,7 @@ bool AnalyseFunction(Napi::Env, InstanceData *instance, FunctionInfo *func)
447
448
 
448
449
  func->sync.Append({ .op = Code2Op(run), .type = func->ret.type });
449
450
  func->async.Append({ .op = Code2Op(call) });
450
- func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregate), .type = func->ret.type });
451
+ func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregateReg), .type = func->ret.type });
451
452
  } break;
452
453
  }
453
454
  } break;
@@ -964,7 +965,8 @@ namespace {
964
965
  return Napi::Number::New(call->env, d);
965
966
  }
966
967
  OP(ReturnPrototype) { K_UNREACHABLE(); return call->env.Null(); }
967
- OP(ReturnAggregate) {
968
+ OP(ReturnAggregateReg) { return DecodeObject(call->env, (const uint8_t *)base, inst->type); }
969
+ OP(ReturnAggregateStack) {
968
970
  uint64_t a0 = *(uint64_t *)base;
969
971
  return DecodeObject(call->env, (const uint8_t *)a0, inst->type);
970
972
  }
@@ -1009,7 +1011,8 @@ namespace {
1009
1011
  HandleCallStackX,
1010
1012
  #define PRIMITIVE(Name) HandleReturn ## Name,
1011
1013
  #include "../primitives.inc"
1012
- HandleReturnAggregate
1014
+ HandleReturnAggregateReg,
1015
+ HandleReturnAggregateStack
1013
1016
  };
1014
1017
 
1015
1018
  FORCE_INLINE napi_value RunLoop(CallData *call, napi_value *args, uint8_t *base, const AbiInstruction *inst)
@@ -82,7 +82,8 @@ enum class AbiOpcode {
82
82
  CallStackX,
83
83
  #define PRIMITIVE(Name) Return ## Name,
84
84
  #include "../primitives.inc"
85
- ReturnAggregate
85
+ ReturnAggregateReg,
86
+ ReturnAggregateStack
86
87
  };
87
88
 
88
89
  enum class AbiMethod {
@@ -454,9 +455,9 @@ bool AnalyseFunction(Napi::Env, InstanceData *, FunctionInfo *func)
454
455
  AbiOpcode run = func->forward_fp ? AbiOpcode::RunAggregateStackX : AbiOpcode::RunAggregateStack;
455
456
  AbiOpcode call = func->forward_fp ? AbiOpcode::CallStackX : AbiOpcode::CallStack;
456
457
 
457
- func->sync.Append({ .op = Code2Op(run), .a = (int32_t)func->stk_size, .type = func->ret.type });
458
- func->async.Append({ .op = Code2Op(call), .a = (int32_t)func->stk_size });
459
- func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregate), .type = func->ret.type });
458
+ func->sync.Append({ .op = Code2Op(run), .a = (int32_t)func->ret.type->size, .type = func->ret.type });
459
+ func->async.Append({ .op = Code2Op(call), .a = (int32_t)func->ret.type->size });
460
+ func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregateStack), .type = func->ret.type });
460
461
 
461
462
  // Allocate stack space for return value
462
463
  func->stk_size += AlignLen(func->ret.type->size, 16);
@@ -467,7 +468,7 @@ bool AnalyseFunction(Napi::Env, InstanceData *, FunctionInfo *func)
467
468
 
468
469
  func->sync.Append({ .op = Code2Op(run), .type = func->ret.type });
469
470
  func->async.Append({ .op = Code2Op(call) });
470
- func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregate), .type = func->ret.type });
471
+ func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregateReg), .type = func->ret.type });
471
472
  } break;
472
473
  case AbiMethod::GprGpr: {
473
474
  AbiOpcode run = func->forward_fp ? AbiOpcode::RunAggregateGGX : AbiOpcode::RunAggregateGG;
@@ -475,7 +476,7 @@ bool AnalyseFunction(Napi::Env, InstanceData *, FunctionInfo *func)
475
476
 
476
477
  func->sync.Append({ .op = Code2Op(run), .type = func->ret.type });
477
478
  func->async.Append({ .op = Code2Op(call) });
478
- func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregate), .type = func->ret.type });
479
+ func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregateReg), .type = func->ret.type });
479
480
  } break;
480
481
  case AbiMethod::Xmm: {
481
482
  AbiOpcode run = func->forward_fp ? AbiOpcode::RunAggregateDDX : AbiOpcode::RunAggregateDD;
@@ -483,7 +484,7 @@ bool AnalyseFunction(Napi::Env, InstanceData *, FunctionInfo *func)
483
484
 
484
485
  func->sync.Append({ .op = Code2Op(run), .type = func->ret.type });
485
486
  func->async.Append({ .op = Code2Op(call) });
486
- func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregate), .type = func->ret.type });
487
+ func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregateReg), .type = func->ret.type });
487
488
  } break;
488
489
  case AbiMethod::XmmXmm: {
489
490
  AbiOpcode run = func->forward_fp ? AbiOpcode::RunAggregateDDX : AbiOpcode::RunAggregateDD;
@@ -491,7 +492,7 @@ bool AnalyseFunction(Napi::Env, InstanceData *, FunctionInfo *func)
491
492
 
492
493
  func->sync.Append({ .op = Code2Op(run), .type = func->ret.type });
493
494
  func->async.Append({ .op = Code2Op(call) });
494
- func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregate), .type = func->ret.type });
495
+ func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregateReg), .type = func->ret.type });
495
496
  } break;
496
497
  case AbiMethod::GprXmm: {
497
498
  AbiOpcode run = func->forward_fp ? AbiOpcode::RunAggregateGDX : AbiOpcode::RunAggregateGD;
@@ -499,7 +500,7 @@ bool AnalyseFunction(Napi::Env, InstanceData *, FunctionInfo *func)
499
500
 
500
501
  func->sync.Append({ .op = Code2Op(run), .type = func->ret.type });
501
502
  func->async.Append({ .op = Code2Op(call) });
502
- func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregate), .type = func->ret.type });
503
+ func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregateReg), .type = func->ret.type });
503
504
  } break;
504
505
  case AbiMethod::XmmGpr: {
505
506
  AbiOpcode run = func->forward_fp ? AbiOpcode::RunAggregateDGX : AbiOpcode::RunAggregateDG;
@@ -507,7 +508,7 @@ bool AnalyseFunction(Napi::Env, InstanceData *, FunctionInfo *func)
507
508
 
508
509
  func->sync.Append({ .op = Code2Op(run), .type = func->ret.type });
509
510
  func->async.Append({ .op = Code2Op(call) });
510
- func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregate), .type = func->ret.type });
511
+ func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregateReg), .type = func->ret.type });
511
512
  } break;
512
513
  }
513
514
  } break;
@@ -815,9 +816,10 @@ namespace {
815
816
  return DecodeObject(call->env, (const uint8_t *)&ret, inst->type);
816
817
  }
817
818
  OP(RunAggregateStack) {
818
- *(uint8_t **)base = base + inst->a;
819
- uint64_t rax = WRAP(ForwardCallGG(call->native, base, &call->saved_sp).rax);
820
- return DecodeObject(call->env, (const uint8_t *)rax, inst->type);
819
+ uint8_t *ptr = call->AllocHeap(inst->a);
820
+ *(uint8_t **)base = ptr;
821
+ WRAP(ForwardCallGG(call->native, base, &call->saved_sp).rax);
822
+ return DecodeObject(call->env, ptr, inst->type);
821
823
  }
822
824
  OP(RunVoidX) {
823
825
  WRAP(ForwardCallGGX(call->native, base, &call->saved_sp));
@@ -898,9 +900,10 @@ namespace {
898
900
  return DecodeObject(call->env, (const uint8_t *)&ret, inst->type);
899
901
  }
900
902
  OP(RunAggregateStackX) {
901
- *(uint8_t **)base = base + inst->a;
902
- uint64_t rax = WRAP(ForwardCallGGX(call->native, base, &call->saved_sp).rax);
903
- return DecodeObject(call->env, (const uint8_t *)rax, inst->type);
903
+ uint8_t *ptr = call->AllocHeap(inst->a);
904
+ *(uint8_t **)base = ptr;
905
+ WRAP(ForwardCallGGX(call->native, base, &call->saved_sp).rax);
906
+ return DecodeObject(call->env, ptr, inst->type);
904
907
  }
905
908
 
906
909
  #undef DISPOSE
@@ -941,7 +944,8 @@ namespace {
941
944
  OP(CallGD) { CALL(GD); return nullptr; }
942
945
  OP(CallDD) { CALL(DD); return nullptr; }
943
946
  OP(CallStack) {
944
- *(uint8_t **)base = base + inst->a;
947
+ uint8_t *ptr = call->AllocHeap(inst->a);
948
+ *(uint8_t **)base = ptr;
945
949
  CALL(GG);
946
950
  return nullptr;
947
951
  }
@@ -951,7 +955,8 @@ namespace {
951
955
  OP(CallGDX) { CALL(GDX); return nullptr; }
952
956
  OP(CallDDX) { CALL(DDX); return nullptr; }
953
957
  OP(CallStackX) {
954
- *(uint8_t **)base = base + inst->a;
958
+ uint8_t *ptr = call->AllocHeap(inst->a);
959
+ *(uint8_t **)base = ptr;
955
960
  CALL(GGX);
956
961
  return nullptr;
957
962
  }
@@ -1015,7 +1020,8 @@ namespace {
1015
1020
  return Napi::Number::New(call->env, d);
1016
1021
  }
1017
1022
  OP(ReturnPrototype) { K_UNREACHABLE(); return call->env.Null(); }
1018
- OP(ReturnAggregate) {
1023
+ OP(ReturnAggregateReg) { return DecodeObject(call->env, base, inst->type); }
1024
+ OP(ReturnAggregateStack) {
1019
1025
  uint64_t rax = *(uint64_t *)base;
1020
1026
  return DecodeObject(call->env, (const uint8_t *)rax, inst->type);
1021
1027
  }
@@ -1060,7 +1066,8 @@ namespace {
1060
1066
  HandleCallStackX,
1061
1067
  #define PRIMITIVE(Name) HandleReturn ## Name,
1062
1068
  #include "../primitives.inc"
1063
- HandleReturnAggregate
1069
+ HandleReturnAggregateReg,
1070
+ HandleReturnAggregateStack
1064
1071
  };
1065
1072
 
1066
1073
  FORCE_INLINE napi_value RunLoop(CallData *call, napi_value *args, uint8_t *base, const AbiInstruction *inst)
@@ -49,7 +49,8 @@ enum class AbiOpcode {
49
49
  CallStackX,
50
50
  #define PRIMITIVE(Name) Return ## Name,
51
51
  #include "../primitives.inc"
52
- ReturnAggregate
52
+ ReturnAggregateReg,
53
+ ReturnAggregateStack
53
54
  };
54
55
 
55
56
  namespace {
@@ -172,14 +173,14 @@ bool AnalyseFunction(Napi::Env, InstanceData *, FunctionInfo *func)
172
173
 
173
174
  func->sync.Append({ .op = Code2Op(run), .type = func->ret.type });
174
175
  func->async.Append({ .op = Code2Op(call) });
175
- func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregate), .type = func->ret.type });
176
+ func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregateReg), .type = func->ret.type });
176
177
  } else {
177
178
  AbiOpcode run = func->forward_fp ? AbiOpcode::RunAggregateStackX : AbiOpcode::RunAggregateStack;
178
179
  AbiOpcode call = func->forward_fp ? AbiOpcode::CallStackX : AbiOpcode::CallStack;
179
180
 
180
181
  func->sync.Append({ .op = Code2Op(run), .a = (int32_t)func->ret.type->size, .type = func->ret.type });
181
182
  func->async.Append({ .op = Code2Op(call), .a = (int32_t)func->ret.type->size });
182
- func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregate), .type = func->ret.type });
183
+ func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregateStack), .type = func->ret.type });
183
184
  }
184
185
  } break;
185
186
  case PrimitiveKind::Array: { K_UNREACHABLE(); } break;
@@ -597,7 +598,7 @@ namespace {
597
598
  OP(CallD) { CALL(D); return nullptr; }
598
599
  OP(CallStack) {
599
600
  *(uint8_t **)base = call->AllocHeap(inst->a);
600
- CALL(G);
601
+ WRAP(ForwardCallG(call->native, (uint8_t *)base, &call->saved_sp));
601
602
  return nullptr;
602
603
  }
603
604
  OP(CallGX) { CALL(GX); return nullptr; }
@@ -605,7 +606,7 @@ namespace {
605
606
  OP(CallDX) { CALL(DX); return nullptr; }
606
607
  OP(CallStackX) {
607
608
  *(uint8_t **)base = call->AllocHeap(inst->a);
608
- CALL(GX);
609
+ WRAP(ForwardCallGX(call->native, (uint8_t *)base, &call->saved_sp));
609
610
  return nullptr;
610
611
  }
611
612
 
@@ -668,7 +669,8 @@ namespace {
668
669
  return Napi::Number::New(call->env, d);
669
670
  }
670
671
  OP(ReturnPrototype) { K_UNREACHABLE(); return call->env.Null(); }
671
- OP(ReturnAggregate) {
672
+ OP(ReturnAggregateReg) { return DecodeObject(call->env, (const uint8_t *)base, inst->type); }
673
+ OP(ReturnAggregateStack) {
672
674
  uint64_t rax = *(uint64_t *)base;
673
675
  return DecodeObject(call->env, (const uint8_t *)rax, inst->type);
674
676
  }
@@ -703,7 +705,8 @@ namespace {
703
705
  HandleCallStackX,
704
706
  #define PRIMITIVE(Name) HandleReturn ## Name,
705
707
  #include "../primitives.inc"
706
- HandleReturnAggregate
708
+ HandleReturnAggregateReg,
709
+ HandleReturnAggregateStack
707
710
  };
708
711
 
709
712
  FORCE_INLINE napi_value RunLoop(CallData *call, napi_value *args, uint64_t *base, const AbiInstruction *inst)
@@ -60,7 +60,8 @@ enum class AbiOpcode {
60
60
  CallStackR,
61
61
  #define PRIMITIVE(Name) Return ## Name,
62
62
  #include "../primitives.inc"
63
- ReturnAggregate
63
+ ReturnAggregateReg,
64
+ ReturnAggregateStack
64
65
  };
65
66
 
66
67
  static inline void *Code2Op(AbiOpcode code)
@@ -207,7 +208,7 @@ bool AnalyseFunction(Napi::Env env, InstanceData *instance, FunctionInfo *func)
207
208
 
208
209
  func->sync.Append({ .op = Code2Op(run), .type = func->ret.type });
209
210
  func->async.Append({ .op = Code2Op(call) });
210
- func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregate), .type = func->ret.type });
211
+ func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregateReg), .type = func->ret.type });
211
212
 
212
213
  break;
213
214
  } else if (member.type->primitive == PrimitiveKind::Float64) {
@@ -216,7 +217,7 @@ bool AnalyseFunction(Napi::Env env, InstanceData *instance, FunctionInfo *func)
216
217
 
217
218
  func->sync.Append({ .op = Code2Op(run), .type = func->ret.type });
218
219
  func->async.Append({ .op = Code2Op(call) });
219
- func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregate), .type = func->ret.type });
220
+ func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregateReg), .type = func->ret.type });
220
221
 
221
222
  break;
222
223
  }
@@ -229,14 +230,14 @@ bool AnalyseFunction(Napi::Env env, InstanceData *instance, FunctionInfo *func)
229
230
 
230
231
  func->sync.Append({ .op = Code2Op(run), .type = func->ret.type });
231
232
  func->async.Append({ .op = Code2Op(call) });
232
- func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregate), .type = func->ret.type });
233
+ func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregateReg), .type = func->ret.type });
233
234
  } else {
234
235
  AbiOpcode run = fast ? AbiOpcode::RunAggregateStackR : AbiOpcode::RunAggregateStack;
235
236
  AbiOpcode call = fast ? AbiOpcode::CallStackR : AbiOpcode::CallStack;
236
237
 
237
238
  func->sync.Append({ .op = Code2Op(run), .a = (int32_t)func->ret.type->size, .type = func->ret.type });
238
239
  func->async.Append({ .op = Code2Op(call), .a = (int32_t)func->ret.type->size });
239
- func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregate), .type = func->ret.type });
240
+ func->async.Append({ .op = Code2Op(AbiOpcode::ReturnAggregateStack), .type = func->ret.type });
240
241
  }
241
242
  } break;
242
243
  case PrimitiveKind::Array: { K_UNREACHABLE(); } break;
@@ -793,8 +794,13 @@ napi_value RunLoop(CallData *call, napi_value *args, uint32_t *base, const AbiIn
793
794
  return Napi::Number::New(call->env, d);
794
795
  }
795
796
  OP(ReturnPrototype) { K_UNREACHABLE(); return call->env.Null(); }
796
- OP(ReturnAggregate) {
797
+ OP(ReturnAggregateReg) { return DecodeObject(call->env, (const uint8_t *)base, inst->type); }
798
+ OP(ReturnAggregateStack) {
799
+ #if defined(_WIN32)
800
+ uint32_t eax = *(uint32_t *)(base + 4);
801
+ #else
797
802
  uint32_t eax = *(uint32_t *)base;
803
+ #endif
798
804
  return DecodeObject(call->env, (const uint8_t *)eax, inst->type);
799
805
  }
800
806
 
@@ -1220,15 +1220,12 @@ bool CallData::CheckDynamicLength(napi_value obj, Size element, const char *coun
1220
1220
  }
1221
1221
 
1222
1222
  // Get actual size
1223
- if (IsArray(env, value)) {
1224
- Napi::Array array = Napi::Array(env, value);
1225
- size = array.Length() * element;
1226
- } else if (IsTypedArray(env, value)) {
1227
- Napi::TypedArray typed = Napi::TypedArray(env, value);
1228
- size = typed.ByteLength();
1229
- } else if (IsArrayBuffer(env, value)) {
1230
- Napi::ArrayBuffer buffer = Napi::ArrayBuffer(env, value);
1231
- size = buffer.ByteLength();
1223
+ if (uint32_t len = 0; napi_get_array_length(env, value, &len) == napi_ok) {
1224
+ size = (int64_t)len * element;
1225
+ } else if (size_t len = 0; node_api_get_buffer_info(env, value, nullptr, &len) == napi_ok) {
1226
+ size = (int64_t)len;
1227
+ } else if (size_t len = 0; napi_get_arraybuffer_info(env, value, nullptr, &len) == napi_ok) {
1228
+ size = (int64_t)len;
1232
1229
  } else if (!IsNullOrUndefined(env, value)) {
1233
1230
  size = element;
1234
1231
  } else {
@@ -1245,7 +1242,7 @@ bool CallData::CheckDynamicLength(napi_value obj, Size element, const char *coun
1245
1242
 
1246
1243
  #if defined(K_DEBUG)
1247
1244
 
1248
- static bool IsDebugEnabled()
1245
+ static bool IsDebugCallsEnabled()
1249
1246
  {
1250
1247
  static bool debug = GetDebugFlag("DEBUG_CALLS");
1251
1248
  return debug;
@@ -1253,7 +1250,7 @@ static bool IsDebugEnabled()
1253
1250
 
1254
1251
  void CallData::DebugCall(const FunctionInfo *func)
1255
1252
  {
1256
- if (!IsDebugEnabled())
1253
+ if (!IsDebugCallsEnabled())
1257
1254
  return;
1258
1255
 
1259
1256
  PrintLn(StdErr, "%!..+---- %1 (%2) ----%!0", func->name, CallConventionNames[(int)func->convention]);
@@ -1271,7 +1268,7 @@ void CallData::DebugCall(const FunctionInfo *func)
1271
1268
 
1272
1269
  void CallData::DebugForward()
1273
1270
  {
1274
- if (!IsDebugEnabled())
1271
+ if (!IsDebugCallsEnabled())
1275
1272
  return;
1276
1273
 
1277
1274
  Span<const uint8_t> stack = MakeSpan(mem->stack.end, prev_stack - mem->stack.end);
@@ -1382,13 +1379,12 @@ napi_value TranslateFastCall(napi_env env, napi_callback_info info)
1382
1379
  napi_status status = napi_get_cb_info(env, info, &count, args, nullptr, (void **)&func);
1383
1380
  K_ASSERT(status == napi_ok);
1384
1381
 
1385
- InstanceData *instance = func->instance;
1386
-
1387
1382
  if (count < (size_t)func->required_parameters) [[unlikely]] {
1388
1383
  ThrowError<Napi::TypeError>(env, "Expected %1 arguments, got %2", func->parameters.len, count);
1389
1384
  return Napi::Env(env).Null();
1390
1385
  }
1391
1386
 
1387
+ InstanceData *instance = func->instance;
1392
1388
  InstanceMemory *mem = instance->memories[0];
1393
1389
  CallData call(env, instance, mem, func->native);
1394
1390
 
@@ -1405,13 +1401,12 @@ napi_value TranslateFastCall(napi_env env, napi_callback_info info)
1405
1401
 
1406
1402
  static FORCE_INLINE napi_value TranslateNormalCall(napi_env env, const FunctionInfo *func, void *native, napi_value *args, Size count)
1407
1403
  {
1408
- InstanceData *instance = func->instance;
1409
-
1410
1404
  if (count < func->required_parameters) [[unlikely]] {
1411
1405
  ThrowError<Napi::TypeError>(env, "Expected %1 arguments, got %2", func->parameters.len, count);
1412
1406
  return Napi::Env(env).Null();
1413
1407
  }
1414
1408
 
1409
+ InstanceData *instance = func->instance;
1415
1410
  InstanceMemory *mem = instance->memories[0];
1416
1411
  CallData call(env, instance, mem, native);
1417
1412
 
@@ -1447,13 +1442,68 @@ napi_value TranslateNormalCall(napi_env env, napi_callback_info info)
1447
1442
  return TranslateNormalCall(env, func, func->native, args, (Size)count);
1448
1443
  }
1449
1444
 
1450
- static napi_value TranslateVariadicCall(napi_env env, const FunctionInfo *func, void *native, napi_value *args, Size count)
1445
+ static FORCE_INLINE napi_value TranslateNormalCallDebugAsync(napi_env env, const FunctionInfo *func, void *native, napi_value *args, Size count)
1451
1446
  {
1447
+ if (count < func->required_parameters) [[unlikely]] {
1448
+ ThrowError<Napi::TypeError>(env, "Expected %1 arguments, got %2", func->parameters.len, count);
1449
+ return Napi::Env(env).Null();
1450
+ }
1451
+
1452
1452
  InstanceData *instance = func->instance;
1453
+ InstanceMemory *mem = instance->memories[0];
1454
+ CallData call(env, instance, mem, native);
1455
+
1456
+ K_DEFER_C(prev_call = instance->sync_call) { instance->sync_call = prev_call; };
1457
+ instance->sync_call = &call;
1458
+
1459
+ call.DebugCall(func);
1460
+
1461
+ // The async call code partly works differently, with Yield and Return instructions which do not
1462
+ // get used for normal sync calls. To exercise them, we also run the sync tests using async
1463
+ // instructions, by setting DEBUG_ASYNC=1.
1464
+
1465
+ napi_value ret;
1466
+ if (call.PrepareAsync(func, args)) {
1467
+ call.ExecuteAsync();
1468
+ ret = call.EndAsync();
1469
+ } else {
1470
+ ret = Napi::Env(env).Null();
1471
+ }
1472
+ call.Finalize();
1473
+
1474
+ return ret;
1475
+ }
1453
1476
 
1477
+ napi_value TranslateNormalCallDebugAsync(napi_env env, napi_callback_info info)
1478
+ {
1479
+ static_assert(MaxParameters >= 8);
1480
+
1481
+ napi_value args[MaxParameters];
1482
+ size_t count = 8;
1483
+ FunctionInfo *func;
1484
+
1485
+ napi_status status = napi_get_cb_info(env, info, &count, args, nullptr, (void **)&func);
1486
+ K_ASSERT(status == napi_ok);
1487
+
1488
+ if (count > 8) {
1489
+ napi_status status = napi_get_cb_info(env, info, &count, args, nullptr, nullptr);
1490
+ K_ASSERT(status == napi_ok);
1491
+
1492
+ count = std::min(count, (size_t)MaxParameters);
1493
+ }
1494
+
1495
+ return TranslateNormalCallDebugAsync(env, func, func->native, args, count);
1496
+ }
1497
+
1498
+ static napi_value TranslateVariadicCall(napi_env env, const FunctionInfo *func, void *native, napi_value *args, Size count)
1499
+ {
1454
1500
  FunctionInfo *variadic = nullptr;
1455
1501
  K_DEFER_N(err_guard) { delete variadic; };
1456
1502
 
1503
+ InstanceData *instance = func->instance;
1504
+ InstanceMemory *mem = instance->memories[0];
1505
+ CallData call(env, instance, mem, native);
1506
+
1457
1507
  // Try cached function
1458
1508
  {
1459
1509
  FunctionInfo *prev = instance->variadic_func;
@@ -1531,9 +1581,6 @@ static napi_value TranslateVariadicCall(napi_env env, const FunctionInfo *func,
1531
1581
  return Napi::Env(env).Null();
1532
1582
  }
1533
1583
 
1534
- InstanceMemory *mem = instance->memories[0];
1535
- CallData call(env, instance, mem, native);
1536
-
1537
1584
  K_DEFER_C(prev_call = instance->sync_call) { instance->sync_call = prev_call; };
1538
1585
  instance->sync_call = &call;
1539
1586
 
@@ -170,6 +170,7 @@ bool InitAsyncBroker(Napi::Env env, InstanceData *instance);
170
170
  napi_value TranslateZeroCall(napi_env env, napi_callback_info info);
171
171
  napi_value TranslateFastCall(napi_env env, napi_callback_info info);
172
172
  napi_value TranslateNormalCall(napi_env env, napi_callback_info info);
173
+ napi_value TranslateNormalCallDebugAsync(napi_env env, napi_callback_info info);
173
174
  napi_value TranslateVariadicCall(napi_env env, napi_callback_info info);
174
175
  napi_value TranslateAsyncCall(napi_env env, napi_callback_info info);
175
176
 
@@ -37,7 +37,8 @@ namespace K {
37
37
 
38
38
  SharedData shared;
39
39
 
40
- // Recent N-API functions are loaded dynamically
40
+ // Some N-API functions are loaded dynamically to work around bugs or because they are recent
41
+ napi_status (NAPI_CDECL *node_api_get_buffer_info)(napi_env env, napi_value value, void **data, size_t *length);
41
42
  napi_status (NAPI_CDECL *node_api_create_property_key_utf8)(napi_env env, const char* str, size_t length, napi_value* result);
42
43
  napi_status (NAPI_CDECL *node_api_post_finalizer)(node_api_basic_env env, napi_finalize finalize_cb, void* finalize_data, void* finalize_hint);
43
44
  napi_value (*translate_zero_call)(napi_env env, napi_callback_info info);
@@ -2365,30 +2366,40 @@ static inline PrimitiveKind GetBigEndianPrimitive(PrimitiveKind kind)
2365
2366
  #endif
2366
2367
  }
2367
2368
 
2368
- static bool CanReferencePrimitiveValues(napi_env env)
2369
+ static bool CanCallNapiGetBufferInfoDirectly(const napi_node_version &node, uint32_t napi)
2369
2370
  {
2370
- uint32_t version = 0;
2371
- napi_get_version(env, &version);
2371
+ if (napi >= 10)
2372
+ return true;
2373
+ if (node.major >= 22)
2374
+ return true;
2372
2375
 
2373
- return version >= 10;
2376
+ // Made by looking at the git history of each release branch
2377
+ if (node.major == 21 && node.minor >= 7)
2378
+ return true;
2379
+ if (node.major == 20 && node.minor >= 12)
2380
+ return true;
2381
+
2382
+ return false;
2374
2383
  }
2375
2384
 
2376
- static bool CanDeleteReferenceInFinalizer(napi_env env)
2385
+ static bool CanReferencePrimitiveValues(const napi_node_version &node, uint32_t napi)
2377
2386
  {
2378
- const napi_node_version *version = nullptr;
2379
- napi_get_node_version(env, &version);
2387
+ return napi >= 10;
2388
+ }
2380
2389
 
2381
- if (version->major >= 24)
2390
+ static bool CanDeleteReferenceInFinalizer(const napi_node_version &node, uint32_t napi)
2391
+ {
2392
+ if (node.major >= 24)
2382
2393
  return true;
2383
2394
 
2384
2395
  // Made by looking at the git history of each release branch
2385
- if (version->major == 23 && version->minor >= 5)
2396
+ if (node.major == 23 && node.minor >= 5)
2386
2397
  return true;
2387
- if (version->major == 22 && version->minor >= 13)
2398
+ if (node.major == 22 && node.minor >= 13)
2388
2399
  return true;
2389
- if (version->major == 20 && version->minor >= 19)
2400
+ if (node.major == 20 && node.minor >= 19)
2390
2401
  return true;
2391
- if (version->major == 20 && version->minor == 18 && version->patch >= 3)
2402
+ if (node.major == 20 && node.minor == 18 && node.patch >= 3)
2392
2403
  return true;
2393
2404
 
2394
2405
  return false;
@@ -2421,13 +2432,32 @@ static Napi::Object InitModule(Napi::Env env, Napi::Object exports)
2421
2432
  #define SYMBOL(Symbol) ((decltype(Symbol))dlsym(h, K_STRINGIFY(Symbol)))
2422
2433
  #endif
2423
2434
 
2424
- if (CanReferencePrimitiveValues(env)) {
2435
+ const napi_node_version *node_version = nullptr;
2436
+ uint32_t napi_version = 0;
2437
+ napi_get_node_version(env, &node_version);
2438
+ napi_get_version(env, &napi_version);
2439
+
2440
+ if (CanCallNapiGetBufferInfoDirectly(*node_version, napi_version)) {
2441
+ node_api_get_buffer_info = napi_get_buffer_info;
2442
+ } else {
2443
+ // Before Node 20.12, napi_get_buffer_info() would assert/crash
2444
+ // when used with something it did not support, instead of returning napi_invalid_arg.
2445
+ // So we need to call napi_is_buffer() for old versions before trying napi_get_buffer_info().
2446
+
2447
+ node_api_get_buffer_info = [](napi_env env, napi_value value, void **data, size_t *length) {
2448
+ if (!IsBuffer(env, value))
2449
+ return napi_invalid_arg;
2450
+ return napi_get_buffer_info(env, value, data, length);
2451
+ };
2452
+ }
2453
+
2454
+ if (CanReferencePrimitiveValues(*node_version, napi_version)) {
2425
2455
  // We can't use optimized property keys in older versions because we need to create
2426
2456
  // references to them, but napi_create_reference() was not usable with primitive values.
2427
2457
  node_api_create_property_key_utf8 = SYMBOL(node_api_create_property_key_utf8);
2428
2458
  }
2429
2459
 
2430
- if (!CanDeleteReferenceInFinalizer(env)) {
2460
+ if (!CanDeleteReferenceInFinalizer(*node_version, napi_version)) {
2431
2461
  // napi_delete_reference cannot be safely used in older Node versions because it
2432
2462
  // errors out (or even asserts) if it gets called in a finalizer. In this case,
2433
2463
  // use experimental API to try to run it later.
@@ -390,7 +390,8 @@ static_assert(MaxTrampolines <= INT16_MAX);
390
390
 
391
391
  extern SharedData shared;
392
392
 
393
- // Recent N-API functions are loaded dynamically
393
+ // Some N-API functions are loaded dynamically to work around bugs or because they are recent
394
+ extern napi_status (NAPI_CDECL *node_api_get_buffer_info)(napi_env env, napi_value value, void **data, size_t *length);
394
395
  extern napi_status (NAPI_CDECL *node_api_create_property_key_utf8)(napi_env env, const char* str, size_t length, napi_value* result);
395
396
  extern napi_status (NAPI_CDECL *node_api_post_finalizer)(node_api_basic_env env, napi_finalize finalize_cb, void* finalize_data, void* finalize_hint);
396
397
  extern napi_value (*translate_zero_call)(napi_env env, napi_callback_info info);
@@ -1948,6 +1948,12 @@ Napi::Object DescribeFunction(Napi::Env env, const FunctionInfo *func)
1948
1948
  return meta;
1949
1949
  }
1950
1950
 
1951
+ static bool IsDebugAsyncEnabled()
1952
+ {
1953
+ static bool debug = GetDebugFlag("DEBUG_ASYNC");
1954
+ return debug;
1955
+ }
1956
+
1951
1957
  Napi::Function WrapFunction(Napi::Env env, const FunctionInfo *func)
1952
1958
  {
1953
1959
  Napi::Function wrapper;
@@ -1959,6 +1965,9 @@ Napi::Function WrapFunction(Napi::Env env, const FunctionInfo *func)
1959
1965
  if (func->variadic) {
1960
1966
  napi_status status = napi_create_function(env, func->name, NAPI_AUTO_LENGTH, TranslateVariadicCall, (void *)func->Ref(), &value);
1961
1967
  K_ASSERT(status == napi_ok);
1968
+ } else if (IsDebugAsyncEnabled()) {
1969
+ napi_status status = napi_create_function(env, func->name, NAPI_AUTO_LENGTH, TranslateNormalCallDebugAsync, (void *)func->Ref(), &value);
1970
+ K_ASSERT(status == napi_ok);
1962
1971
  } else if (!func->parameters.len) {
1963
1972
  InstanceData *instance = env.GetInstanceData<InstanceData>();
1964
1973
  napi_status status = napi_create_function(env, func->name, NAPI_AUTO_LENGTH, instance->translate_zero_call, (void *)func->Ref(), &value);
@@ -275,10 +275,8 @@ static FORCE_INLINE bool TryPointer(napi_env env, napi_value value, void **out_p
275
275
  }
276
276
  }
277
277
 
278
- if (IsTypedArray(env, value)) {
279
- napi_get_typedarray_info(env, value, nullptr, nullptr, out_ptr, nullptr, nullptr);
278
+ if (node_api_get_buffer_info(env, value, out_ptr, nullptr) == napi_ok)
280
279
  return true;
281
- }
282
280
 
283
281
  napi_valuetype kind = GetKindOf(env, value);
284
282
 
@@ -299,12 +297,10 @@ static FORCE_INLINE bool TryPointer(napi_env env, napi_value value, void **out_p
299
297
  *out_ptr = (void *)external.Data();
300
298
  return true;
301
299
  #endif
302
- } else if (IsArrayBuffer(env, value)) {
303
- Napi::ArrayBuffer buffer = Napi::ArrayBuffer(env, value);
300
+ }
304
301
 
305
- *out_ptr = (void *)buffer.Data();
302
+ if (napi_get_arraybuffer_info(env, value, out_ptr, nullptr) == napi_ok)
306
303
  return true;
307
- }
308
304
 
309
305
  return false;
310
306
  }
@@ -325,10 +321,8 @@ static FORCE_INLINE bool TryPointer(napi_env env, napi_value value, void **out_p
325
321
  }
326
322
  }
327
323
 
328
- if (IsTypedArray(env, value)) {
329
- napi_get_typedarray_info(env, value, nullptr, nullptr, out_ptr, nullptr, nullptr);
324
+ if (node_api_get_buffer_info(env, value, out_ptr, nullptr) == napi_ok) {
330
325
  *out_kind = napi_object;
331
-
332
326
  return true;
333
327
  }
334
328
 
@@ -357,12 +351,10 @@ static FORCE_INLINE bool TryPointer(napi_env env, napi_value value, void **out_p
357
351
 
358
352
  return true;
359
353
  #endif
360
- } else if (IsArrayBuffer(env, value)) {
361
- Napi::ArrayBuffer buffer = Napi::ArrayBuffer(env, value);
354
+ }
362
355
 
363
- *out_ptr = (void *)buffer.Data();
356
+ if (napi_get_arraybuffer_info(env, value, out_ptr, nullptr) == napi_ok) {
364
357
  *out_kind = napi_object;
365
-
366
358
  return true;
367
359
  }
368
360
 
@@ -372,23 +364,14 @@ static FORCE_INLINE bool TryPointer(napi_env env, napi_value value, void **out_p
372
364
 
373
365
  static FORCE_INLINE bool TryBuffer(napi_env env, napi_value value, Span<uint8_t> *out_buffer)
374
366
  {
375
- // Before somewhere around Node 20.12, napi_get_buffer_info() would assert/crash
376
- // when used with something it did not support, instead of returning napi_invalid_arg.
377
- // So we need to call napi_is_buffer(), at least for now.
378
-
379
- if (IsBuffer(env, value)) {
380
- void *ptr = nullptr;
381
- size_t length = 0;
367
+ void *ptr = nullptr;
368
+ size_t len = 0;
382
369
 
383
- // Assume it works
384
- napi_get_buffer_info(env, value, &ptr, &length);
385
-
386
- *out_buffer = MakeSpan((uint8_t *)ptr, (Size)length);
370
+ if (node_api_get_buffer_info(env, value, &ptr, &len) == napi_ok) {
371
+ *out_buffer = MakeSpan((uint8_t *)ptr, (Size)len);
387
372
  return true;
388
- } else if (IsArrayBuffer(env, value)) {
389
- Napi::ArrayBuffer buffer = Napi::ArrayBuffer(env, value);
390
-
391
- *out_buffer = MakeSpan((uint8_t *)buffer.Data(), (Size)buffer.ByteLength());
373
+ } else if (napi_get_arraybuffer_info(env, value, &ptr, &len) == napi_ok) {
374
+ *out_buffer = MakeSpan((uint8_t *)ptr, (Size)len);
392
375
  return true;
393
376
  }
394
377