koffi 1.2.4 → 1.3.0-rc.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 (126) hide show
  1. package/README.md +7 -489
  2. package/benchmark/CMakeLists.txt +13 -9
  3. package/benchmark/raylib_node_raylib.js +67 -0
  4. package/build/qemu/1.3.0-rc.1/koffi_darwin_arm64.tar.gz +0 -0
  5. package/build/qemu/1.3.0-rc.1/koffi_darwin_x64.tar.gz +0 -0
  6. package/build/qemu/1.3.0-rc.1/koffi_freebsd_arm64.tar.gz +0 -0
  7. package/build/qemu/1.3.0-rc.1/koffi_freebsd_ia32.tar.gz +0 -0
  8. package/build/qemu/1.3.0-rc.1/koffi_freebsd_x64.tar.gz +0 -0
  9. package/build/qemu/1.3.0-rc.1/koffi_linux_arm32hf.tar.gz +0 -0
  10. package/build/qemu/1.3.0-rc.1/koffi_linux_arm64.tar.gz +0 -0
  11. package/build/qemu/1.3.0-rc.1/koffi_linux_ia32.tar.gz +0 -0
  12. package/build/qemu/1.3.0-rc.1/koffi_linux_riscv64hf64.tar.gz +0 -0
  13. package/build/qemu/1.3.0-rc.1/koffi_linux_x64.tar.gz +0 -0
  14. package/build/qemu/1.3.0-rc.1/koffi_openbsd_ia32.tar.gz +0 -0
  15. package/build/qemu/1.3.0-rc.1/koffi_openbsd_x64.tar.gz +0 -0
  16. package/build/qemu/1.3.0-rc.1/koffi_win32_arm64.tar.gz +0 -0
  17. package/build/qemu/1.3.0-rc.1/koffi_win32_ia32.tar.gz +0 -0
  18. package/build/qemu/1.3.0-rc.1/koffi_win32_x64.tar.gz +0 -0
  19. package/doc/Makefile +20 -0
  20. package/doc/_static/bench_linux.png +0 -0
  21. package/doc/_static/bench_windows.png +0 -0
  22. package/doc/_static/custom.css +22 -0
  23. package/doc/benchmarks.md +113 -0
  24. package/doc/benchmarks.xlsx +0 -0
  25. package/doc/conf.py +54 -0
  26. package/doc/contribute.md +115 -0
  27. package/doc/dist/doctrees/benchmarks.doctree +0 -0
  28. package/doc/dist/doctrees/contribute.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/memory.doctree +0 -0
  33. package/doc/dist/doctrees/platforms.doctree +0 -0
  34. package/doc/dist/doctrees/start.doctree +0 -0
  35. package/doc/dist/doctrees/types.doctree +0 -0
  36. package/doc/dist/html/.buildinfo +4 -0
  37. package/doc/dist/html/_sources/benchmarks.md.txt +113 -0
  38. package/doc/dist/html/_sources/contribute.md.txt +115 -0
  39. package/doc/dist/html/_sources/functions.md.txt +224 -0
  40. package/doc/dist/html/_sources/index.rst.txt +33 -0
  41. package/doc/dist/html/_sources/memory.md.txt +29 -0
  42. package/doc/dist/html/_sources/platforms.md.txt +17 -0
  43. package/doc/dist/html/_sources/start.md.txt +89 -0
  44. package/doc/dist/html/_sources/types.md.txt +514 -0
  45. package/doc/dist/html/_static/_sphinx_javascript_frameworks_compat.js +134 -0
  46. package/doc/dist/html/_static/basic.css +932 -0
  47. package/doc/dist/html/_static/bench_linux.png +0 -0
  48. package/doc/dist/html/_static/bench_windows.png +0 -0
  49. package/doc/dist/html/_static/custom.css +22 -0
  50. package/doc/dist/html/_static/debug.css +69 -0
  51. package/doc/dist/html/_static/doctools.js +264 -0
  52. package/doc/dist/html/_static/documentation_options.js +14 -0
  53. package/doc/dist/html/_static/file.png +0 -0
  54. package/doc/dist/html/_static/jquery-3.6.0.js +10881 -0
  55. package/doc/dist/html/_static/jquery.js +2 -0
  56. package/doc/dist/html/_static/language_data.js +199 -0
  57. package/doc/dist/html/_static/minus.png +0 -0
  58. package/doc/dist/html/_static/plus.png +0 -0
  59. package/doc/dist/html/_static/pygments.css +252 -0
  60. package/doc/dist/html/_static/scripts/furo-extensions.js +0 -0
  61. package/doc/dist/html/_static/scripts/furo.js +3 -0
  62. package/doc/dist/html/_static/scripts/furo.js.LICENSE.txt +7 -0
  63. package/doc/dist/html/_static/scripts/furo.js.map +1 -0
  64. package/doc/dist/html/_static/searchtools.js +531 -0
  65. package/doc/dist/html/_static/skeleton.css +296 -0
  66. package/doc/dist/html/_static/styles/furo-extensions.css +2 -0
  67. package/doc/dist/html/_static/styles/furo-extensions.css.map +1 -0
  68. package/doc/dist/html/_static/styles/furo.css +2 -0
  69. package/doc/dist/html/_static/styles/furo.css.map +1 -0
  70. package/doc/dist/html/_static/underscore-1.13.1.js +2042 -0
  71. package/doc/dist/html/_static/underscore.js +6 -0
  72. package/doc/dist/html/benchmarks.html +547 -0
  73. package/doc/dist/html/contribute.html +382 -0
  74. package/doc/dist/html/functions.html +530 -0
  75. package/doc/dist/html/genindex.html +249 -0
  76. package/doc/dist/html/index.html +342 -0
  77. package/doc/dist/html/memory.html +337 -0
  78. package/doc/dist/html/objects.inv +0 -0
  79. package/doc/dist/html/platforms.html +332 -0
  80. package/doc/dist/html/search.html +257 -0
  81. package/doc/dist/html/searchindex.js +1 -0
  82. package/doc/dist/html/start.html +367 -0
  83. package/doc/dist/html/types.html +1001 -0
  84. package/doc/functions.md +224 -0
  85. package/doc/index.rst +33 -0
  86. package/doc/make.bat +35 -0
  87. package/doc/memory.md +29 -0
  88. package/doc/platforms.md +17 -0
  89. package/doc/start.md +89 -0
  90. package/doc/types.md +514 -0
  91. package/package.json +5 -2
  92. package/qemu/qemu.js +41 -27
  93. package/qemu/registry/machines.json +59 -79
  94. package/qemu/registry/sha256sum.txt +4 -4
  95. package/src/abi_arm32.cc +20 -48
  96. package/src/abi_arm64.cc +18 -46
  97. package/src/abi_arm64_fwd.S +5 -0
  98. package/src/abi_riscv64.cc +19 -47
  99. package/src/abi_x64_sysv.cc +18 -46
  100. package/src/abi_x64_win.cc +19 -47
  101. package/src/abi_x86.cc +21 -49
  102. package/src/call.cc +505 -242
  103. package/src/call.hh +14 -7
  104. package/src/ffi.cc +47 -26
  105. package/src/ffi.hh +1 -1
  106. package/src/parser.cc +2 -20
  107. package/src/util.cc +50 -11
  108. package/src/util.hh +2 -0
  109. package/test/misc.c +31 -0
  110. package/test/sync.js +41 -4
  111. package/benchmark/atoi_cc.cc +0 -59
  112. package/build/qemu/1.2.4/koffi_darwin_arm64.tar.gz +0 -0
  113. package/build/qemu/1.2.4/koffi_darwin_x64.tar.gz +0 -0
  114. package/build/qemu/1.2.4/koffi_freebsd_arm64.tar.gz +0 -0
  115. package/build/qemu/1.2.4/koffi_freebsd_ia32.tar.gz +0 -0
  116. package/build/qemu/1.2.4/koffi_freebsd_x64.tar.gz +0 -0
  117. package/build/qemu/1.2.4/koffi_linux_arm.tar.gz +0 -0
  118. package/build/qemu/1.2.4/koffi_linux_arm64.tar.gz +0 -0
  119. package/build/qemu/1.2.4/koffi_linux_ia32.tar.gz +0 -0
  120. package/build/qemu/1.2.4/koffi_linux_riscv64.tar.gz +0 -0
  121. package/build/qemu/1.2.4/koffi_linux_x64.tar.gz +0 -0
  122. package/build/qemu/1.2.4/koffi_openbsd_ia32.tar.gz +0 -0
  123. package/build/qemu/1.2.4/koffi_openbsd_x64.tar.gz +0 -0
  124. package/build/qemu/1.2.4/koffi_win32_arm64.tar.gz +0 -0
  125. package/build/qemu/1.2.4/koffi_win32_ia32.tar.gz +0 -0
  126. package/build/qemu/1.2.4/koffi_win32_x64.tar.gz +0 -0
