koffi 2.0.1 → 2.1.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/CMakeLists.txt +2 -9
  2. package/ChangeLog.md +17 -0
  3. package/benchmark/atoi_koffi.js +12 -8
  4. package/benchmark/atoi_napi.js +12 -8
  5. package/benchmark/atoi_node_ffi.js +11 -10
  6. package/benchmark/raylib_cc.cc +12 -9
  7. package/benchmark/raylib_koffi.js +15 -13
  8. package/benchmark/raylib_node_ffi.js +15 -13
  9. package/benchmark/raylib_node_raylib.js +14 -11
  10. package/build/qemu/2.1.0-beta.3/koffi_darwin_arm64.tar.gz +0 -0
  11. package/build/qemu/2.1.0-beta.3/koffi_darwin_x64.tar.gz +0 -0
  12. package/build/qemu/2.1.0-beta.3/koffi_freebsd_arm64.tar.gz +0 -0
  13. package/build/qemu/2.1.0-beta.3/koffi_freebsd_ia32.tar.gz +0 -0
  14. package/build/qemu/2.1.0-beta.3/koffi_freebsd_x64.tar.gz +0 -0
  15. package/build/qemu/2.1.0-beta.3/koffi_linux_arm32hf.tar.gz +0 -0
  16. package/build/qemu/2.1.0-beta.3/koffi_linux_arm64.tar.gz +0 -0
  17. package/build/qemu/2.1.0-beta.3/koffi_linux_ia32.tar.gz +0 -0
  18. package/build/qemu/2.1.0-beta.3/koffi_linux_riscv64hf64.tar.gz +0 -0
  19. package/build/qemu/2.1.0-beta.3/koffi_linux_x64.tar.gz +0 -0
  20. package/build/qemu/2.1.0-beta.3/koffi_openbsd_ia32.tar.gz +0 -0
  21. package/build/qemu/2.1.0-beta.3/koffi_openbsd_x64.tar.gz +0 -0
  22. package/build/qemu/2.1.0-beta.3/koffi_win32_arm64.tar.gz +0 -0
  23. package/build/qemu/2.1.0-beta.3/koffi_win32_ia32.tar.gz +0 -0
  24. package/build/qemu/2.1.0-beta.3/koffi_win32_x64.tar.gz +0 -0
  25. package/doc/Makefile +1 -1
  26. package/doc/changes.md +12 -8
  27. package/doc/conf.py +5 -0
  28. package/doc/dist/doctrees/changes.doctree +0 -0
  29. package/doc/dist/doctrees/environment.pickle +0 -0
  30. package/doc/dist/doctrees/functions.doctree +0 -0
  31. package/doc/dist/doctrees/index.doctree +0 -0
  32. package/doc/dist/doctrees/types.doctree +0 -0
  33. package/doc/dist/html/.buildinfo +1 -1
  34. package/doc/dist/html/_sources/changes.md.txt +12 -8
  35. package/doc/dist/html/_sources/functions.md.txt +71 -5
  36. package/doc/dist/html/_sources/types.md.txt +147 -159
  37. package/doc/dist/html/benchmarks.html +2 -3
  38. package/doc/dist/html/changes.html +64 -35
  39. package/doc/dist/html/contribute.html +2 -3
  40. package/doc/dist/html/functions.html +73 -12
  41. package/doc/dist/html/genindex.html +2 -3
  42. package/doc/dist/html/index.html +6 -7
  43. package/doc/dist/html/memory.html +2 -3
  44. package/doc/dist/html/objects.inv +0 -0
  45. package/doc/dist/html/platforms.html +3 -4
  46. package/doc/dist/html/search.html +2 -3
  47. package/doc/dist/html/searchindex.js +1 -1
  48. package/doc/dist/html/start.html +2 -3
  49. package/doc/dist/html/types.html +238 -237
  50. package/doc/functions.md +71 -5
  51. package/doc/make.bat +1 -1
  52. package/doc/templates/badges.html +1 -2
  53. package/doc/types.md +149 -159
  54. package/package.json +3 -2
  55. package/qemu/qemu.js +1 -1
  56. package/src/abi_arm32.cc +208 -102
  57. package/src/abi_arm64.cc +239 -55
  58. package/src/abi_riscv64.cc +128 -40
  59. package/src/abi_x64_sysv.cc +135 -41
  60. package/src/abi_x64_win.cc +134 -40
  61. package/src/abi_x86.cc +182 -67
  62. package/src/call.cc +241 -26
  63. package/src/call.hh +15 -3
  64. package/src/ffi.cc +120 -31
  65. package/src/ffi.hh +19 -0
  66. package/src/index.js +4 -2
  67. package/src/parser.cc +3 -5
  68. package/src/util.cc +44 -1
  69. package/src/util.hh +4 -0
  70. package/test/async.js +1 -2
  71. package/test/callbacks.js +2 -3
  72. package/test/misc.c +64 -1
  73. package/test/raylib.js +1 -1
  74. package/test/sqlite.js +3 -3
  75. package/test/sync.js +108 -3
  76. package/build/qemu/2.0.1/koffi_darwin_arm64.tar.gz +0 -0
  77. package/build/qemu/2.0.1/koffi_darwin_x64.tar.gz +0 -0
  78. package/build/qemu/2.0.1/koffi_freebsd_arm64.tar.gz +0 -0
  79. package/build/qemu/2.0.1/koffi_freebsd_ia32.tar.gz +0 -0
  80. package/build/qemu/2.0.1/koffi_freebsd_x64.tar.gz +0 -0
  81. package/build/qemu/2.0.1/koffi_linux_arm32hf.tar.gz +0 -0
  82. package/build/qemu/2.0.1/koffi_linux_arm64.tar.gz +0 -0
  83. package/build/qemu/2.0.1/koffi_linux_ia32.tar.gz +0 -0
  84. package/build/qemu/2.0.1/koffi_linux_riscv64hf64.tar.gz +0 -0
  85. package/build/qemu/2.0.1/koffi_linux_x64.tar.gz +0 -0
  86. package/build/qemu/2.0.1/koffi_openbsd_ia32.tar.gz +0 -0
  87. package/build/qemu/2.0.1/koffi_openbsd_x64.tar.gz +0 -0
  88. package/build/qemu/2.0.1/koffi_win32_arm64.tar.gz +0 -0
  89. package/build/qemu/2.0.1/koffi_win32_ia32.tar.gz +0 -0
  90. package/build/qemu/2.0.1/koffi_win32_x64.tar.gz +0 -0
