koffi 2.15.1 → 2.16.0-beta.1

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 (50) 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_armhf/koffi.node +0 -0
  8. package/build/koffi/linux_ia32/koffi.node +0 -0
  9. package/build/koffi/linux_loong64/koffi.node +0 -0
  10. package/build/koffi/linux_riscv64d/koffi.node +0 -0
  11. package/build/koffi/linux_x64/koffi.node +0 -0
  12. package/build/koffi/musl_arm64/koffi.node +0 -0
  13. package/build/koffi/musl_x64/koffi.node +0 -0
  14. package/build/koffi/openbsd_ia32/koffi.node +0 -0
  15. package/build/koffi/openbsd_x64/koffi.node +0 -0
  16. package/build/koffi/win32_arm64/koffi.node +0 -0
  17. package/build/koffi/win32_ia32/koffi.node +0 -0
  18. package/build/koffi/win32_x64/koffi.node +0 -0
  19. package/index.d.ts +11 -9
  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 +222 -219
  24. package/src/koffi/src/abi_arm32_asm.S +1 -29
  25. package/src/koffi/src/abi_arm64.cc +257 -235
  26. package/src/koffi/src/abi_arm64_asm.S +1 -32
  27. package/src/koffi/src/abi_arm64_asm.asm +1 -23
  28. package/src/koffi/src/abi_loong64_asm.S +1 -25
  29. package/src/koffi/src/abi_riscv64.cc +220 -217
  30. package/src/koffi/src/abi_riscv64_asm.S +1 -25
  31. package/src/koffi/src/abi_x64_sysv.cc +196 -192
  32. package/src/koffi/src/abi_x64_sysv_asm.S +1 -31
  33. package/src/koffi/src/abi_x64_win.cc +188 -172
  34. package/src/koffi/src/abi_x64_win_asm.S +1 -19
  35. package/src/koffi/src/abi_x64_win_asm.asm +1 -21
  36. package/src/koffi/src/abi_x86.cc +224 -189
  37. package/src/koffi/src/abi_x86_asm.S +6 -25
  38. package/src/koffi/src/abi_x86_asm.asm +9 -22
  39. package/src/koffi/src/call.cc +246 -428
  40. package/src/koffi/src/call.hh +9 -8
  41. package/src/koffi/src/ffi.cc +140 -87
  42. package/src/koffi/src/ffi.hh +12 -58
  43. package/src/koffi/src/primitives.inc +39 -0
  44. package/src/koffi/src/trampolines/armasm.inc +0 -32770
  45. package/src/koffi/src/trampolines/gnu.inc +0 -24578
  46. package/src/koffi/src/trampolines/masm32.inc +0 -32770
  47. package/src/koffi/src/trampolines/masm64.inc +0 -32770
  48. package/src/koffi/src/trampolines/prototypes.inc +16385 -16385
  49. package/src/koffi/src/util.cc +149 -107
  50. package/src/koffi/src/util.hh +76 -40
@@ -532,7 +532,7 @@ const char *GetValueType(const InstanceData *instance, Napi::Value value)
532
532
  if (CheckValueTag(value, &TypeInfoMarker))
533
533
  return "Type";
534
534
  for (const TypeInfo &type: instance->types) {
535
- if (type.ref.marker && CheckValueTag(value, type.ref.marker))
535
+ if (type.ref.type && CheckValueTag(value, type.ref.type))
536
536
  return type.name;
537
537
  }
538
538
 
@@ -857,8 +857,7 @@ void DecodeObject(Napi::Object obj, const uint8_t *origin, const TypeInfo *type)
857
857
  const char32_t *str32 = *(const char32_t **)src;
858
858
  obj.Set(member.name, str32 ? MakeStringFromUTF32(env, str32) : env.Null());
859
859
  } break;