package/src/call.cc CHANGED
@@ -32,6 +32,10 @@ CallData::CallData(Napi::Env env, InstanceData *instance, const FunctionInfo *fu
32
32
 
33
33
  CallData::~CallData()
34
34
  {
35
+ for (const OutArgument &out: out_arguments) {
36
+ napi_delete_reference(env, out.ref);
37
+ }
38
+
35
39
  mem->stack = old_stack_mem;
36
40
  mem->heap = old_heap_mem;
37
41
 
@@ -42,7 +46,7 @@ CallData::~CallData()
42
46
  }
43
47
  }
44
48
 
45
- const char *CallData::PushString(const Napi::Value &value)
49
+ const char *CallData::PushString(Napi::Value value)
46
50
  {
47
51
  RG_ASSERT(value.IsString());
48
52
 
@@ -79,7 +83,7 @@ const char *CallData::PushString(const Napi::Value &value)
79
83
  return buf.ptr;
80
84
  }
81
85
 
82
- const char16_t *CallData::PushString16(const Napi::Value &value)
86
+ const char16_t *CallData::PushString16(Napi::Value value)
83
87
  {
84
88
  RG_ASSERT(value.IsString());
85
89
 
@@ -116,7 +120,7 @@ const char16_t *CallData::PushString16(const Napi::Value &value)
116
120
  return buf.ptr;
117
121
  }
118
122
 