package/src/ffi.cc CHANGED
@@ -42,6 +42,7 @@ namespace RG {
42
42
 
43
43
  // Value does not matter, the tag system uses memory addresses
44
44
  const int TypeInfoMarker = 0xDEADBEEF;
45
+ const int CastMarker = 0xDEADBEEF;
45
46
 
46
47
  static bool ChangeMemorySize(const char *name, Napi::Value value, Size *out_size)
47
48
  {
@@ -209,18 +210,40 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
209
210
 
210
211
  std::string key = ((Napi::Value)keys[i]).As<Napi::String>();
211
212
  Napi::Value value = obj[key];
213
+ int16_t align = 0;
212
214
 
213
215
  member.name = DuplicateString(key.c_str(), &instance->str_alloc).ptr;
216
+
217
+ if (value.IsArray()) {
218
+ Napi::Array array = value.As<Napi::Array>();
219
+
220
+ if (array.Length() != 2 || !((Napi::Value)array[0u]).IsNumber()) {
221
+ ThrowError<Napi::Error>(env, "Member specifier array must contain alignement value and type");
222
+ return env.Null();
223
+ }
224
+
225
+ int64_t align64 = ((Napi::Value)array[0u]).As<Napi::Number>().Int64Value();
226
+
227
+ if (align64 < 1 || align64 > 64) {
228
+ ThrowError<Napi::Error>(env, "Alignment value must be between 1 and 64");
229
+ return env.Null();
230
+ }
231
+
232
+ value = array[1u];
233
+ align = (int16_t)align64;
234
+ }
235
+
214
236
  member.type = ResolveType(value);
215
237
  if (!member.type)
216
238
  return env.Null();
217
- if (member.type->primitive == PrimitiveKind::Void ||
218
- member.type->primitive == PrimitiveKind::Prototype) {
239
+ if (!CanStoreType(member.type)) {
219
240
  ThrowError<Napi::TypeError>(env, "Type %1 cannot be used as a member (maybe try %1 *)", member.type->name);
220
241
  return env.Null();
221
242
  }
222
243
 
223
- int16_t align = pad ? member.type->align : 1;
244
+ if (!align) {
245
+ align = pad ? member.type->align : 1;
246
+ }
224
247
  member.offset = (int16_t)AlignLen(type->size, align);
225
248
 
226
249
  type->size = (int16_t)(member.offset + member.type->size);
@@ -264,7 +287,7 @@ static Napi::Value CreatePackedStructType(const Napi::CallbackInfo &info)
264
287
  return CreateStructType(info, false);
265
288
  }
266
289
 
267
- static Napi::Value CreateHandleType(const Napi::CallbackInfo &info)
290
+ static Napi::Value CreateOpaqueType(const Napi::CallbackInfo &info)
268
291
  {
269
292
  Napi::Env env = info.Env();
270
293
  InstanceData *instance = env.GetInstanceData<InstanceData>();
@@ -387,7 +410,7 @@ static Napi::Value EncodePointerDirection(const Napi::CallbackInfo &info, int di
387
410
  return env.Null();
388
411
 
389
412
  if (type->primitive != PrimitiveKind::Pointer) {
390
- ThrowError<Napi::TypeError>(env, "Unexpected %1 type, expected pointer type", PrimitiveKindNames[(int)type->primitive]);
413
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 type, expected pointer type", type->name);
391
414
  return env.Null();
392
415
  }
393
416
 
@@ -440,7 +463,7 @@ static Napi::Value CreateDisposableType(const Napi::CallbackInfo &info)
440
463
  if (src->primitive != PrimitiveKind::String &&
441
464
  src->primitive != PrimitiveKind::String16 &&
442
465
  src->primitive != PrimitiveKind::Pointer) {
443
- ThrowError<Napi::TypeError>(env, "Unexpected %1 type, expected pointer or string type", PrimitiveKindNames[(int)src->primitive]);
466
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 type, expected pointer or string type", src->name);
444
467
  return env.Null();
445
468
  }
446
469
  if (src->dispose) {
@@ -618,8 +641,8 @@ static bool ParseClassicFunction(Napi::Env env, Napi::String name, Napi::Value r
618
641
  func->ret.type = ResolveType(ret);
619
642
  if (!func->ret.type)
620
643
  return false;
621
- if (func->ret.type->primitive == PrimitiveKind::Array) {
622
- ThrowError<Napi::Error>(env, "You are not allowed to directly return fixed-size arrays");
644
+ if (!CanReturnType(func->ret.type)) {
645
+ ThrowError<Napi::TypeError>(env, "You are not allowed to directly return %1 values (maybe try %1 *)", func->ret.type->name);
623
646
  return false;
624
647
  }
625
648
 
@@ -645,9 +668,7 @@ static bool ParseClassicFunction(Napi::Env env, Napi::String name, Napi::Value r
645
668
  param.type = ResolveType(parameters[j], &param.directions);
646
669
  if (!param.type)
647
670
  return false;
648
- if (param.type->primitive == PrimitiveKind::Void ||
649
- param.type->primitive == PrimitiveKind::Array ||
650
- param.type->primitive == PrimitiveKind::Prototype) {
671
+ if (!CanPassType(param.type)) {
651
672
  ThrowError<Napi::TypeError>(env, "Type %1 cannot be used as a parameter (maybe try %1 *)", param.type->name);
652
673
  return false;
653
674
  }
@@ -840,11 +861,17 @@ static Napi::Value GetTypeDefinition(const Napi::CallbackInfo &info)
840
861
  case PrimitiveKind::Int8:
841
862
  case PrimitiveKind::UInt8:
842
863
  case PrimitiveKind::Int16:
864
+ case PrimitiveKind::Int16S:
843
865
  case PrimitiveKind::UInt16:
866
+ case PrimitiveKind::UInt16S:
844
867
  case PrimitiveKind::Int32:
868
+ case PrimitiveKind::Int32S:
845
869
  case PrimitiveKind::UInt32:
870
+ case PrimitiveKind::UInt32S:
846
871
  case PrimitiveKind::Int64:
872
+ case PrimitiveKind::Int64S:
847
873
  case PrimitiveKind::UInt64:
874
+ case PrimitiveKind::UInt64S:
848
875
  case PrimitiveKind::String:
849
876
  case PrimitiveKind::String16:
850
877
  case PrimitiveKind::Float32:
@@ -988,10 +1015,8 @@ static Napi::Value TranslateVariadicCall(const Napi::CallbackInfo &info)
988
1015
  param.type = ResolveType(info[i], &param.directions);
989
1016
  if (RG_UNLIKELY(!param.type))
990
1017
  return env.Null();
991
- if (RG_UNLIKELY(param.type->primitive == PrimitiveKind::Void ||
992
- param.type->primitive == PrimitiveKind::Array ||
993
- param.type->primitive == PrimitiveKind::Prototype)) {
994
- ThrowError<Napi::TypeError>(env, "Type %1 cannot be used as a parameter (maybe try %1 *)", PrimitiveKindNames[(int)param.type->primitive]);
1018
+ if (RG_UNLIKELY(!CanPassType(param.type))) {
1019
+ ThrowError<Napi::TypeError>(env, "Type %1 cannot be used as a parameter (maybe try %1 *)", param.type->name);
995
1020
  return env.Null();
996
1021
  }
997
1022
 
@@ -1384,17 +1409,6 @@ void LibraryHolder::Unref() const
1384
1409
  }
1385
1410
  }
1386
1411
 
1387
- static inline PrimitiveKind GetIntegerPrimitive(Size len, bool sign)
1388
- {
1389
- switch (len) {
1390
- case 1: return sign ? PrimitiveKind::Int8 : PrimitiveKind::UInt8;
1391
- case 2: return sign ? PrimitiveKind::Int16 : PrimitiveKind::UInt16;
1392
- case 4: return sign ? PrimitiveKind::Int32 : PrimitiveKind::UInt32;
1393
- case 8: return sign ? PrimitiveKind::Int64 : PrimitiveKind::UInt64;
1394
- }
1395
-
1396
- RG_UNREACHABLE();
1397
- }
1398
1412
 
1399
1413
  static void RegisterPrimitiveType(Napi::Env env, Napi::Object map, std::initializer_list<const char *> names,
1400
1414
  PrimitiveKind primitive, int16_t size, int16_t align, const char *ref = nullptr)
@@ -1432,8 +1446,34 @@ static void RegisterPrimitiveType(Napi::Env env, Napi::Object map, std::initiali
1432
1446
  }
1433
1447
  }
1434
1448
 
1449
+ static inline PrimitiveKind GetSignPrimitive(Size len, bool sign)
1450
+ {
1451
+ switch (len) {
1452
+ case 1: return sign ? PrimitiveKind::Int8 : PrimitiveKind::UInt8;
1453
+ case 2: return sign ? PrimitiveKind::Int16 : PrimitiveKind::UInt16;
1454
+ case 4: return sign ? PrimitiveKind::Int32 : PrimitiveKind::UInt32;
1455
+ case 8: return sign ? PrimitiveKind::Int64 : PrimitiveKind::UInt64;
1456
+ }
1457
+
1458
+ RG_UNREACHABLE();
1459
+ }
1460
+
1461
+ static inline PrimitiveKind GetLittleEndianPrimitive(PrimitiveKind kind)
1462
+ {
1463
+ // Sensitive to endianness
1464
+ return kind;
1465
+ }
1466
+
1467
+ static inline PrimitiveKind GetBigEndianPrimitive(PrimitiveKind kind)
1468
+ {
1469
+ // Sensitive to endianness
1470
+ return (PrimitiveKind)((int)kind + 1);
1471
+ }
1472
+
1435
1473
  static Napi::Object InitBaseTypes(Napi::Env env)
1436
1474
  {
1475
+ InstanceData *instance = env.GetInstanceData<InstanceData>();
1476
+
1437
1477
  Napi::Object types = Napi::Object::New(env);
1438
1478
 
1439
1479
  RegisterPrimitiveType(env, types, {"void"}, PrimitiveKind::Void, 0, 0);
@@ -1444,19 +1484,32 @@ static Napi::Object InitBaseTypes(Napi::Env env)
1444
1484
  RegisterPrimitiveType(env, types, {"unsigned char", "uchar"}, PrimitiveKind::UInt8, 1, 1);
1445
1485
  RegisterPrimitiveType(env, types, {"char16_t", "char16"}, PrimitiveKind::Int16, 2, 2);
1446
1486
  RegisterPrimitiveType(env, types, {"int16_t", "int16"}, PrimitiveKind::Int16, 2, 2);
1487
+ RegisterPrimitiveType(env, types, {"int16_le_t", "int16_le"}, GetLittleEndianPrimitive(PrimitiveKind::Int16), 2, 2);
1488
+ RegisterPrimitiveType(env, types, {"int16_be_t", "int16_be"}, GetBigEndianPrimitive(PrimitiveKind::Int16), 2, 2);
1447
1489
  RegisterPrimitiveType(env, types, {"uint16_t", "uint16"}, PrimitiveKind::UInt16, 2, 2);
1490
+ RegisterPrimitiveType(env, types, {"uint16_le_t", "uint16_le"}, GetLittleEndianPrimitive(PrimitiveKind::UInt16), 2, 2);
1491
+ RegisterPrimitiveType(env, types, {"uint16_be_t", "uint16_be"}, GetBigEndianPrimitive(PrimitiveKind::UInt16), 2, 2);
1448
1492
  RegisterPrimitiveType(env, types, {"short"}, PrimitiveKind::Int16, 2, 2);
1449
1493
  RegisterPrimitiveType(env, types, {"unsigned short", "ushort"}, PrimitiveKind::UInt16, 2, 2);
1450
1494
  RegisterPrimitiveType(env, types, {"int32_t", "int32"}, PrimitiveKind::Int32, 4, 4);
1495
+ RegisterPrimitiveType(env, types, {"int32_le_t", "int32_le"}, GetLittleEndianPrimitive(PrimitiveKind::Int32), 4, 4);
1496
+ RegisterPrimitiveType(env, types, {"int32_be_t", "int32_be"}, GetBigEndianPrimitive(PrimitiveKind::Int32), 4, 4);
1451
1497
  RegisterPrimitiveType(env, types, {"uint32_t", "uint32"}, PrimitiveKind::UInt32, 4, 4);
1498
+ RegisterPrimitiveType(env, types, {"uint32_le_t", "uint32_le"}, GetLittleEndianPrimitive(PrimitiveKind::UInt32), 4, 4);
1499
+ RegisterPrimitiveType(env, types, {"uint32_be_t", "uint32_be"}, GetBigEndianPrimitive(PrimitiveKind::UInt32), 4, 4);
1452
1500
  RegisterPrimitiveType(env, types, {"int"}, PrimitiveKind::Int32, 4, 4);
1453
1501
  RegisterPrimitiveType(env, types, {"unsigned int", "uint"}, PrimitiveKind::UInt32, 4, 4);
1454
1502
  RegisterPrimitiveType(env, types, {"int64_t", "int64"}, PrimitiveKind::Int64, 8, alignof(int64_t));
1503
+ RegisterPrimitiveType(env, types, {"int64_le_t", "int64_le"}, GetLittleEndianPrimitive(PrimitiveKind::Int64), 8, alignof(int64_t));
1504
+ RegisterPrimitiveType(env, types, {"int64_be_t", "int64_be"}, GetBigEndianPrimitive(PrimitiveKind::Int64), 8, alignof(int64_t));
1455
1505
  RegisterPrimitiveType(env, types, {"uint64_t", "uint64"}, PrimitiveKind::UInt64, 8, alignof(int64_t));
1456
- RegisterPrimitiveType(env, types, {"intptr_t", "intptr"}, GetIntegerPrimitive(RG_SIZE(intptr_t), true), RG_SIZE(intptr_t), alignof(intptr_t));
1457
- RegisterPrimitiveType(env, types, {"uintptr_t", "uintptr"}, GetIntegerPrimitive(RG_SIZE(intptr_t), false), RG_SIZE(intptr_t), alignof(intptr_t));
1458
- RegisterPrimitiveType(env, types, {"long"}, GetIntegerPrimitive(RG_SIZE(long), true), RG_SIZE(long), alignof(long));
1459
- RegisterPrimitiveType(env, types, {"unsigned long", "ulong"}, GetIntegerPrimitive(RG_SIZE(long), false), RG_SIZE(long), alignof(long));
1506
+ RegisterPrimitiveType(env, types, {"uint64_le_t", "uint64_le"}, GetLittleEndianPrimitive(PrimitiveKind::UInt64), 8, alignof(int64_t));
1507
+ RegisterPrimitiveType(env, types, {"uint64_be_t", "uint64_be"}, GetBigEndianPrimitive(PrimitiveKind::UInt64), 8, alignof(int64_t));
1508
+ RegisterPrimitiveType(env, types, {"intptr_t", "intptr"}, GetSignPrimitive(RG_SIZE(intptr_t), true), RG_SIZE(intptr_t), alignof(intptr_t));
1509
+ RegisterPrimitiveType(env, types, {"uintptr_t", "uintptr"}, GetSignPrimitive(RG_SIZE(intptr_t), false), RG_SIZE(intptr_t), alignof(intptr_t));
1510
+ RegisterPrimitiveType(env, types, {"size_t"}, GetSignPrimitive(RG_SIZE(size_t), false), RG_SIZE(size_t), alignof(size_t));
1511
+ RegisterPrimitiveType(env, types, {"long"}, GetSignPrimitive(RG_SIZE(long), true), RG_SIZE(long), alignof(long));
1512
+ RegisterPrimitiveType(env, types, {"unsigned long", "ulong"}, GetSignPrimitive(RG_SIZE(long), false), RG_SIZE(long), alignof(long));
1460
1513
  RegisterPrimitiveType(env, types, {"long long", "longlong"}, PrimitiveKind::Int64, RG_SIZE(int64_t), alignof(int64_t));
1461
1514
  RegisterPrimitiveType(env, types, {"unsigned long long", "ulonglong"}, PrimitiveKind::UInt64, RG_SIZE(uint64_t), alignof(uint64_t));
1462
1515
  RegisterPrimitiveType(env, types, {"float", "float32"}, PrimitiveKind::Float32, 4, alignof(float));
@@ -1464,6 +1517,9 @@ static Napi::Object InitBaseTypes(Napi::Env env)
1464
1517
  RegisterPrimitiveType(env, types, {"char *", "str", "string"}, PrimitiveKind::String, RG_SIZE(void *), alignof(void *), "char");
1465
1518
  RegisterPrimitiveType(env, types, {"char16_t *", "char16 *", "str16", "string16"}, PrimitiveKind::String16, RG_SIZE(void *), alignof(void *), "char16_t");
1466
1519
 
1520
+ instance->void_type = instance->types_map.FindValue("void", nullptr);
1521
+ RG_ASSERT(instance->void_type);
1522
+
1467
1523
  types.Freeze();
1468
1524
 
1469
1525
  return types;
@@ -1515,6 +1571,37 @@ InstanceData::~InstanceData()
1515
1571
  }
1516
1572
  }
1517
1573
 
1574
+ static Napi::Value CastValue(const Napi::CallbackInfo &info)
1575
+ {
1576
+ Napi::Env env = info.Env();
1577
+ InstanceData *instance = env.GetInstanceData<InstanceData>();
1578
+
1579
+ if (RG_UNLIKELY(info.Length() < 2)) {
1580
+ ThrowError<Napi::TypeError>(env, "Expected 2 arguments, got %1", info.Length());
1581
+ return env.Null();
1582
+ }
1583
+
1584
+ Napi::Value value = info[0];
1585
+
1586
+ const TypeInfo *type = ResolveType(info[1]);
1587
+ if (RG_UNLIKELY(!type))
1588
+ return env.Null();
1589
+ if (type->primitive != PrimitiveKind::Pointer) {
1590
+ ThrowError<Napi::TypeError>(env, "Only pointer types can be used for casting");
1591
+ return env.Null();
1592
+ }
1593
+
1594
+ ValueCast *cast = new ValueCast;
1595
+
1596
+ cast->ref.Reset(value, 1);
1597
+ cast->type = type;
1598
+
1599
+ Napi::External<ValueCast> external = Napi::External<ValueCast>::New(env, cast, [](Napi::Env, ValueCast *cast) { delete cast; });
1600
+ SetValueTag(instance, external, &CastMarker);
1601
+
1602
+ return external;
1603
+ }
1604
+
1518
1605
  template <typename Func>
1519
1606
  static void SetExports(Napi::Env env, Func func)
1520
1607
  {
@@ -1522,7 +1609,7 @@ static void SetExports(Napi::Env env, Func func)
1522
1609
 
1523
1610
  func("struct", Napi::Function::New(env, CreatePaddedStructType));
1524
1611
  func("pack", Napi::Function::New(env, CreatePackedStructType));
1525
- func("handle", Napi::Function::New(env, CreateHandleType));
1612
+ func("opaque", Napi::Function::New(env, CreateOpaqueType));
1526
1613
  func("pointer", Napi::Function::New(env, CreatePointerType));
1527
1614
  func("array", Napi::Function::New(env, CreateArrayType));
1528
1615
  func("callback", Napi::Function::New(env, CreateCallbackType));
@@ -1545,6 +1632,8 @@ static void SetExports(Napi::Env env, Func func)
1545
1632
  func("register", Napi::Function::New(env, RegisterCallback));
1546
1633
  func("unregister", Napi::Function::New(env, UnregisterCallback));
1547
1634
 
1635
+ func("as", Napi::Function::New(env, CastValue));
1636
+
1548
1637
  #if defined(_WIN32)
1549
1638
  func("extension", Napi::String::New(env, ".dll"));
1550
1639
  #elif defined(__APPLE__)
package/src/ffi.hh CHANGED
@@ -32,6 +32,7 @@ static const Size MaxOutParameters = 4;
32
32
  static const Size MaxTrampolines = 16;
33
33
 
34
34
  extern const int TypeInfoMarker;
35
+ extern const int CastMarker;
35
36
 
36
37
  enum class PrimitiveKind {
37
38
  Void,
@@ -39,11 +40,17 @@ enum class PrimitiveKind {
39
40
  Int8,
40
41
  UInt8,
41
42
  Int16,
43
+ Int16S,
42
44
  UInt16,
45
+ UInt16S,
43
46
  Int32,
47
+ Int32S,
44
48
  UInt32,
49
+ UInt32S,
45
50
  Int64,
51
+ Int64S,
46
52
  UInt64,
53
+ UInt64S,
47
54
  String,
48
55
  String16,
49
56
  Pointer,
@@ -60,11 +67,17 @@ static const char *const PrimitiveKindNames[] = {
60
67
  "Int8",
61
68
  "UInt8",
62
69
  "Int16",
70
+ "Int16S",
63
71
  "UInt16",
72
+ "UInt16S",
64
73
  "Int32",
74
+ "Int32S",
65
75
  "UInt32",
76
+ "UInt32S",
66
77
  "Int64",
78
+ "Int64S",
67
79
  "UInt64",
80
+ "UInt64S",
68
81
  "String",
69
82
  "String16",
70
83
  "Pointer",
@@ -171,6 +184,11 @@ struct ParameterInfo {
171
184
  #endif
172
185
  };
173
186
 
187
+ struct ValueCast {
188
+ Napi::Reference<Napi::Value> ref;
189
+ const TypeInfo *type;
190
+ };
191
+
174
192
  // Also used for callbacks, even though many members are not used in this case
175
193
  struct FunctionInfo {
176
194
  mutable std::atomic_int refcount {1};
@@ -230,6 +248,7 @@ struct InstanceData {
230
248
 
231
249
  bool debug;
232
250
  uint64_t tag_lower;
251
+ const TypeInfo *void_type;
233
252
 
234
253
  LocalArray<InstanceMemory *, 9> memories;
235
254
  int temporaries = 0;
package/src/index.js CHANGED
@@ -15,7 +15,9 @@
15
15
 
16
16
  'use strict';
17
17
 
18
- const path = require('path');
18
+ const util = require('util');
19
19
 
20
- let filename = path.dirname(__filename) + '/../build/koffi.node';
20
+ let filename = __dirname + '/../build/koffi.node';
21
21
  module.exports = require(filename);
22
+
23
+ module.exports.handle = util.deprecate(module.exports.opaque, 'The koffi.handle() function was deprecated in Koffi 2.1, use koffi.opaque() instead', 'KOFFI001');
package/src/parser.cc CHANGED
@@ -28,8 +28,8 @@ bool PrototypeParser::Parse(const char *str, FunctionInfo *out_func)
28
28
  Tokenize(str);
29
29
 
30
30
  out_func->ret.type = ParseType();
31
- if (out_func->ret.type->primitive == PrimitiveKind::Array) {
32
- MarkError("You are not allowed to directly return C arrays");
31
+ if (!CanReturnType(out_func->ret.type)) {
32
+ MarkError("You are not allowed to directly return %1 values (maybe try %1 *)", out_func->ret.type->name);
33
33
  return false;
34
34
  }
35
35
  if (Match("__cdecl")) {
@@ -65,9 +65,7 @@ bool PrototypeParser::Parse(const char *str, FunctionInfo *out_func)
65
65
  }
66
66
 
67
67
  param.type = ParseType();
68
- if (param.type->primitive == PrimitiveKind::Void ||
69
- param.type->primitive == PrimitiveKind::Array ||
70
- param.type->primitive == PrimitiveKind::Prototype) {
68
+ if (!CanPassType(param.type)) {
71
69
  MarkError("Type %1 cannot be used as a parameter (maybe try %1 *)", param.type->name);
72
70
  return false;
73
71
  }
package/src/util.cc CHANGED
@@ -170,10 +170,53 @@ const TypeInfo *MakePointerType(InstanceData *instance, const TypeInfo *ref, int
170
170
  return ref;
171
171
  }
172
172
 
173
+ bool CanPassType(const TypeInfo *type)
174
+ {
175
+ if (type->primitive == PrimitiveKind::Void)
176
+ return false;
177
+ if (type->primitive == PrimitiveKind::Array)
178
+ return false;
179
+ if (type->primitive == PrimitiveKind::Prototype)
180
+ return false;
181
+
182
+ return true;
183
+ }
184
+
185
+ bool CanReturnType(const TypeInfo *type)
186
+ {
187
+ if (type->primitive == PrimitiveKind::Void && !TestStr(type->name, "void"))
188
+ return false;
189
+ if (type->primitive == PrimitiveKind::Array)
190
+ return false;
191
+ if (type->primitive == PrimitiveKind::Prototype)
192
+ return false;
193
+
194
+ return true;
195
+ }
196
+
197
+ bool CanStoreType(const TypeInfo *type)
198
+ {
199
+ if (type->primitive == PrimitiveKind::Void)
200
+ return false;
201
+ if (type->primitive == PrimitiveKind::Prototype)
202
+ return false;
203
+
204
+ return true;
205
+ }
206
+
173
207
  const char *GetValueType(const InstanceData *instance, Napi::Value value)
174
208
  {
209
+ if (CheckValueTag(instance, value, &CastMarker)) {
210
+ Napi::External<ValueCast> external = value.As<Napi::External<ValueCast>>();
211
+ ValueCast *cast = external.Data();
212
+
213
+ return cast->type->name;
214
+ }
215
+
216
+ if (CheckValueTag(instance, value, &TypeInfoMarker))
217
+ return "Type";
175
218
  for (const TypeInfo &type: instance->types) {
176
- if (CheckValueTag(instance, value, type.ref.marker))
219
+ if (type.ref.marker && CheckValueTag(instance, value, type.ref.marker))
177
220
  return type.name;
178
221
  }
179
222
 
package/src/util.hh CHANGED
@@ -71,6 +71,10 @@ const TypeInfo *ResolveType(Napi::Value value, int *out_directions = nullptr);
71
71
  const TypeInfo *ResolveType(InstanceData *instance, Span<const char> str, int *out_directions = nullptr);
72
72
  const TypeInfo *MakePointerType(InstanceData *instance, const TypeInfo *type, int count = 1);
73
73
 
74
+ bool CanPassType(const TypeInfo *type);
75
+ bool CanReturnType(const TypeInfo *type);
76
+ bool CanStoreType(const TypeInfo *type);
77
+
74
78
  // Can be slow, only use for error messages
75
79
  const char *GetValueType(const InstanceData *instance, Napi::Value value);
76
80
 
package/test/async.js CHANGED
@@ -15,7 +15,6 @@
15
15
 
16
16
  const koffi = require('./build/koffi.node');
17
17
  const assert = require('assert');
18
- const path = require('path');
19
18
 
20
19
  const PackedBFG = koffi.pack('PackedBFG', {
21
20
  a: 'int8_t',
@@ -42,7 +41,7 @@ async function main() {
42
41
  }
43
42
 
44
43
  async function test() {
45
- const lib_filename = path.dirname(__filename) + '/build/misc' + koffi.extension;
44
+ const lib_filename = __dirname + '/build/misc' + koffi.extension;
46
45
  const lib = koffi.load(lib_filename);
47
46
 
48
47
  const ConcatenateToInt1 = lib.func('ConcatenateToInt1', 'int64_t', Array(12).fill('int8_t'));
package/test/callbacks.js CHANGED
@@ -15,11 +15,10 @@
15
15
 
16
16
  const koffi = require('./build/koffi.node');
17
17
  const assert = require('assert');
18
- const path = require('path');
19
18
 
20
19
  const BFG = koffi.struct('BFG', {
21
20
  a: 'int8_t',
22
- b: 'int64_t',
21
+ b: [16, 'int64_t'],
23
22
  c: 'char',
24
23
  d: 'str',
25
24
  e: 'short',
@@ -55,7 +54,7 @@ async function main() {
55
54
  }
56
55
 
57
56
  async function test() {
58
- const lib_filename = path.dirname(__filename) + '/build/misc' + koffi.extension;
57
+ const lib_filename = __dirname + '/build/misc' + koffi.extension;
59
58
  const lib = koffi.load(lib_filename);
60
59
 
61
60
  const CallJS = lib.func('int CallJS(const char *str, SimpleCallback *cb)');
package/test/misc.c CHANGED
@@ -90,7 +90,7 @@ typedef struct IJK8 { int64_t i; int64_t j; int64_t k; } IJK8;
90
90
 
91
91
  typedef struct BFG {
92
92
  int8_t a;
93
- int64_t b;
93
+ char _pad1[15]; int64_t b;
94
94
  signed char c;
95
95
  const char *d;
96
96
  short e;
@@ -143,6 +143,21 @@ typedef struct StructCallbacks {
143
143
  IntCallback *third;
144
144
  } StructCallbacks;
145
145
 
146
+ typedef struct EndianInts {
147
+ int16_t i16le;
148
+ int16_t i16be;
149
+ uint16_t u16le;
150
+ uint16_t u16be;
151
+ int32_t i32le;
152
+ int32_t i32be;
153
+ uint32_t u32le;
154
+ uint32_t u32be;
155
+ int64_t i64le;
156
+ int64_t i64be;
157
+ uint64_t u64le;
158
+ uint64_t u64be;
159
+ } EndianInts;
160
+
146
161
  EXPORT int8_t GetMinusOne1(void)
147
162
  {
148
163
  return -1;
@@ -399,6 +414,15 @@ EXPORT PackedBFG FASTCALL MakePackedBFG(int x, double y, PackedBFG *p, const cha
399
414
  return bfg;
400
415
  }
401
416
 
417
+ EXPORT void MakePolymorphBFG(int type, int x, double y, const char *str, void *p)
418
+ {
419
+ if (type == 0) {
420
+ MakeBFG(p, x, y, str);
421
+ } else if (type == 1) {
422
+ MakePackedBFG(x, y, p, str);
423
+ }
424
+ }
425
+
402
426
  #ifdef _WIN32
403
427
  // Exported by DEF file
404
428
  const char *ReturnBigString(const char *str)
@@ -633,3 +657,42 @@ EXPORT int CallCallback(int x)
633
657
  {
634
658
  return callback(x);
635
659
  }
660
+
661
+ EXPORT void ReverseBytes(void *p, int len)
662
+ {
663
+ uint8_t *bytes = (uint8_t *)p;
664
+
665
+ for (int i = 0; i < len / 2; i++) {
666
+ uint8_t tmp = bytes[i];
667
+ bytes[i] = bytes[len - i - 1];
668
+ bytes[len - i - 1] = tmp;
669
+ }
670
+ }
671
+
672
+ EXPORT void CopyEndianInts1(EndianInts ints, uint8_t buf[56])
673
+ {
674
+ memcpy(buf, &ints, sizeof(ints));
675
+ }
676
+
677
+ EXPORT void CopyEndianInts2(int16_t i16le, int16_t i16be, uint16_t u16le, uint16_t u16be,
678
+ int32_t i32le, int32_t i32be, uint32_t u32le, uint32_t u32be,
679
+ int64_t i64le, int64_t i64be, uint64_t u64le, uint64_t u64be,
680
+ EndianInts *out)
681
+ {
682
+ out->i16le = i16le;
683
+ out->i16be = i16be;
684
+ out->u16le = u16le;
685
+ out->u16be = u16be;
686
+ out->i32le = i32le;
687
+ out->i32be = i32be;
688
+ out->u32le = u32le;
689
+ out->u32be = u32be;
690
+ out->i64le = i64le;
691
+ out->i64be = i64be;
692
+ out->u64le = u64le;
693
+ out->u64be = u64be;
694
+ }
695
+
696
+ EXPORT uint16_t ReturnEndianInt2(uint16_t v) { return v; }
697
+ EXPORT uint32_t ReturnEndianInt4(uint32_t v) { return v; }
698
+ EXPORT uint64_t ReturnEndianInt8(uint64_t v) { return v; }
package/test/raylib.js CHANGED
@@ -83,7 +83,7 @@ async function main() {
83
83
  }
84
84
 
85
85
  async function test() {
86
- let lib_filename = path.dirname(__filename) + '/build/raylib' + koffi.extension;
86
+ let lib_filename = __dirname + '/build/raylib' + koffi.extension;
87
87
  let lib = koffi.load(lib_filename);
88
88
 
89
89
  const InitWindow = lib.func('InitWindow', 'void', ['int', 'int', 'str']);
package/test/sqlite.js CHANGED
@@ -20,8 +20,8 @@ const fs = require('fs');
20
20
  const os = require('os');
21
21
  const path = require('path');
22
22
 
23
- const sqlite3 = koffi.handle('sqlite3');
24
- const sqlite3_stmt = koffi.handle('sqlite3_stmt');
23
+ const sqlite3 = koffi.opaque('sqlite3');
24
+ const sqlite3_stmt = koffi.opaque('sqlite3_stmt');
25
25
 
26
26
  main();
27
27
 
@@ -36,7 +36,7 @@ async function main() {
36
36
  }
37
37
 
38
38
  async function test() {
39
- let lib_filename = path.dirname(__filename) + '/build/sqlite3' + koffi.extension;
39
+ let lib_filename = __dirname + '/build/sqlite3' + koffi.extension;
40
40
  let lib = koffi.load(lib_filename);
41
41
 
42
42
  const sqlite3_open_v2 = lib.func('sqlite3_open_v2', 'int', ['str', koffi.out(koffi.pointer(sqlite3, 2)), 'int', 'str']);