860
- case PrimitiveKind::Pointer:
861
- case PrimitiveKind::Callback: {
860
+ case PrimitiveKind::Pointer: {
862
861
  void *ptr2 = *(void **)src;
863
862
 
864
863
  if (member.countedby >= 0) {
@@ -867,19 +866,25 @@ void DecodeObject(Napi::Object obj, const uint8_t *origin, const TypeInfo *type)
867
866
 
868
867
  Napi::Value value = DecodeArray(env, (const uint8_t *)ptr2, member.type, len);
869
868
  obj.Set(member.name, value);
870
- } else if (ptr2) {
871
- Napi::External<void> external = Napi::External<void>::New(env, ptr2);
872
- SetValueTag(external, member.type->ref.marker);
873
-
874
- obj.Set(member.name, external);
875
869
  } else {
876
- obj.Set(member.name, env.Null());
870
+ Napi::Value p = ptr2 ? WrapPointer(env, member.type->ref.type, ptr2) : env.Null();
871
+ obj.Set(member.name, p);
877
872
  }
878
873
 
879
874
  if (member.type->dispose) {
880
875
  member.type->dispose(env, member.type, ptr2);
881
876
  }
882
877
  } break;
878
+ case PrimitiveKind::Callback: {
879
+ void *ptr2 = *(void **)src;
880
+
881
+ Napi::Value p = ptr2 ? WrapCallback(env, member.type->ref.type, ptr2) : env.Null();
882
+ obj.Set(member.name, p);
883
+
884
+ if (member.type->dispose) {
885
+ member.type->dispose(env, member.type, ptr2);
886
+ }
887
+ } break;
883
888
  case PrimitiveKind::Record:
884
889
  case PrimitiveKind::Union: {
885
890
  Napi::Object obj2 = DecodeObject(env, src, member.type);
@@ -936,7 +941,8 @@ Napi::Value DecodeArray(Napi::Env env, const uint8_t *origin, const TypeInfo *ty
936
941
  \
937
942
  return array; \
938
943
  } while (false)
939
- #define POP_NUMBER_ARRAY(TypedArrayType, CType) \
944
+
945
+ #define POP_NUMBERS(TypedArrayType, CType) \
940
946
  do { \
941
947
  if (type->hint == ArrayHint::Array) { \
942
948
  POP_ARRAY({ \
@@ -952,7 +958,7 @@ Napi::Value DecodeArray(Napi::Env env, const uint8_t *origin, const TypeInfo *ty
952
958
  return array; \
953
959
  } \
954
960
  } while (false)
955
- #define POP_NUMBER_ARRAY_SWAP(TypedArrayType, CType) \
961
+ #define POP_NUMBERS_SWAP(TypedArrayType, CType) \
956
962
  do { \
957
963
  if (type->hint == ArrayHint::Array) { \
958
964
  POP_ARRAY({ \
@@ -988,9 +994,9 @@ Napi::Value DecodeArray(Napi::Env env, const uint8_t *origin, const TypeInfo *ty
988
994
  return str;
989
995
  }
990
996
 
991
- POP_NUMBER_ARRAY(Int8Array, int8_t);
997
+ POP_NUMBERS(Int8Array, int8_t);
992
998
  } break;
993
- case PrimitiveKind::UInt8: { POP_NUMBER_ARRAY(Uint8Array, uint8_t); } break;
999
+ case PrimitiveKind::UInt8: { POP_NUMBERS(Uint8Array, uint8_t); } break;
994
1000
  case PrimitiveKind::Int16: {
995
1001
  if (type->hint == ArrayHint::String) {
996
1002
  const char16_t *ptr = (const char16_t *)origin;
@@ -1000,11 +1006,11 @@ Napi::Value DecodeArray(Napi::Env env, const uint8_t *origin, const TypeInfo *ty
1000
1006
  return str;
1001
1007
  }
1002
1008
 
1003
- POP_NUMBER_ARRAY(Int16Array, int16_t);
1009
+ POP_NUMBERS(Int16Array, int16_t);
1004
1010
  } break;
1005
- case PrimitiveKind::Int16S: { POP_NUMBER_ARRAY_SWAP(Int16Array, int16_t); } break;
1006
- case PrimitiveKind::UInt16: { POP_NUMBER_ARRAY(Uint16Array, uint16_t); } break;
1007
- case PrimitiveKind::UInt16S: { POP_NUMBER_ARRAY_SWAP(Uint16Array, uint16_t); } break;
1011
+ case PrimitiveKind::Int16S: { POP_NUMBERS_SWAP(Int16Array, int16_t); } break;
1012
+ case PrimitiveKind::UInt16: { POP_NUMBERS(Uint16Array, uint16_t); } break;
1013
+ case PrimitiveKind::UInt16S: { POP_NUMBERS_SWAP(Uint16Array, uint16_t); } break;
1008
1014
  case PrimitiveKind::Int32: {
1009
1015
  if (type->hint == ArrayHint::String) {
1010
1016
  const char32_t *ptr = (const char32_t *)origin;
@@ -1014,11 +1020,11 @@ Napi::Value DecodeArray(Napi::Env env, const uint8_t *origin, const TypeInfo *ty
1014
1020
  return str;
1015
1021
  }
1016
1022
 
1017
- POP_NUMBER_ARRAY(Int32Array, int32_t);
1023
+ POP_NUMBERS(Int32Array, int32_t);
1018
1024
  } break;
1019
- case PrimitiveKind::Int32S: { POP_NUMBER_ARRAY_SWAP(Int32Array, int32_t); } break;
1020
- case PrimitiveKind::UInt32: { POP_NUMBER_ARRAY(Uint32Array, uint32_t); } break;
1021
- case PrimitiveKind::UInt32S: { POP_NUMBER_ARRAY_SWAP(Uint32Array, uint32_t); } break;
1025
+ case PrimitiveKind::Int32S: { POP_NUMBERS_SWAP(Int32Array, int32_t); } break;
1026
+ case PrimitiveKind::UInt32: { POP_NUMBERS(Uint32Array, uint32_t); } break;
1027
+ case PrimitiveKind::UInt32S: { POP_NUMBERS_SWAP(Uint32Array, uint32_t); } break;
1022
1028
  case PrimitiveKind::Int64: {
1023
1029
  POP_ARRAY({
1024
1030
  int64_t v = *(int64_t *)src;
@@ -1061,19 +1067,20 @@ Napi::Value DecodeArray(Napi::Env env, const uint8_t *origin, const TypeInfo *ty
1061
1067
  array.Set(i, str32 ? MakeStringFromUTF32(env, str32) : env.Null());
1062
1068
  });
1063
1069
  } break;
1064
- case PrimitiveKind::Pointer:
1065
- case PrimitiveKind::Callback: {
1070
+ case PrimitiveKind::Pointer: {
1066
1071
  POP_ARRAY({
1067
1072
  void *ptr2 = *(void **)src;
1068
1073
 
1069
- if (ptr2) {
1070
- Napi::External<void> external = Napi::External<void>::New(env, ptr2);
1071
- SetValueTag(external, type->ref.type->ref.marker);
1074
+ Napi::Value p = ptr2 ? WrapPointer(env, type->ref.type->ref.type, ptr2) : env.Null();
1075
+ array.Set(i, p);
1076
+ });
1077
+ } break;
1078
+ case PrimitiveKind::Callback: {
1079
+ POP_ARRAY({
1080
+ void *ptr2 = *(void **)src;
1072
1081
 
1073
- array.Set(i, external);
1074
- } else {
1075
- array.Set(i, env.Null());
1076
- }
1082
+ Napi::Value p = ptr2 ? WrapCallback(env, type->ref.type->ref.type, ptr2) : env.Null();
1083
+ array.Set(i, p);
1077
1084
  });
1078
1085
  } break;
1079
1086
  case PrimitiveKind::Record:
@@ -1089,14 +1096,15 @@ Napi::Value DecodeArray(Napi::Env env, const uint8_t *origin, const TypeInfo *ty
1089
1096
  array.Set(i, value);
1090
1097
  });
1091
1098
  } break;
1092
- case PrimitiveKind::Float32: { POP_NUMBER_ARRAY(Float32Array, float); } break;
1093
- case PrimitiveKind::Float64: { POP_NUMBER_ARRAY(Float64Array, double); } break;
1099
+ case PrimitiveKind::Float32: { POP_NUMBERS(Float32Array, float); } break;
1100
+ case PrimitiveKind::Float64: { POP_NUMBERS(Float64Array, double); } break;
1094
1101
 
1095
1102
  case PrimitiveKind::Prototype: { K_UNREACHABLE(); } break;
1096
1103
  }
1097
1104
 
1098
- #undef POP_NUMBER_ARRAY_SWAP
1099
- #undef POP_NUMBER_ARRAY
1105
+ #undef POP_NUMBERS_SWAP
1106
+ #undef POP_NUMBERS
1107
+
1100
1108
  #undef POP_ARRAY
1101
1109
 
1102
1110
  K_UNREACHABLE();
@@ -1131,14 +1139,15 @@ void DecodeNormalArray(Napi::Array array, const uint8_t *origin, const TypeInfo
1131
1139
  offset += ref->size; \
1132
1140
  } \
1133
1141
  } while (false)
1134
- #define POP_NUMBER_ARRAY(CType) \
1142
+
1143
+ #define POP_NUMBERS(CType) \
1135
1144
  do { \
1136
1145
  POP_ARRAY({ \
1137
1146
  double d = (double)*(CType *)src; \
1138
1147
  array.Set(i, Napi::Number::New(env, d)); \
1139
1148
  }); \
1140
1149
  } while (false)
1141
- #define POP_NUMBER_ARRAY_SWAP(CType) \
1150
+ #define POP_NUMBERS_SWAP(CType) \
1142
1151
  do { \
1143
1152
  POP_ARRAY({ \
1144
1153
  CType v = *(CType *)src; \
@@ -1156,16 +1165,16 @@ void DecodeNormalArray(Napi::Array array, const uint8_t *origin, const TypeInfo
1156
1165
  array.Set(i, Napi::Boolean::New(env, b));
1157
1166
  });
1158
1167
  } break;
1159
- case PrimitiveKind::Int8: { POP_NUMBER_ARRAY(int8_t); } break;
1160
- case PrimitiveKind::UInt8: { POP_NUMBER_ARRAY(uint8_t); } break;
1161
- case PrimitiveKind::Int16: { POP_NUMBER_ARRAY(int16_t); } break;
1162
- case PrimitiveKind::Int16S: { POP_NUMBER_ARRAY_SWAP(int16_t); } break;
1163
- case PrimitiveKind::UInt16: { POP_NUMBER_ARRAY(uint16_t); } break;
1164
- case PrimitiveKind::UInt16S: { POP_NUMBER_ARRAY_SWAP(uint16_t); } break;
1165
- case PrimitiveKind::Int32: { POP_NUMBER_ARRAY(int32_t); } break;
1166
- case PrimitiveKind::Int32S: { POP_NUMBER_ARRAY_SWAP(int32_t); } break;
1167
- case PrimitiveKind::UInt32: { POP_NUMBER_ARRAY(uint32_t); } break;
1168
- case PrimitiveKind::UInt32S: { POP_NUMBER_ARRAY_SWAP(uint32_t); } break;
1168
+ case PrimitiveKind::Int8: { POP_NUMBERS(int8_t); } break;
1169
+ case PrimitiveKind::UInt8: { POP_NUMBERS(uint8_t); } break;
1170
+ case PrimitiveKind::Int16: { POP_NUMBERS(int16_t); } break;
1171
+ case PrimitiveKind::Int16S: { POP_NUMBERS_SWAP(int16_t); } break;
1172
+ case PrimitiveKind::UInt16: { POP_NUMBERS(uint16_t); } break;
1173
+ case PrimitiveKind::UInt16S: { POP_NUMBERS_SWAP(uint16_t); } break;
1174
+ case PrimitiveKind::Int32: { POP_NUMBERS(int32_t); } break;
1175
+ case PrimitiveKind::Int32S: { POP_NUMBERS_SWAP(int32_t); } break;
1176
+ case PrimitiveKind::UInt32: { POP_NUMBERS(uint32_t); } break;
1177
+ case PrimitiveKind::UInt32S: { POP_NUMBERS_SWAP(uint32_t); } break;
1169
1178
  case PrimitiveKind::Int64: {
1170
1179
  POP_ARRAY({
1171
1180
  int64_t v = *(int64_t *)src;
@@ -1220,19 +1229,24 @@ void DecodeNormalArray(Napi::Array array, const uint8_t *origin, const TypeInfo
1220
1229
  }
1221
1230
  });
1222
1231
  } break;
1223
- case PrimitiveKind::Pointer:
1224
- case PrimitiveKind::Callback: {
1232
+ case PrimitiveKind::Pointer: {
1225
1233
  POP_ARRAY({
1226
1234
  void *ptr2 = *(void **)src;
1227
1235
 
1228
- if (ptr2) {
1229
- Napi::External<void> external = Napi::External<void>::New(env, ptr2);
1230
- SetValueTag(external, ref->ref.marker);
1236
+ Napi::Value p = ptr2 ? WrapPointer(env, ref->ref.type, ptr2) : env.Null();
1237
+ array.Set(i, p);
1231
1238
 
1232
- array.Set(i, external);
1233
- } else {
1234
- array.Set(i, env.Null());
1239
+ if (ref->dispose) {
1240
+ ref->dispose(env, ref, ptr2);
1235
1241
  }
1242
+ });
1243
+ } break;
1244
+ case PrimitiveKind::Callback: {
1245
+ POP_ARRAY({
1246
+ void *ptr2 = *(void **)src;
1247
+
1248
+ Napi::Value p = ptr2 ? WrapCallback(env, ref->ref.type, ptr2) : env.Null();
1249
+ array.Set(i, p);
1236
1250
 
1237
1251
  if (ref->dispose) {
1238
1252
  ref->dispose(env, ref, ptr2);
@@ -1252,14 +1266,15 @@ void DecodeNormalArray(Napi::Array array, const uint8_t *origin, const TypeInfo
1252
1266
  array.Set(i, value);
1253
1267
  });
1254
1268
  } break;
1255
- case PrimitiveKind::Float32: { POP_NUMBER_ARRAY(float); } break;
1256
- case PrimitiveKind::Float64: { POP_NUMBER_ARRAY(double); } break;
1269
+ case PrimitiveKind::Float32: { POP_NUMBERS(float); } break;
1270
+ case PrimitiveKind::Float64: { POP_NUMBERS(double); } break;
1257
1271
 
1258
1272
  case PrimitiveKind::Prototype: { K_UNREACHABLE(); } break;
1259
1273
  }
1260
1274
 
1261
- #undef POP_NUMBER_ARRAY_SWAP
1262
- #undef POP_NUMBER_ARRAY
1275
+ #undef POP_NUMBERS_SWAP
1276
+ #undef POP_NUMBERS
1277
+
1263
1278
  #undef POP_ARRAY
1264
1279
  }
1265
1280
 
@@ -1294,14 +1309,9 @@ Napi::Value Decode(Napi::Value value, Size offset, const TypeInfo *type, const S
1294
1309
  Napi::Env env = value.Env();
1295
1310
  InstanceData *instance = env.GetInstanceData<InstanceData>();
1296
1311
 
1297
- const uint8_t *ptr = nullptr;
1298
-
1299
- if (value.IsExternal()) {
1300
- Napi::External<void> external = value.As<Napi::External<void>>();
1301
- ptr = (const uint8_t *)external.Data();
1302
- } else if (IsRawBuffer(value)) {
1303
- Span<uint8_t> buffer = GetRawBuffer(value);
1312
+ const uint8_t *src = nullptr;
1304
1313
 
1314
+ if (Span<uint8_t> buffer = {}; TryBuffer(value, &buffer)) {
1305
1315
  if (offset < 0) [[unlikely]] {
1306
1316
  ThrowError<Napi::Error>(env, "Offset must be >= 0");
1307
1317
  return env.Null();
@@ -1312,17 +1322,19 @@ Napi::Value Decode(Napi::Value value, Size offset, const TypeInfo *type, const S
1312
1322
  return env.Null();
1313
1323
  }
1314
1324
 
1315
- ptr = (const uint8_t *)buffer.ptr;
1325
+ src = (const uint8_t *)buffer.ptr;
1326
+ } else if (void *ptr = nullptr; TryPointer(value, &ptr)) {
1327
+ src = (const uint8_t *)ptr;
1316
1328
  } else {
1317
- ThrowError<Napi::TypeError>(env, "Unexpected %1 value for variable, expected external or TypedArray", GetValueType(instance, value));
1329
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for variable, expected pointer", GetValueType(instance, value));
1318
1330
  return env.Null();
1319
1331
  }
1320
1332
 
1321
- if (!ptr)
1333
+ if (!src)
1322
1334
  return env.Null();
1323
- ptr += offset;
1335
+ src += offset;
1324
1336
 
1325
- Napi::Value ret = Decode(env, ptr, type, len);
1337
+ Napi::Value ret = Decode(env, src, type, len);
1326
1338
  return ret;
1327
1339
  }
1328
1340
 
@@ -1429,10 +1441,13 @@ Napi::Value Decode(Napi::Env env, const uint8_t *ptr, const TypeInfo *type, cons
1429
1441
  return str32 ? MakeStringFromUTF32(env, str32) : env.Null();
1430
1442
  }
1431
1443
  } break;
1432
- case PrimitiveKind::Pointer:
1444
+ case PrimitiveKind::Pointer: {
1445
+ void *ptr2 = *(void **)ptr;
1446
+ return ptr2 ? WrapPointer(env, type->ref.type, ptr2) : env.Null();
1447
+ } break;
1433
1448
  case PrimitiveKind::Callback: {
1434
1449
  void *ptr2 = *(void **)ptr;
1435
- return ptr2 ? Napi::External<void>::New(env, ptr2, [](Napi::Env, void *) {}) : env.Null();
1450
+ return ptr2 ? WrapCallback(env, type->ref.type, ptr2) : env.Null();
1436
1451
  } break;
1437
1452
  case PrimitiveKind::Record:
1438
1453
  case PrimitiveKind::Union: {
@@ -1462,10 +1477,12 @@ Napi::Value Decode(Napi::Env env, const uint8_t *ptr, const TypeInfo *type, cons
1462
1477
 
1463
1478
  memcpy((void *)func, proto, K_SIZE(*proto));
1464
1479
  memset((void *)&func->parameters, 0, K_SIZE(func->parameters));
1465
- func->parameters = proto->parameters;
1480
+ memset((void *)&func->primitives, 0, K_SIZE(func->primitives));
1466
1481
 
1467
1482
  func->name = "<anonymous>";
1468
1483
  func->native = (void *)ptr;
1484
+ func->parameters = proto->parameters;
1485
+ func->primitives = proto->primitives;
1469
1486
 
1470
1487
  // Fix back parameter offset
1471
1488
  for (ParameterInfo &param: func->parameters) {
@@ -1489,14 +1506,9 @@ bool Encode(Napi::Value ref, Size offset, Napi::Value value, const TypeInfo *typ
1489
1506
  Napi::Env env = ref.Env();
1490
1507
  InstanceData *instance = env.GetInstanceData<InstanceData>();
1491
1508
 
1492
- uint8_t *ptr = nullptr;
1493
-
1494
- if (ref.IsExternal()) {
1495
- Napi::External<void> external = ref.As<Napi::External<void>>();
1496
- ptr = (uint8_t *)external.Data();
1497
- } else if (IsRawBuffer(ref)) {
1498
- Span<uint8_t> buffer = GetRawBuffer(ref);
1509
+ uint8_t *dest = nullptr;
1499
1510
 
1511
+ if (Span<uint8_t> buffer = {}; TryBuffer(ref, &buffer)) {
1500
1512
  if (offset < 0) [[unlikely]] {
1501
1513
  ThrowError<Napi::Error>(env, "Offset must be >= 0");
1502
1514
  return env.Null();
@@ -1507,19 +1519,21 @@ bool Encode(Napi::Value ref, Size offset, Napi::Value value, const TypeInfo *typ
1507
1519
  return env.Null();
1508
1520
  }
1509
1521
 
1510
- ptr = (uint8_t *)buffer.ptr;
1522
+ dest = (uint8_t *)buffer.ptr;
1523
+ } else if (void *ptr = nullptr; TryPointer(ref, &ptr)) {
1524
+ dest = (uint8_t *)ptr;
1511
1525
  } else {
1512
- ThrowError<Napi::TypeError>(env, "Unexpected %1 value for reference, expected external or TypedArray", GetValueType(instance, ref));
1526
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for reference, expected pointer", GetValueType(instance, ref));
1513
1527
  return env.Null();
1514
1528
  }
1515
1529
 
1516
- if (!ptr) [[unlikely]] {
1530
+ if (!dest) [[unlikely]] {
1517
1531
  ThrowError<Napi::Error>(env, "Cannot encode data in NULL pointer");
1518
1532
  return env.Null();
1519
1533
  }
1520
- ptr += offset;
1534
+ dest += offset;
1521
1535
 
1522
- return Encode(env, ptr, value, type, len);
1536
+ return Encode(env, dest, value, type, len);
1523
1537
  }
1524
1538
 
1525
1539
  bool Encode(Napi::Env env, uint8_t *origin, Napi::Value value, const TypeInfo *type, const Size *len)
@@ -1543,22 +1557,24 @@ bool Encode(Napi::Env env, uint8_t *origin, Napi::Value value, const TypeInfo *t
1543
1557
 
1544
1558
  #define PUSH_INTEGER(CType) \
1545
1559
  do { \
1546
- if (!value.IsNumber() && !value.IsBigInt()) [[unlikely]] { \
1560
+ CType v; \
1561
+ \
1562
+ if (!TryNumber(value, &v)) [[unlikely]] { \
1547
1563
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected number", GetValueType(instance, value)); \
1548
1564
  return false; \
1549
1565
  } \
1550
1566
  \
1551
- CType v = GetNumber<CType>(value); \
1552
1567
  *(CType *)origin = v; \
1553
1568
  } while (false)
1554
1569
  #define PUSH_INTEGER_SWAP(CType) \
1555
1570
  do { \
1556
- if (!value.IsNumber() && !value.IsBigInt()) [[unlikely]] { \
1571
+ CType v; \
1572
+ \
1573
+ if (!TryNumber(value, &v)) [[unlikely]] { \
1557
1574
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected number", GetValueType(instance, value)); \
1558
1575
  return false; \
1559
1576
  } \
1560
1577
  \
1561
- CType v = GetNumber<CType>(value); \
1562
1578
  *(CType *)origin = ReverseBytes(v); \
1563
1579
  } while (false)
1564
1580
 
@@ -1566,12 +1582,14 @@ bool Encode(Napi::Env env, uint8_t *origin, Napi::Value value, const TypeInfo *t
1566
1582
  case PrimitiveKind::Void: { K_UNREACHABLE(); } break;
1567
1583
 
1568
1584
  case PrimitiveKind::Bool: {
1569
- if (!value.IsBoolean()) [[unlikely]] {
1585
+ bool b;
1586
+ napi_status status = napi_get_value_bool(env, value, &b);
1587
+
1588
+ if (status != napi_ok) [[unlikely]] {
1570
1589
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected boolean", GetValueType(instance, value));
1571
1590
  return false;
1572
1591
  }
1573
1592
 
1574
- bool b = value.As<Napi::Boolean>();
1575
1593
  *(bool *)origin = b;
1576
1594
  } break;
1577
1595
  case PrimitiveKind::Int8: { PUSH_INTEGER(int8_t); } break;
@@ -1629,8 +1647,7 @@ bool Encode(Napi::Env env, uint8_t *origin, Napi::Value value, const TypeInfo *t
1629
1647
  Napi::Array array = value.As<Napi::Array>();
1630
1648
  if (!call.PushNormalArray(array, type, type->size, origin))
1631
1649
  return false;
1632
- } else if (IsRawBuffer(value)) {
1633
- Span<const uint8_t> buffer = GetRawBuffer(value);
1650
+ } else if (Span<uint8_t> buffer = {}; TryBuffer(value, &buffer)) {
1634
1651
  call.PushBuffer(buffer, type, origin);
1635
1652
  } else if (value.IsString()) {
1636
1653
  if (!call.PushStringArray(value, type, origin))
@@ -1641,35 +1658,34 @@ bool Encode(Napi::Env env, uint8_t *origin, Napi::Value value, const TypeInfo *t
1641
1658
  }
1642
1659
  } break;
1643
1660
  case PrimitiveKind::Float32: {
1644
- if (!value.IsNumber() && !value.IsBigInt()) [[unlikely]] {
1661
+ float f;
1662
+
1663
+ if (!TryNumber(value, &f)) [[unlikely]] {
1645
1664
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected number", GetValueType(instance, value));
1646
1665
  return false;
1647
1666
  }
1648
1667
 
1649
- float f = GetNumber<float>(value);
1650
1668
  *(float *)origin = f;
1651
1669
  } break;
1652
1670
  case PrimitiveKind::Float64: {
1653
- if (!value.IsNumber() && !value.IsBigInt()) [[unlikely]] {
1671
+ double d;
1672
+
1673
+ if (!TryNumber(value, &d)) [[unlikely]] {
1654
1674
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected number", GetValueType(instance, value));
1655
1675
  return false;
1656
1676
  }
1657
1677
 
1658
- double d = GetNumber<double>(value);
1659
1678
  *(double *)origin = d;
1660
1679
  } break;
1661
1680
  case PrimitiveKind::Callback: {
1662
1681
  void *ptr;
1663
1682
 
1664
- if (value.IsFunction()) {
1665
- ThrowError<Napi::Error>(env, "Cannot encode non-registered callback");
1666
- return false;
1667
- } else if (CheckValueTag(value, type->ref.marker)) {
1668
- ptr = value.As<Napi::External<void>>().Data();
1669
- } else if (IsNullOrUndefined(value)) {
1670
- ptr = nullptr;
1671
- } else {
1672
- ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected %2", GetValueType(instance, value), type->name);
1683
+ if (!TryPointer(value, &ptr)) [[unlikely]] {
1684
+ if (value.IsFunction()) {
1685
+ ThrowError<Napi::Error>(env, "Cannot encode non-registered callback");
1686
+ } else {
1687
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected %2", GetValueType(instance, value), type->name);
1688
+ }
1673
1689
  return false;
1674
1690
  }
1675
1691
 
@@ -1741,6 +1757,32 @@ Napi::Function WrapFunction(Napi::Env env, const FunctionInfo *func)
1741
1757
  return wrapper;
1742
1758
  }
1743
1759
 
1760
+ Napi::Value WrapPointer(Napi::Env env, const TypeInfo *ref, void *ptr)
1761
+ {
1762
+ InstanceData *instance = env.GetInstanceData<InstanceData>();
1763
+
1764
+ if (instance->config.fast_pointers) {
1765
+ Napi::BigInt big = Napi::BigInt::New(env, (uint64_t)(uintptr_t)ptr);
1766
+ return big;
1767
+ } else {
1768
+ Napi::External<void> external = Napi::External<void>::New(env, ptr);
1769
+ return external;
1770
+ }
1771
+ }
1772
+
1773
+ Napi::Value WrapCallback(Napi::Env env, const TypeInfo *ref, void *ptr)
1774
+ {
1775
+ InstanceData *instance = env.GetInstanceData<InstanceData>();
1776
+
1777
+ if (instance->config.fast_callbacks) {
1778
+ Napi::BigInt big = Napi::BigInt::New(env, (uint64_t)(uintptr_t)ptr);
1779
+ return big;
1780
+ } else {
1781
+ Napi::External<void> external = Napi::External<void>::New(env, ptr);
1782
+ return external;
1783
+ }
1784
+ }
1785
+
1744
1786
  bool DetectCallConvention(Span<const char> name, CallConvention *out_convention)
1745
1787
  {
1746
1788
  if (name == "__cdecl") {
@@ -1797,7 +1839,7 @@ void DumpMemory(const char *type, Span<const uint8_t> bytes)
1797
1839
  PrintLn(StdErr, "%1 at 0x%2 (%3):", type, bytes.ptr, FmtMemSize(bytes.len));
1798
1840
 
1799
1841
  for (const uint8_t *ptr = bytes.begin(); ptr < bytes.end();) {
1800
- Print(StdErr, " [0x%1 %2 %3] ", FmtInt((uintptr_t)ptr, 16),
1842
+ Print(StdErr, " [0x%1 %2 %3] ", FmtHex((uintptr_t)ptr, 16),
1801
1843
  FmtInt((ptr - bytes.begin()) / sizeof(void *), 4, ' '),
1802
1844
  FmtInt(ptr - bytes.begin(), 4, ' '));
1803
1845
  for (int i = 0; ptr < bytes.end() && i < (int)sizeof(void *); i++, ptr++) {
@@ -98,64 +98,97 @@ static inline bool IsObject(Napi::Value value)
98
98
  return value.IsObject() && !IsNullOrUndefined(value) && !value.IsArray();
99
99
  }
100
100
 
101
- static inline bool IsRawBuffer(Napi::Value value)
102
- {
103
- return value.IsTypedArray() || value.IsArrayBuffer();
104
- }
105
-
106
- static inline Span<uint8_t> GetRawBuffer(Napi::Value value)
101
+ template <typename T>
102
+ bool TryNumber(Napi::Value value, T *out_value)
107
103
  {
108
- if (value.IsTypedArray()) {
109
- napi_typedarray_type type = napi_int8_array;
110
- size_t length = 0;
111
- void *ptr = nullptr;
104
+ // Assume number first
105
+ {
106
+ T v;
107
+ napi_status status;
108
+
109
+ if constexpr (std::is_same_v<T, double>) {
110
+ status = napi_get_value_double(value.Env(), value, &v);
111
+ } else if constexpr (std::is_same_v<T, float>) {
112
+ double d;
113
+ status = napi_get_value_double(value.Env(), value, &d);
114
+ v = (float)d;
115
+ } else {
116
+ int64_t i64;
117
+ status = napi_get_value_int64(value.Env(), value, &i64);
118
+ v = (T)i64;
119
+ }
112
120
 
113
- napi_get_typedarray_info(value.Env(), value, &type, &length, &ptr, nullptr, nullptr);
114
-
115
- switch (type) {
116
- case napi_int8_array: { length *= 1; } break;
117
- case napi_uint8_array: { length *= 1; } break;
118
- case napi_uint8_clamped_array: { length *= 1; } break;
119
- case napi_int16_array: { length *= 2; } break;
120
- case napi_uint16_array: { length *= 2; } break;
121
- case napi_int32_array: { length *= 4; } break;
122
- case napi_uint32_array: { length *= 4; } break;
123
- case napi_float16_array: { length *= 2; } break;
124
- case napi_float32_array: { length *= 4; } break;
125
- case napi_float64_array: { length *= 8; } break;
126
- case napi_bigint64_array: { length *= 8; } break;
127
- case napi_biguint64_array: { length *= 8; } break;
121
+ if (status == napi_ok) [[likely]] {
122
+ *out_value = v;
123
+ return true;
128
124
  }
125
+ }
126
+
127
+ if (value.IsBigInt()) {
128
+ Napi::BigInt bigint = value.As<Napi::BigInt>();
129
+
130
+ bool lossless;
131
+ *out_value = (T)bigint.Uint64Value(&lossless);
132
+
133
+ return true;
134
+ }
135
+
136
+ return false;
137
+ }
129
138
 
130
- return MakeSpan((uint8_t *)ptr, (Size)length);
139
+ static inline bool TryPointer(Napi::Value value, void **out_ptr)
140
+ {
141
+ if (IsNullOrUndefined(value)) {
142
+ *out_ptr = nullptr;
143
+ return true;
144
+ } else if (uintptr_t ptr = 0; TryNumber(value, &ptr)) {
145
+ *out_ptr = (void *)ptr;
146
+ return true;
147
+ } else if (value.IsTypedArray()) {
148
+ napi_get_typedarray_info(value.Env(), value, nullptr, nullptr, out_ptr, nullptr, nullptr);
149
+ return true;
150
+ } else if (value.IsExternal()) {
151
+ Napi::External<void> external = value.As<Napi::External<void>>();
152
+
153
+ *out_ptr = (void *)external.Data();
154
+ return true;
131
155
  } else if (value.IsArrayBuffer()) {
132
156
  Napi::ArrayBuffer buffer = value.As<Napi::ArrayBuffer>();
133
157
 
134
- return MakeSpan((uint8_t *)buffer.Data(), (Size)buffer.ByteLength());
158
+ *out_ptr = (void *)buffer.Data();
159
+ return true;
135
160
  }
136
161
 
137
- K_UNREACHABLE();
162
+ return false;
138
163
  }
139
164
 
140
- int GetTypedArrayType(const TypeInfo *type);
141
-
142
- template <typename T>
143
- T GetNumber(Napi::Value value)
165
+ static inline bool TryBuffer(Napi::Value value, Span<uint8_t> *out_buffer)
144
166
  {
145
- K_ASSERT(value.IsNumber() || value.IsBigInt());
167
+ // Before somewhere around Node 20.12, napi_get_buffer_info() would assert/crash
168
+ // when used with something it did not support, instead of returning napi_invalid_arg.
169
+ // So we need to call napi_is_buffer(), at least for now.
146
170
 
147
- if (value.IsNumber()) [[likely]] {
148
- return (T)value.As<Napi::Number>().DoubleValue();
149
- } else if (value.IsBigInt()) {
150
- Napi::BigInt bigint = value.As<Napi::BigInt>();
171
+ if (value.IsBuffer()) {
172
+ void *ptr = nullptr;
173
+ size_t length = 0;
151
174
 
152
- bool lossless;
153
- return (T)bigint.Uint64Value(&lossless);
175
+ // Assume it works
176
+ napi_get_buffer_info(value.Env(), value, &ptr, &length);
177
+
178
+ *out_buffer = MakeSpan((uint8_t *)ptr, (Size)length);
179
+ return true;
180
+ } else if (value.IsArrayBuffer()) {
181
+ Napi::ArrayBuffer buffer = value.As<Napi::ArrayBuffer>();
182
+
183
+ *out_buffer = MakeSpan((uint8_t *)buffer.Data(), (Size)buffer.ByteLength());
184
+ return true;
154
185
  }
155
186
 
156
- K_UNREACHABLE();
187
+ return false;
157
188
  }
158
189
 
190
+ int GetTypedArrayType(const TypeInfo *type);
191
+
159
192
  template <typename T>
160
193
  Size NullTerminatedLength(const T *ptr)
161
194
  {
@@ -227,6 +260,9 @@ static inline Napi::Array GetOwnPropertyNames(Napi::Object obj)
227
260
 
228
261
  Napi::Function WrapFunction(Napi::Env env, const FunctionInfo *func);
229
262
 
263
+ Napi::Value WrapPointer(Napi::Env env, const TypeInfo *ref, void *ptr);
264
+ Napi::Value WrapCallback(Napi::Env env, const TypeInfo *ref, void *ptr);
265
+
230
266
  bool DetectCallConvention(Span<const char> name, CallConvention *out_convention);
231
267
 
232
268
  int AnalyseFlat(const TypeInfo *type, FunctionRef<void(const TypeInfo *type, int offset, int count)> func);