119
- bool CallData::PushObject(const Napi::Object &obj, const TypeInfo *type, uint8_t *origin, int16_t realign)
123
+ bool CallData::PushObject(Napi::Object obj, const TypeInfo *type, uint8_t *origin, int16_t realign)
120
124
  {
121
125
  RG_ASSERT(IsObject(obj));
122
126
  RG_ASSERT(type->primitive == PrimitiveKind::Record);
@@ -265,14 +269,25 @@ bool CallData::PushObject(const Napi::Object &obj, const TypeInfo *type, uint8_t
265
269
  return false;
266
270
  } break;
267
271
  case PrimitiveKind::Array: {
268
- if (RG_UNLIKELY(!value.IsArray() && !value.IsTypedArray() && !value.IsString())) {
272
+ if (value.IsArray()) {
273
+ Napi::Array array = value.As<Napi::Array>();
274
+ Size len = (Size)member.type->size / member.type->ref->size;
275
+
276
+ if (!PushNormalArray(array, len, member.type->ref, dest, realign))
277
+ return false;
278
+ } else if (value.IsTypedArray()) {
279
+ Napi::TypedArray array = value.As<Napi::TypedArray>();
280
+ Size len = (Size)member.type->size / member.type->ref->size;
281
+
282
+ if (!PushTypedArray(array, len, member.type->ref, dest, realign))
283
+ return false;
284
+ } else if (value.IsString() && !realign) {
285
+ if (!PushStringArray(value, member.type, dest))
286
+ return false;
287
+ } else {
269
288
  ThrowError<Napi::TypeError>(env, "Unexpected value %1 for member '%2', expected array", GetValueType(instance, value), member.name);
270
289
  return false;
271
290
  }
272
-
273
- Napi::Array array = value.As<Napi::Array>();
274
- if (!PushArray(array, member.type, dest, realign))
275
- return false;
276
291
  } break;
277
292
  case PrimitiveKind::Float32: {
278
293
  if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
@@ -321,248 +336,404 @@ bool CallData::PushObject(const Napi::Object &obj, const TypeInfo *type, uint8_t
321
336
  return true;
322
337
  }
323
338
 
324
- bool CallData::PushArray(const Napi::Value &obj, const TypeInfo *type, uint8_t *origin, int16_t realign)
339
+ bool CallData::PushNormalArray(Napi::Array array, Size len, const TypeInfo *ref, uint8_t *origin, int16_t realign)
325
340
  {
326
- RG_ASSERT(obj.IsArray() || obj.IsTypedArray() || obj.IsString());
327
- RG_ASSERT(type->primitive == PrimitiveKind::Array);
341
+ RG_ASSERT(array.IsArray());
328
342
 
329
- uint32_t len = type->size / type->ref->size;
330
- Size offset = 0;
331
-
332
- if (obj.IsArray()) {
333
- Napi::Array array = obj.As<Napi::Array>();
343
+ if (RG_UNLIKELY(array.Length() != len)) {
344
+ ThrowError<Napi::Error>(env, "Expected array of length %1, got %2", len, array.Length());
345
+ return false;
346
+ }
334
347
 
335
- if (RG_UNLIKELY(array.Length() != len)) {
336
- ThrowError<Napi::Error>(env, "Expected array of length %1, got %2", len, array.Length());
337
- return false;
338
- }
348
+ Size offset = 0;
339
349
 
340
350
  #define PUSH_ARRAY(Check, Expected, GetCode) \
341
- do { \
342
- for (uint32_t i = 0; i < len; i++) { \
343
- Napi::Value value = array[i]; \
344
- \
345
- int16_t align = std::max(type->ref->align, realign); \
346
- \
347
- offset = AlignLen(offset, align); \
348
- uint8_t *dest = origin + offset; \
349
- \
350
- if (RG_UNLIKELY(!(Check))) { \
351
- ThrowError<Napi::TypeError>(env, "Unexpected value %1 in array, expected %2", GetValueType(instance, value), (Expected)); \
352
- return false; \
353
- } \
354
- \
355
- GetCode \
356
- \
357
- offset += type->ref->size; \
351
+ do { \
352
+ for (uint32_t i = 0; i < len; i++) { \
353
+ Napi::Value value = array[i]; \
354
+ \
355
+ int16_t align = std::max(ref->align, realign); \
356
+ \
357
+ offset = AlignLen(offset, align); \
358
+ uint8_t *dest = origin + offset; \
359
+ \
360
+ if (RG_UNLIKELY(!(Check))) { \
361
+ ThrowError<Napi::TypeError>(env, "Unexpected value %1 in array, expected %2", GetValueType(instance, value), (Expected)); \
362
+ return false; \
358
363
  } \
359
- } while (false)
364
+ \
365
+ GetCode \
366
+ \
367
+ offset += ref->size; \
368
+ } \
369
+ } while (false)
360
370
 
361
- switch (type->ref->primitive) {
362
- case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
371
+ switch (ref->primitive) {
372
+ case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
363
373
 
364
- case PrimitiveKind::Bool: {
365
- PUSH_ARRAY(value.IsBoolean(), "boolean", {
366
- bool b = value.As<Napi::Boolean>();
367
- *(bool *)dest = b;
368
- });
369
- } break;
370
- case PrimitiveKind::Int8: {
371
- PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
372
- int8_t v = CopyNumber<int8_t>(value);
373
- *(int8_t *)dest = v;
374
- });
375
- } break;
376
- case PrimitiveKind::UInt8: {
377
- PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
378
- uint8_t v = CopyNumber<uint8_t>(value);
379
- *(uint8_t *)dest = v;
380
- });
381
- } break;
382
- case PrimitiveKind::Int16: {
383
- PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
384
- int16_t v = CopyNumber<int16_t>(value);
385
- *(int16_t *)dest = v;
386
- });
387
- } break;
388
- case PrimitiveKind::UInt16: {
389
- PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
390
- uint16_t v = CopyNumber<uint16_t>(value);
391
- *(uint16_t *)dest = v;
392
- });
393
- } break;
394
- case PrimitiveKind::Int32: {
395
- PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
396
- int32_t v = CopyNumber<int32_t>(value);
397
- *(int32_t *)dest = v;
398
- });
399
- } break;
400
- case PrimitiveKind::UInt32: {
401
- PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
402
- uint32_t v = CopyNumber<uint32_t>(value);
403
- *(uint32_t *)dest = v;
404
- });
405
- } break;
406
- case PrimitiveKind::Int64: {
407
- PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
408
- int64_t v = CopyNumber<int64_t>(value);
409
- *(int64_t *)dest = v;
410
- });
411
- } break;
412
- case PrimitiveKind::UInt64: {
413
- PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
414
- uint64_t v = CopyNumber<uint64_t>(value);
415
- *(uint64_t *)dest = v;
416
- });
417
- } break;
418
- case PrimitiveKind::String: {
419
- PUSH_ARRAY(value.IsString(), "string", {
374
+ case PrimitiveKind::Bool: {
375
+ PUSH_ARRAY(value.IsBoolean(), "boolean", {
376
+ bool b = value.As<Napi::Boolean>();
377
+ *(bool *)dest = b;
378
+ });
379
+ } break;
380
+ case PrimitiveKind::Int8: {
381
+ PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
382
+ int8_t v = CopyNumber<int8_t>(value);
383
+ *(int8_t *)dest = v;
384
+ });
385
+ } break;
386
+ case PrimitiveKind::UInt8: {
387
+ PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
388
+ uint8_t v = CopyNumber<uint8_t>(value);
389
+ *(uint8_t *)dest = v;
390
+ });
391
+ } break;
392
+ case PrimitiveKind::Int16: {
393
+ PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
394
+ int16_t v = CopyNumber<int16_t>(value);
395
+ *(int16_t *)dest = v;
396
+ });
397
+ } break;
398
+ case PrimitiveKind::UInt16: {
399
+ PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
400
+ uint16_t v = CopyNumber<uint16_t>(value);
401
+ *(uint16_t *)dest = v;
402
+ });
403
+ } break;
404
+ case PrimitiveKind::Int32: {
405
+ PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
406
+ int32_t v = CopyNumber<int32_t>(value);
407
+ *(int32_t *)dest = v;
408
+ });
409
+ } break;
410
+ case PrimitiveKind::UInt32: {
411
+ PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
412
+ uint32_t v = CopyNumber<uint32_t>(value);
413
+ *(uint32_t *)dest = v;
414
+ });
415
+ } break;
416
+ case PrimitiveKind::Int64: {
417
+ PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
418
+ int64_t v = CopyNumber<int64_t>(value);
419
+ *(int64_t *)dest = v;
420
+ });
421
+ } break;
422
+ case PrimitiveKind::UInt64: {
423
+ PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
424
+ uint64_t v = CopyNumber<uint64_t>(value);
425
+ *(uint64_t *)dest = v;
426
+ });
427
+ } break;
428
+ case PrimitiveKind::String: {
429
+ PUSH_ARRAY(value.IsString() || IsNullOrUndefined(value), "string", {
430
+ if (!IsNullOrUndefined(value)) {
420
431
  const char *str = PushString(value);
421
432
  if (RG_UNLIKELY(!str))
422
433
  return false;
423
434
  *(const char **)dest = str;
424
- });
425
- } break;
426
- case PrimitiveKind::String16: {
427
- PUSH_ARRAY(value.IsString(), "string", {
435
+ } else {
436
+ *(const char **)dest = nullptr;
437
+ }
438
+ });
439
+ } break;
440
+ case PrimitiveKind::String16: {
441
+ PUSH_ARRAY(value.IsString() || IsNullOrUndefined(value), "string", {
442
+ if (!IsNullOrUndefined(value)) {
428
443
  const char16_t *str16 = PushString16(value);
429
444
  if (RG_UNLIKELY(!str16))
430
445
  return false;
431
446
  *(const char16_t **)dest = str16;
432
- });
433
- } break;
434
- case PrimitiveKind::Pointer: {
435
- PUSH_ARRAY(CheckValueTag(instance, value, type->ref) || IsNullOrUndefined(value), type->ref->name, {
436
- if (!IsNullOrUndefined(value)) {
437
- Napi::External external = value.As<Napi::External<void>>();
438
- *(void **)dest = external.Data();
439
- } else {
440
- *(void **)dest = nullptr;
441
- }
442
- });
443
- } break;
444
- case PrimitiveKind::Record: {
445
- PUSH_ARRAY(IsObject(value), "object", {
446
- Napi::Object obj2 = value.As<Napi::Object>();
447
- if (!PushObject(obj2, type->ref, dest, realign))
447
+ } else {
448
+ *(const char16_t **)dest = nullptr;
449
+ }
450
+ });
451
+ } break;
452
+ case PrimitiveKind::Pointer: {
453
+ PUSH_ARRAY(CheckValueTag(instance, value, ref) || IsNullOrUndefined(value), ref->name, {
454
+ if (!IsNullOrUndefined(value)) {
455
+ Napi::External external = value.As<Napi::External<void>>();
456
+ *(void **)dest = external.Data();
457
+ } else {
458
+ *(void **)dest = nullptr;
459
+ }
460
+ });
461
+ } break;
462
+ case PrimitiveKind::Record: {
463
+ PUSH_ARRAY(IsObject(value), "object", {
464
+ Napi::Object obj2 = value.As<Napi::Object>();
465
+ if (!PushObject(obj2, ref, dest, realign))
466
+ return false;
467
+ });
468
+ } break;
469
+ case PrimitiveKind::Array: {
470
+ for (uint32_t i = 0; i < len; i++) {
471
+ Napi::Value value = array[i];
472
+
473
+ int16_t align = std::max(ref->align, realign);
474
+ offset = AlignLen(offset, align);
475
+
476
+ uint8_t *dest = origin + offset;
477
+
478
+ if (value.IsArray()) {
479
+ Napi::Array array2 = value.As<Napi::Array>();
480
+ Size len2 = (Size)ref->size / ref->ref->size;
481
+
482
+ if (!PushNormalArray(array2, len2, ref->ref, dest, realign))
448
483
  return false;
449
- });
450
- } break;
451
- case PrimitiveKind::Array: {
452
- PUSH_ARRAY(value.IsArray() || value.IsTypedArray() || value.IsString(), "array", {
453
- Napi::Object array2 = value.As<Napi::Array>();
454
- if (!PushArray(array2, type->ref, dest, realign))
484
+ } else if (value.IsTypedArray()) {
485
+ Napi::TypedArray array2 = value.As<Napi::TypedArray>();
486
+ Size len2 = (Size)ref->size / ref->ref->size;
487
+
488
+ if (!PushTypedArray(array2, len2, ref->ref, dest, realign))
455
489
  return false;
456
- });
457
- } break;
458
- case PrimitiveKind::Float32: {
459
- PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
460
- float f = CopyNumber<float>(value);
461
- *(float *)dest = f;
462
- });
463
- } break;
464
- case PrimitiveKind::Float64: {
465
- PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
466
- double d = CopyNumber<double>(value);
467
- *(double *)dest = d;
468
- });
469
- } break;
470
- case PrimitiveKind::Callback: {
471
- for (uint32_t i = 0; i < len; i++) {
472
- Napi::Value value = array[i];
490
+ } else if (value.IsString() && !realign) {
491
+ if (!PushStringArray(value, ref, dest))
492
+ return false;
493
+ } else {
494
+ ThrowError<Napi::TypeError>(env, "Unexpected value %1 in array, expected array", GetValueType(instance, value));
495
+ return false;
496
+ }
497
+
498
+ offset += ref->size;
499
+ }
500
+ } break;
501
+ case PrimitiveKind::Float32: {
502
+ PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
503
+ float f = CopyNumber<float>(value);
504
+ *(float *)dest = f;
505
+ });
506
+ } break;
507
+ case PrimitiveKind::Float64: {
508
+ PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
509
+ double d = CopyNumber<double>(value);
510
+ *(double *)dest = d;
511
+ });
512
+ } break;
513
+ case PrimitiveKind::Callback: {
514
+ for (uint32_t i = 0; i < len; i++) {
515
+ Napi::Value value = array[i];
473
516
 
474
- int16_t align = std::max(type->ref->align, realign);
475
- offset = AlignLen(offset, align);
517
+ int16_t align = std::max(ref->align, realign);
518
+ offset = AlignLen(offset, align);
476
519
 
477
- uint8_t *dest = origin + offset;
520
+ uint8_t *dest = origin + offset;
478
521
 
479
- void *ptr;
522
+ void *ptr;
480
523
 
481
- if (value.IsFunction()) {
482
- Napi::Function func = value.As<Napi::Function>();
524
+ if (value.IsFunction()) {
525
+ Napi::Function func = value.As<Napi::Function>();
483
526
 
484
- ptr = ReserveTrampoline(type->proto, func);
485
- if (RG_UNLIKELY(!ptr))
486
- return false;
487
- } else if (CheckValueTag(instance, value, type->ref)) {
488
- Napi::External external = value.As<Napi::External<void>>();
489
- ptr = external.Data();
490
- } else if (IsNullOrUndefined(value)) {
491
- ptr = nullptr;
492
- } else {
493
- ThrowError<Napi::TypeError>(env, "Unexpected value %1 in array, expected %2", GetValueType(instance, value), type->ref->name);
527
+ ptr = ReserveTrampoline(ref->proto, func);
528
+ if (RG_UNLIKELY(!ptr))
494
529
  return false;
495
- }
530
+ } else if (CheckValueTag(instance, value, ref)) {
531
+ Napi::External external = value.As<Napi::External<void>>();
532
+ ptr = external.Data();
533
+ } else if (IsNullOrUndefined(value)) {
534
+ ptr = nullptr;
535
+ } else {
536
+ ThrowError<Napi::TypeError>(env, "Unexpected value %1 in array, expected %2", GetValueType(instance, value), ref->name);
537
+ return false;
538
+ }
496
539
 
497
- *(void **)dest = ptr;
540
+ *(void **)dest = ptr;
498
541
 
499
- offset += type->ref->size;
500
- }
501
- } break;
502
- }
542
+ offset += ref->size;
543
+ }
544
+ } break;
545
+ }
503
546
 
504
547
  #undef PUSH_ARRAY
505
- } else if (obj.IsTypedArray()) {
506
- Napi::TypedArray array = obj.As<Napi::TypedArray>();
507
- const uint8_t *buf = (const uint8_t *)array.ArrayBuffer().Data();
508
548
 
509
- if (RG_UNLIKELY(array.ElementLength() != len)) {
510
- ThrowError<Napi::Error>(env, "Expected array of length %1, got %2", len, array.ElementLength());
511
- return false;
512
- }
549
+ return true;
550
+ }
513
551
 
514
- bool match;
515
- switch (type->ref->primitive) {
516
- case PrimitiveKind::Int8: { match = (array.TypedArrayType() == napi_int8_array); } break;
517
- case PrimitiveKind::UInt8: { match = (array.TypedArrayType() == napi_uint8_array); } break;
518
- case PrimitiveKind::Int16: { match = (array.TypedArrayType() == napi_int16_array); } break;
519
- case PrimitiveKind::UInt16: { match = (array.TypedArrayType() == napi_uint16_array); } break;
520
- case PrimitiveKind::Int32: { match = (array.TypedArrayType() == napi_int32_array); } break;
521
- case PrimitiveKind::UInt32: { match = (array.TypedArrayType() == napi_uint32_array); } break;
522
- case PrimitiveKind::Float32: { match = (array.TypedArrayType() == napi_float32_array); } break;
523
- case PrimitiveKind::Float64: { match = (array.TypedArrayType() == napi_float64_array); } break;
524
-
525
- default: { match = false; } break;
526
- }
527
- if (RG_UNLIKELY(!match)) {
528
- ThrowError<Napi::TypeError>(env, "TypedArray is not approriate for %1 array", type->ref->name);
529
- return false;
530
- }
552
+ bool CallData::PushTypedArray(Napi::TypedArray array, Size len, const TypeInfo *ref, uint8_t *origin, int16_t realign)
553
+ {
554
+ RG_ASSERT(array.IsTypedArray());
555
+
556
+ if (RG_UNLIKELY(array.ElementLength() != (size_t)len)) {
557
+ ThrowError<Napi::Error>(env, "Expected array of length %1, got %2", len, array.ElementLength());
558
+ return false;
559
+ }
531
560
 
561
+ Size offset = 0;
562
+ const uint8_t *buf = (const uint8_t *)array.ArrayBuffer().Data();
563
+
564
+ if (RG_UNLIKELY(array.TypedArrayType() != GetTypedArrayType(ref))) {
565
+ ThrowError<Napi::TypeError>(env, "Cannot use %1 value for %2 array", GetValueType(instance, array), ref->name);
566
+ return false;
567
+ }
568
+
569
+ if (realign) {
532
570
  for (uint32_t i = 0; i < len; i++) {
533
- int16_t align = std::max(type->ref->align, realign);
571
+ int16_t align = std::max(ref->align, realign);
534
572
  offset = AlignLen(offset, align);
535
573
 
536
574
  uint8_t *dest = origin + offset;
537
575
 
538
- memcpy(dest, buf + i * type->ref->size, type->ref->size);
576
+ memcpy(dest, buf + i * ref->size, ref->size);
539
577
 
540
- offset += type->ref->size;
578
+ offset += ref->size;
541
579
  }
542
- } else if (obj.IsString()) {
543
- size_t encoded = 0;
580
+ } else {
581
+ memcpy_safe(origin, buf, (size_t)array.ByteLength());
582
+ }
544
583
 
545
- if (type->ref->primitive == PrimitiveKind::Int8 || type->ref->primitive == PrimitiveKind::UInt8) {
584
+ return true;
585
+ }
586
+
587
+ bool CallData::PushStringArray(Napi::Value obj, const TypeInfo *type, uint8_t *origin)
588
+ {
589
+ RG_ASSERT(obj.IsString());
590
+ RG_ASSERT(type->primitive == PrimitiveKind::Array);
591
+
592
+ size_t encoded = 0;
593
+
594
+ switch (type->ref->primitive) {
595
+ case PrimitiveKind::Int8: {
546
596
  napi_status status = napi_get_value_string_utf8(env, obj, (char *)origin, type->size, &encoded);
547
597
  RG_ASSERT(status == napi_ok);
548
- } else if (type->ref->primitive == PrimitiveKind::Int16 || type->ref->primitive == PrimitiveKind::UInt16) {
598
+ } break;
599
+ case PrimitiveKind::Int16: {
549
600
  napi_status status = napi_get_value_string_utf16(env, obj, (char16_t *)origin, type->size / 2, &encoded);
550
601
  RG_ASSERT(status == napi_ok);
551
602
 
552
603
  encoded *= 2;
553
- } else {
604
+ } break;
605
+
606
+ default: {
554
607
  ThrowError<Napi::TypeError>(env, "Strings cannot be converted to %1 array", type->ref->name);
555
608
  return false;
556
- }
557
-
558
- memset_safe(origin + encoded, 0, type->size - encoded);
559
- } else {
560
- RG_UNREACHABLE();
609
+ } break;
561
610
  }
562
611
 
612
+ memset_safe(origin + encoded, 0, type->size - encoded);
613
+
563
614
  return true;
564
615
  }
565
616
 
617
+ bool CallData::PushPointer(Napi::Value value, const ParameterInfo &param, void **out_ptr)
618
+ {
619
+ switch (value.Type()) {
620
+ case napi_undefined:
621
+ case napi_null: {
622
+ *out_ptr = nullptr;
623
+ return true;
624
+ } break;
625
+
626
+ case napi_external: {
627
+ if (RG_UNLIKELY(!CheckValueTag(instance, value, param.type)))
628
+ goto unexpected;
629
+
630
+ *out_ptr = value.As<Napi::External<uint8_t>>().Data();
631
+ return true;
632
+ } break;
633
+
634
+ case napi_object: {
635
+ uint8_t *ptr = nullptr;
636
+
637
+ if (value.IsArray()) {
638
+ Napi::Array array = value.As<Napi::Array>();
639
+
640
+ Size len = (Size)array.Length();
641
+ Size size = len * param.type->ref->size;
642
+
643
+ ptr = AllocHeap(size, 16);
644
+
645
+ if (param.directions & 1) {
646
+ if (!PushNormalArray(array, len, param.type->ref, ptr))
647
+ return false;
648
+ } else {
649
+ memset(ptr, 0, size);
650
+ }
651
+ } else if (value.IsTypedArray()) {
652
+ Napi::TypedArray array = value.As<Napi::TypedArray>();
653
+
654
+ Size len = (Size)array.ElementLength();
655
+ Size size = (Size)array.ByteLength();
656
+
657
+ ptr = AllocHeap(size, 16);
658
+
659
+ if (param.directions & 1) {
660
+ if (!PushTypedArray(array, len, param.type->ref, ptr))
661
+ return false;
662
+ } else {
663
+ if (RG_UNLIKELY(array.TypedArrayType() != GetTypedArrayType(param.type->ref))) {
664
+ ThrowError<Napi::TypeError>(env, "Cannot use %1 value for %2 array", GetValueType(instance, array), param.type->ref->name);
665
+ return false;
666
+ }
667
+
668
+ memset(ptr, 0, size);
669
+ }
670
+ } else if (RG_LIKELY(param.type->ref->primitive == PrimitiveKind::Record)) {
671
+ Napi::Object obj = value.As<Napi::Object>();
672
+ RG_ASSERT(IsObject(value));
673
+
674
+ ptr = AllocHeap(param.type->ref->size, 16);
675
+
676
+ if (param.directions & 1) {
677
+ if (!PushObject(obj, param.type->ref, ptr))
678
+ return false;
679
+ } else {
680
+ memset(ptr, 0, param.type->size);
681
+ }
682
+ } else {
683
+ goto unexpected;
684
+ }
685
+
686
+ if (param.directions & 2) {
687
+ OutArgument *out = out_arguments.AppendDefault();
688
+
689
+ napi_status status = napi_create_reference(env, value, 1, &out->ref);
690
+ RG_ASSERT(status == napi_ok);
691
+
692
+ out->ptr = ptr;
693
+ out->type = param.type->ref;
694
+ }
695
+
696
+ *out_ptr = ptr;
697
+ return true;
698
+ } break;
699
+
700
+ default: {} break;
701
+ }
702
+
703
+ unexpected:
704
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected %3", GetValueType(instance, value), param.offset + 1, param.type->name);
705
+ return false;
706
+ }
707
+
708
+ static inline Napi::Value GetReferenceValue(Napi::Env env, napi_ref ref)
709
+ {
710
+ napi_value value;
711
+
712
+ napi_status status = napi_get_reference_value(env, ref, &value);
713
+ RG_ASSERT(status == napi_ok);
714
+
715
+ return Napi::Value(env, value);
716
+ }
717
+
718
+ void CallData::PopOutArguments()
719
+ {
720
+ for (const OutArgument &out: out_arguments) {
721
+ Napi::Value value = GetReferenceValue(env, out.ref);
722
+ RG_ASSERT(!value.IsEmpty());
723
+
724
+ if (value.IsArray()) {
725
+ Napi::Array array(env, value);
726
+ PopNormalArray(array, out.ptr, out.type);
727
+ } else if (value.IsTypedArray()) {
728
+ Napi::TypedArray array(env, value);
729
+ PopTypedArray(array, out.ptr, out.type);
730
+ } else {
731
+ Napi::Object obj(env, value);
732
+ PopObject(obj, out.ptr, out.type);
733
+ }
734
+ }
735
+ }
736
+
566
737
  void *CallData::ReserveTrampoline(const FunctionInfo *proto, Napi::Function func)
567
738
  {
568
739
  uint32_t idx = CountTrailingZeros(instance->free_trampolines);
@@ -638,11 +809,11 @@ void CallData::PopObject(Napi::Object obj, const uint8_t *origin, const TypeInfo
638
809
  } break;
639
810
  case PrimitiveKind::String: {
640
811
  const char *str = *(const char **)src;
641
- obj.Set(member.name, Napi::String::New(env, str));
812
+ obj.Set(member.name, str ? Napi::String::New(env, str) : env.Null());
642
813
  } break;
643
814
  case PrimitiveKind::String16: {
644
815
  const char16_t *str16 = *(const char16_t **)src;
645
- obj.Set(member.name, Napi::String::New(env, str16));
816
+ obj.Set(member.name, str16 ? Napi::String::New(env, str16) : env.Null());
646
817
  } break;
647
818
  case PrimitiveKind::Pointer:
648
819
  case PrimitiveKind::Callback: {
@@ -697,10 +868,137 @@ static Size WideStringLength(const char16_t *str16, Size max)
697
868
  return len;
698
869
  }
699
870
 
700
- Napi::Value CallData::PopArray(const uint8_t *origin, const TypeInfo *type, int16_t realign)
871
+ void CallData::PopNormalArray(Napi::Array array, const uint8_t *origin, const TypeInfo *ref, int16_t realign)
701
872
  {
702
- InstanceData *instance = env.GetInstanceData<InstanceData>();
873
+ RG_ASSERT(array.IsArray());
874
+
875
+ Size offset = 0;
876
+ uint32_t len = array.Length();
877
+
878
+ #define POP_ARRAY(SetCode) \
879
+ do { \
880
+ for (uint32_t i = 0; i < len; i++) { \
881
+ int16_t align = std::max(realign, ref->align); \
882
+ offset = AlignLen(offset, align); \
883
+ \
884
+ const uint8_t *src = origin + offset; \
885
+ \
886
+ SetCode \
887
+ \
888
+ offset += ref->size; \
889
+ } \
890
+ } while (false)
891
+ #define POP_NUMBER_ARRAY(CType) \
892
+ do { \
893
+ POP_ARRAY({ \
894
+ double d = (double)*(CType *)src; \
895
+ array.Set(i, Napi::Number::New(env, d)); \
896
+ }); \
897
+ } while (false)
898
+
899
+ switch (ref->primitive) {
900
+ case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
901
+
902
+ case PrimitiveKind::Bool: {
903
+ POP_ARRAY({
904
+ bool b = *(bool *)src;
905
+ array.Set(i, Napi::Boolean::New(env, b));
906
+ });
907
+ } break;
908
+ case PrimitiveKind::Int8: { POP_NUMBER_ARRAY(int8_t); } break;
909
+ case PrimitiveKind::UInt8: { POP_NUMBER_ARRAY(uint8_t); } break;
910
+ case PrimitiveKind::Int16: { POP_NUMBER_ARRAY(int16_t); } break;
911
+ case PrimitiveKind::UInt16: { POP_NUMBER_ARRAY(uint16_t); } break;
912
+ case PrimitiveKind::Int32: { POP_NUMBER_ARRAY(int32_t); } break;
913
+ case PrimitiveKind::UInt32: { POP_NUMBER_ARRAY(uint32_t); } break;
914
+ case PrimitiveKind::Int64: {
915
+ POP_ARRAY({
916
+ int64_t v = *(int64_t *)src;
917
+ array.Set(i, Napi::BigInt::New(env, v));
918
+ });
919
+ } break;
920
+ case PrimitiveKind::UInt64: {
921
+ POP_ARRAY({
922
+ uint64_t v = *(uint64_t *)src;
923
+ array.Set(i, Napi::BigInt::New(env, v));
924
+ });
925
+ } break;
926
+ case PrimitiveKind::String: {
927
+ POP_ARRAY({
928
+ const char *str = *(const char **)src;
929
+ array.Set(i, str ? Napi::String::New(env, str) : env.Null());
930
+ });
931
+ } break;
932
+ case PrimitiveKind::String16: {
933
+ POP_ARRAY({
934
+ const char16_t *str16 = *(const char16_t **)src;
935
+ array.Set(i, str16 ? Napi::String::New(env, str16) : env.Null());
936
+ });
937
+ } break;
938
+ case PrimitiveKind::Pointer:
939
+ case PrimitiveKind::Callback: {
940
+ POP_ARRAY({
941
+ void *ptr2 = *(void **)src;
942
+
943
+ if (ptr2) {
944
+ Napi::External<void> external = Napi::External<void>::New(env, ptr2);
945
+ SetValueTag(instance, external, ref);
946
+
947
+ array.Set(i, external);
948
+ } else {
949
+ array.Set(i, env.Null());
950
+ }
951
+ });
952
+ } break;
953
+ case PrimitiveKind::Record: {
954
+ POP_ARRAY({
955
+ Napi::Object obj = PopObject(src, ref, realign);
956
+ array.Set(i, obj);
957
+ });
958
+ } break;
959
+ case PrimitiveKind::Array: {
960
+ POP_ARRAY({
961
+ Napi::Value value = PopArray(src, ref, realign);
962
+ array.Set(i, value);
963
+ });
964
+ } break;
965
+ case PrimitiveKind::Float32: { POP_NUMBER_ARRAY(float); } break;
966
+ case PrimitiveKind::Float64: { POP_NUMBER_ARRAY(double); } break;
967
+ }
968
+
969
+ #undef POP_NUMBER_ARRAY
970
+ #undef POP_ARRAY
971
+ }
972
+
973
+ void CallData::PopTypedArray(Napi::TypedArray array, const uint8_t *origin, const TypeInfo *ref, int16_t realign)
974
+ {
975
+ RG_ASSERT(array.IsTypedArray());
976
+ RG_ASSERT(GetTypedArrayType(ref) == array.TypedArrayType());
977
+
978
+ uint8_t *buf = (uint8_t *)array.ArrayBuffer().Data();
979
+
980
+ if (realign) {
981
+ Size offset = 0;
982
+ Size len = (Size)array.ElementLength();
983
+
984
+ for (Size i = 0; i < len; i++) {
985
+ int16_t align = std::max(ref->align, realign);
986
+ offset = AlignLen(offset, align);
703
987
 
988
+ uint8_t *dest = buf + i * ref->size;
989
+ const uint8_t *src = origin + offset;
990
+
991
+ memcpy(dest, src, ref->size);
992
+
993
+ offset += ref->size;
994
+ }
995
+ } else {
996
+ memcpy_safe(buf, origin, (size_t)array.ByteLength());
997
+ }
998
+ }
999
+
1000
+ Napi::Value CallData::PopArray(const uint8_t *origin, const TypeInfo *type, int16_t realign)
1001
+ {
704
1002
  RG_ASSERT(type->primitive == PrimitiveKind::Array);
705
1003
 
706
1004
  uint32_t len = type->size / type->ref->size;
@@ -732,18 +1030,7 @@ Napi::Value CallData::PopArray(const uint8_t *origin, const TypeInfo *type, int1
732
1030
  }); \
733
1031
  } else { \
734
1032
  Napi::TypedArrayType array = Napi::TypedArrayType::New(env, len); \
735
- \
736
- for (uint32_t i = 0; i < len; i++) { \
737
- int16_t align = std::max(realign, type->ref->align); \
738
- offset = AlignLen(offset, align); \
739
- \
740
- const uint8_t *src = origin + offset; \
741
- \
742
- CType f = *(CType *)src; \
743
- array[i] = f; \
744
- \
745
- offset += type->ref->size; \
746
- } \
1033
+ PopTypedArray(array, origin, type->ref, realign); \
747
1034
  \
748
1035
  return array; \
749
1036
  } \
@@ -771,19 +1058,7 @@ Napi::Value CallData::PopArray(const uint8_t *origin, const TypeInfo *type, int1
771
1058
 
772
1059
  POP_NUMBER_ARRAY(Int8Array, int8_t);
773
1060
  } break;
774
- case PrimitiveKind::UInt8: {
775
- if (type->hint == TypeInfo::ArrayHint::String) {
776
- RG_ASSERT(!realign);
777
-
778
- const char *ptr = (const char *)origin;
779
- size_t count = strnlen(ptr, (size_t)len);
780
-
781
- Napi::String str = Napi::String::New(env, ptr, count);
782
- return str;
783
- }
784
-
785
- POP_NUMBER_ARRAY(Uint8Array, uint8_t);
786
- } break;
1061
+ case PrimitiveKind::UInt8: { POP_NUMBER_ARRAY(Uint8Array, uint8_t); } break;
787
1062
  case PrimitiveKind::Int16: {
788
1063
  if (type->hint == TypeInfo::ArrayHint::String) {
789
1064
  RG_ASSERT(!realign);
@@ -797,19 +1072,7 @@ Napi::Value CallData::PopArray(const uint8_t *origin, const TypeInfo *type, int1
797
1072
 
798
1073
  POP_NUMBER_ARRAY(Int16Array, int16_t);
799
1074
  } break;
800
- case PrimitiveKind::UInt16: {
801
- if (type->hint == TypeInfo::ArrayHint::String) {
802
- RG_ASSERT(!realign);
803
-
804
- const char16_t *ptr = (const char16_t *)origin;
805
- Size count = WideStringLength(ptr, len);
806
-
807
- Napi::String str = Napi::String::New(env, ptr, count);
808
- return str;
809
- }
810
-
811
- POP_NUMBER_ARRAY(Uint16Array, uint16_t);
812
- } break;
1075
+ case PrimitiveKind::UInt16: { POP_NUMBER_ARRAY(Uint16Array, uint16_t); } break;
813
1076
  case PrimitiveKind::Int32: { POP_NUMBER_ARRAY(Int32Array, int32_t); } break;
814
1077
  case PrimitiveKind::UInt32: { POP_NUMBER_ARRAY(Uint32Array, uint32_t); } break;
815
1078
  case PrimitiveKind::Int64: {
@@ -827,13 +1090,13 @@ Napi::Value CallData::PopArray(const uint8_t *origin, const TypeInfo *type, int1
827
1090
  case PrimitiveKind::String: {
828
1091
  POP_ARRAY({
829
1092
  const char *str = *(const char **)src;
830
- array.Set(i, Napi::String::New(env, str));
1093
+ array.Set(i, str ? Napi::String::New(env, str) : env.Null());
831
1094
  });
832
1095
  } break;
833
1096
  case PrimitiveKind::String16: {
834
1097
  POP_ARRAY({
835
1098
  const char16_t *str16 = *(const char16_t **)src;
836
- array.Set(i, Napi::String::New(env, str16));
1099
+ array.Set(i, str16 ? Napi::String::New(env, str16) : env.Null());
837
1100
  });
838
1101
  } break;
839
1102
  case PrimitiveKind::Pointer: