koffi 1.3.12 → 2.1.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 (104) hide show
  1. package/CMakeLists.txt +8 -10
  2. package/ChangeLog.md +48 -16
  3. package/README.md +6 -0
  4. package/benchmark/atoi_koffi.js +12 -8
  5. package/benchmark/atoi_napi.js +12 -8
  6. package/benchmark/atoi_node_ffi.js +11 -10
  7. package/benchmark/raylib_cc.cc +12 -9
  8. package/benchmark/raylib_koffi.js +15 -13
  9. package/benchmark/raylib_node_ffi.js +15 -13
  10. package/benchmark/raylib_node_raylib.js +14 -11
  11. package/build/qemu/2.1.0-beta.1/koffi_darwin_arm64.tar.gz +0 -0
  12. package/build/qemu/2.1.0-beta.1/koffi_darwin_x64.tar.gz +0 -0
  13. package/build/qemu/2.1.0-beta.1/koffi_freebsd_arm64.tar.gz +0 -0
  14. package/build/qemu/2.1.0-beta.1/koffi_freebsd_ia32.tar.gz +0 -0
  15. package/build/qemu/2.1.0-beta.1/koffi_freebsd_x64.tar.gz +0 -0
  16. package/build/qemu/2.1.0-beta.1/koffi_linux_arm32hf.tar.gz +0 -0
  17. package/build/qemu/2.1.0-beta.1/koffi_linux_arm64.tar.gz +0 -0
  18. package/build/qemu/2.1.0-beta.1/koffi_linux_ia32.tar.gz +0 -0
  19. package/build/qemu/2.1.0-beta.1/koffi_linux_riscv64hf64.tar.gz +0 -0
  20. package/build/qemu/2.1.0-beta.1/koffi_linux_x64.tar.gz +0 -0
  21. package/build/qemu/2.1.0-beta.1/koffi_openbsd_ia32.tar.gz +0 -0
  22. package/build/qemu/2.1.0-beta.1/koffi_openbsd_x64.tar.gz +0 -0
  23. package/build/qemu/2.1.0-beta.1/koffi_win32_arm64.tar.gz +0 -0
  24. package/build/qemu/2.1.0-beta.1/koffi_win32_ia32.tar.gz +0 -0
  25. package/build/qemu/2.1.0-beta.1/koffi_win32_x64.tar.gz +0 -0
  26. package/doc/changes.md +160 -1
  27. package/doc/conf.py +14 -1
  28. package/doc/contribute.md +0 -1
  29. package/doc/dist/doctrees/benchmarks.doctree +0 -0
  30. package/doc/dist/doctrees/changes.doctree +0 -0
  31. package/doc/dist/doctrees/environment.pickle +0 -0
  32. package/doc/dist/doctrees/functions.doctree +0 -0
  33. package/doc/dist/doctrees/index.doctree +0 -0
  34. package/doc/dist/doctrees/types.doctree +0 -0
  35. package/doc/dist/html/.buildinfo +1 -1
  36. package/doc/dist/html/_sources/benchmarks.md.txt +2 -2
  37. package/doc/dist/html/_sources/changes.md.txt +160 -1
  38. package/doc/dist/html/_sources/functions.md.txt +17 -13
  39. package/doc/dist/html/_sources/types.md.txt +87 -35
  40. package/doc/dist/html/benchmarks.html +7 -3
  41. package/doc/dist/html/changes.html +241 -14
  42. package/doc/dist/html/contribute.html +5 -1
  43. package/doc/dist/html/functions.html +30 -23
  44. package/doc/dist/html/genindex.html +5 -1
  45. package/doc/dist/html/index.html +13 -19
  46. package/doc/dist/html/memory.html +7 -3
  47. package/doc/dist/html/objects.inv +0 -0
  48. package/doc/dist/html/platforms.html +6 -2
  49. package/doc/dist/html/search.html +5 -1
  50. package/doc/dist/html/searchindex.js +1 -1
  51. package/doc/dist/html/start.html +5 -1
  52. package/doc/dist/html/types.html +104 -43
  53. package/doc/functions.md +139 -15
  54. package/doc/templates/badges.html +5 -0
  55. package/doc/types.md +108 -40
  56. package/package.json +2 -2
  57. package/qemu/qemu.js +1 -1
  58. package/qemu/registry/machines.json +5 -5
  59. package/qemu/registry/sha256sum.txt +16 -16
  60. package/src/abi_arm32.cc +91 -19
  61. package/src/abi_arm32_fwd.S +121 -57
  62. package/src/abi_arm64.cc +91 -19
  63. package/src/abi_arm64_fwd.S +96 -0
  64. package/src/abi_arm64_fwd.asm +128 -0
  65. package/src/abi_riscv64.cc +89 -19
  66. package/src/abi_riscv64_fwd.S +96 -0
  67. package/src/abi_x64_sysv.cc +94 -22
  68. package/src/abi_x64_sysv_fwd.S +96 -0
  69. package/src/abi_x64_win.cc +89 -19
  70. package/src/abi_x64_win_fwd.asm +128 -0
  71. package/src/abi_x86.cc +94 -19
  72. package/src/abi_x86_fwd.S +96 -0
  73. package/src/abi_x86_fwd.asm +128 -0
  74. package/src/call.cc +128 -78
  75. package/src/call.hh +17 -4
  76. package/src/ffi.cc +514 -145
  77. package/src/ffi.hh +30 -9
  78. package/src/index.js +4 -2
  79. package/src/parser.cc +19 -44
  80. package/src/util.cc +160 -27
  81. package/src/util.hh +7 -2
  82. package/test/async.js +1 -2
  83. package/test/callbacks.js +56 -11
  84. package/test/misc.c +50 -15
  85. package/test/raylib.js +2 -2
  86. package/test/sqlite.js +27 -19
  87. package/test/sync.js +71 -35
  88. package/vendor/libcc/libcc.cc +18 -5
  89. package/vendor/libcc/libcc.hh +70 -23
  90. package/build/qemu/1.3.12/koffi_darwin_arm64.tar.gz +0 -0
  91. package/build/qemu/1.3.12/koffi_darwin_x64.tar.gz +0 -0
  92. package/build/qemu/1.3.12/koffi_freebsd_arm64.tar.gz +0 -0
  93. package/build/qemu/1.3.12/koffi_freebsd_ia32.tar.gz +0 -0
  94. package/build/qemu/1.3.12/koffi_freebsd_x64.tar.gz +0 -0
  95. package/build/qemu/1.3.12/koffi_linux_arm32hf.tar.gz +0 -0
  96. package/build/qemu/1.3.12/koffi_linux_arm64.tar.gz +0 -0
  97. package/build/qemu/1.3.12/koffi_linux_ia32.tar.gz +0 -0
  98. package/build/qemu/1.3.12/koffi_linux_riscv64hf64.tar.gz +0 -0
  99. package/build/qemu/1.3.12/koffi_linux_x64.tar.gz +0 -0
  100. package/build/qemu/1.3.12/koffi_openbsd_ia32.tar.gz +0 -0
  101. package/build/qemu/1.3.12/koffi_openbsd_x64.tar.gz +0 -0
  102. package/build/qemu/1.3.12/koffi_win32_arm64.tar.gz +0 -0
  103. package/build/qemu/1.3.12/koffi_win32_ia32.tar.gz +0 -0
  104. package/build/qemu/1.3.12/koffi_win32_x64.tar.gz +0 -0
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,
@@ -51,6 +52,7 @@ enum class PrimitiveKind {
51
52
  Array,
52
53
  Float32,
53
54
  Float64,
55
+ Prototype,
54
56
  Callback
55
57
  };
56
58
  static const char *const PrimitiveKindNames[] = {
@@ -71,6 +73,7 @@ static const char *const PrimitiveKindNames[] = {
71
73
  "Array",
72
74
  "Float32",
73
75
  "Float64",
76
+ "Prototype",
74
77
  "Callback"
75
78
  };
76
79
 
@@ -78,6 +81,8 @@ struct TypeInfo;
78
81
  struct RecordMember;
79
82
  struct FunctionInfo;
80
83
 
84
+ typedef void DisposeFunc (Napi::Env env, const TypeInfo *type, const void *ptr);
85
+
81
86
  struct TypeInfo {
82
87
  enum class ArrayHint {
83
88
  Array,
@@ -87,16 +92,22 @@ struct TypeInfo {
87
92
 
88
93
  const char *name;
89
94
 
90
- Napi::ObjectReference defn;
91
-
92
95
  PrimitiveKind primitive;
93
96
  int16_t size;
94
97
  int16_t align;
95
98
 
99
+ DisposeFunc *dispose;
100
+ Napi::FunctionReference dispose_ref;
101
+
96
102
  HeapArray<RecordMember> members; // Record only
97
- const TypeInfo *ref; // Pointer or array
103
+ union {
104
+ const void *marker;
105
+ const TypeInfo *type; // Pointer or array
106
+ const FunctionInfo *proto; // Callback only
107
+ } ref;
98
108
  ArrayHint hint; // Array only
99
- const FunctionInfo *proto; // Callback only
109
+
110
+ mutable Napi::ObjectReference defn;
100
111
 
101
112
  RG_HASHTABLE_HANDLER(TypeInfo, name);
102
113
  };
@@ -104,7 +115,7 @@ struct TypeInfo {
104
115
  struct RecordMember {
105
116
  const char *name;
106
117
  const TypeInfo *type;
107
- int16_t align;
118
+ int16_t offset;
108
119
  };
109
120
 
110
121
  struct LibraryHolder {
@@ -161,6 +172,11 @@ struct ParameterInfo {
161
172
  #endif
162
173
  };
163
174
 
175
+ struct ValueCast {
176
+ Napi::Reference<Napi::Value> ref;
177
+ const TypeInfo *type;
178
+ };
179
+
164
180
  // Also used for callbacks, even though many members are not used in this case
165
181
  struct FunctionInfo {
166
182
  mutable std::atomic_int refcount {1};
@@ -199,6 +215,7 @@ struct InstanceMemory {
199
215
  Span<uint8_t> heap;
200
216
 
201
217
  uint16_t generation; // Can wrap without risk
218
+
202
219
  int16_t depth;
203
220
  bool temporary;
204
221
  };
@@ -206,24 +223,28 @@ struct InstanceMemory {
206
223
  struct TrampolineInfo {
207
224
  const FunctionInfo *proto;
208
225
  Napi::FunctionReference func;
209
- uint16_t generation;
226
+
227
+ int32_t generation;
210
228
  };
211
229
 
212
230
  struct InstanceData {
213
231
  ~InstanceData();
214
232
 
215
233
  BucketArray<TypeInfo> types;
216
- HashTable<const char *, TypeInfo *> types_map;
234
+ HashMap<const char *, const TypeInfo *> types_map;
217
235
  BucketArray<FunctionInfo> callbacks;
218
236
 
219
237
  bool debug;
220
238
  uint64_t tag_lower;
239
+ const TypeInfo *void_type;
221
240
 
222
241
  LocalArray<InstanceMemory *, 9> memories;
223
242
  int temporaries = 0;
224
243
 
225
- TrampolineInfo trampolines[MaxTrampolines];
226
- uint32_t free_trampolines = UINT32_MAX;
244
+ TrampolineInfo trampolines[MaxTrampolines * 2];
245
+ int16_t next_trampoline = 0;
246
+ int16_t temp_trampolines = 0;
247
+ uint32_t registered_trampolines = 0;
227
248
 
228
249
  BlockAllocator str_alloc;
229
250
 
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,8 @@ 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
- MarkError("Type %1 cannot be used as a parameter", param.type->name);
68
+ if (!CanPassType(param.type)) {
69
+ MarkError("Type %1 cannot be used as a parameter (maybe try %1 *)", param.type->name);
71
70
  return false;
72
71
  }
73
72
 
@@ -144,60 +143,36 @@ void PrototypeParser::Tokenize(const char *str)
144
143
 
145
144
  const TypeInfo *PrototypeParser::ParseType()
146
145
  {
147
- HeapArray<char> buf(&instance->str_alloc);
148
-
149
- Size indirect = 0;
150
-
151
146
  Size start = offset;
152
- while (offset < tokens.len && IsIdentifier(tokens[offset])) {
153
- Span<const char> tok = tokens[offset++];
154
147
 
155
- if (tok != "const") {
156
- buf.Append(tok);
157
- buf.Append(' ');
158
- }
159
- }
160
- if (offset == start) {
161
- if (offset < tokens.len) {
162
- MarkError("Unexpected token '%1', expected type", tokens[offset]);
163
- } else {
164
- MarkError("Unexpected end of prototype, expected type");
165
- }
148
+ if (offset >= tokens.len) {
149
+ MarkError("Unexpected end of prototype, expected type");
150
+ return instance->types_map.FindValue("void", nullptr);
151
+ } else if (!IsIdentifier(tokens[offset])) {
152
+ MarkError("Unexpected token '%1', expected type", tokens[offset]);
166
153
  return instance->types_map.FindValue("void", nullptr);
167
154
  }
168
- while (offset < tokens.len && tokens[offset] == "*") {
155
+
156
+ while (offset < tokens.len && (IsIdentifier(tokens[offset]) ||
157
+ tokens[offset] == '*')) {
169
158
  offset++;
170
- indirect++;
171
159
  }
172
- buf.ptr[--buf.len] = 0;
160
+ offset += (offset < tokens.len && tokens[offset] == "!");
173
161
 
174
- while (buf.len) {
175
- const TypeInfo *type = instance->types_map.FindValue(buf.ptr, nullptr);
162
+ while (offset >= start) {
163
+ Span<const char> str = MakeSpan(tokens[start].ptr, tokens[offset].end() - tokens[start].ptr);
164
+ const TypeInfo *type = ResolveType(instance, str);
176
165
 
177
166
  if (type) {
178
- for (Size i = 0; i < indirect; i++) {
179
- type = GetPointerType(instance, type);
180
- RG_ASSERT(type);
181
- }
167
+ offset++;
182
168
  return type;
183
169
  }
184
170
 
185
- // Truncate last token
186
- {
187
- Span<const char> remain;
188
- SplitStrReverse(buf, ' ', &remain);
189
- buf.len = remain.len;
190
- buf.ptr[buf.len] = 0;
191
- }
192
-
193
- if (indirect) {
194
- offset -= indirect;
195
- indirect = 0;
196
- }
197
171
  offset--;
198
172
  }
173
+ offset = start;
199
174
 
200
- MarkError("Unknown type '%1'", tokens[start]);
175
+ MarkError("Unknown or invalid type name '%1'", tokens[offset]);
201
176
  return instance->types_map.FindValue("void", nullptr);
202
177
  }
203
178
 
package/src/util.cc CHANGED
@@ -20,21 +20,20 @@
20
20
 
21
21
  namespace RG {
22
22
 
23
- const TypeInfo *ResolveType(const InstanceData *instance, Napi::Value value, int *out_directions)
23
+ const TypeInfo *ResolveType(Napi::Value value, int *out_directions)
24
24
  {
25
+ Napi::Env env = value.Env();
26
+ InstanceData *instance = env.GetInstanceData<InstanceData>();
27
+
25
28
  if (value.IsString()) {
26
29
  std::string str = value.As<Napi::String>();
27
-
28
- const TypeInfo *type = instance->types_map.FindValue(str.c_str(), nullptr);
30
+ const TypeInfo *type = ResolveType(instance, str.c_str(), out_directions);
29
31
 
30
32
  if (!type) {
31
- ThrowError<Napi::TypeError>(value.Env(), "Unknown type name '%1'", str.c_str());
33
+ ThrowError<Napi::TypeError>(env, "Unknown or invalid type name '%1'", str.c_str());
32
34
  return nullptr;
33
35
  }
34
36
 
35
- if (out_directions) {
36
- *out_directions = 1;
37
- }
38
37
  return type;
39
38
  } else if (CheckValueTag(instance, value, &TypeInfoMarker)) {
40
39
  Napi::External<TypeInfo> external = value.As<Napi::External<TypeInfo>>();
@@ -49,45 +48,175 @@ const TypeInfo *ResolveType(const InstanceData *instance, Napi::Value value, int
49
48
  }
50
49
  return type;
51
50
  } else {
52
- ThrowError<Napi::TypeError>(value.Env(), "Unexpected %1 value as type specifier, expected string or type", GetValueType(instance, value));
51
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value as type specifier, expected string or type", GetValueType(instance, value));
53
52
  return nullptr;
54
53
  }
55
54
  }
56
55
 
57
- const TypeInfo *GetPointerType(InstanceData *instance, const TypeInfo *ref)
56
+ const TypeInfo *ResolveType(InstanceData *instance, Span<const char> str, int *out_directions)
58
57
  {
59
- // Special cases
60
- if (TestStr(ref->name, "char")) {
61
- return instance->types_map.FindValue("string", nullptr);
62
- } else if (TestStr(ref->name, "char16") || TestStr(ref->name, "char16_t")) {
63
- return instance->types_map.FindValue("string16", nullptr);
58
+ Span<const char> remain = TrimStr(str);
59
+
60
+ int indirect = 0;
61
+ bool dispose = false;
62
+
63
+ while (remain.len >= 6 && StartsWith(remain, "const") && IsAsciiWhite(remain[5])) {
64
+ remain = remain.Take(6, remain.len - 6);
65
+ remain = TrimStr(remain);
64
66
  }
67
+ if (remain.len && remain[remain.len - 1] == '!') {
68
+ dispose = true;
65
69
 
66
- char name_buf[256];
67
- Fmt(name_buf, "%1%2*", ref->name, ref->primitive == PrimitiveKind::Pointer ? "" : " ");
70
+ remain = remain.Take(0, remain.len - 1);
71
+ remain = TrimStr(remain);
72
+ }
73
+ while (remain.len) {
74
+ if (remain[remain.len - 1] == '*') {
75
+ remain = remain.Take(0, remain.len - 1);
76
+ indirect++;
77
+ } else if (remain.len >= 6 && EndsWith(remain, "const") && IsAsciiWhite(remain[remain.len - 6])) {
78
+ remain = remain.Take(0, remain.len - 6);
79
+ } else {
80
+ break;
81
+ }
82
+ remain = TrimStr(remain);
83
+ }
68
84
 
69
- TypeInfo *type = instance->types_map.FindValue(name_buf, nullptr);
85
+ const TypeInfo *type = instance->types_map.FindValue(remain, nullptr);
70
86
 
71
87
  if (!type) {
72
- type = instance->types.AppendDefault();
88
+ // Try with cleaned up spaces
89
+ if (remain.len < 256) {
90
+ LocalArray<char, 256> buf;
91
+ for (Size i = 0; i < remain.len; i++) {
92
+ char c = remain[i];
93
+
94
+ if (IsAsciiWhite(c)) {
95
+ buf.Append(' ');
96
+ while (++i < remain.len && IsAsciiWhite(remain[i]));
97
+ i--;
98
+ } else {
99
+ buf.Append(c);
100
+ }
101
+ }
73
102
 
74
- type->name = DuplicateString(name_buf, &instance->str_alloc).ptr;
103
+ type = instance->types_map.FindValue(buf, nullptr);
104
+ }
75
105
 
76
- type->primitive = PrimitiveKind::Pointer;
77
- type->size = RG_SIZE(void *);
78
- type->align = RG_SIZE(void *);
79
- type->ref = ref;
106
+ if (!type)
107
+ return nullptr;
108
+ }
109
+
110
+ if (indirect) {
111
+ type = MakePointerType(instance, type, indirect);
112
+ RG_ASSERT(type);
113
+ }
114
+
115
+ if (dispose) {
116
+ if (type->primitive != PrimitiveKind::String &&
117
+ type->primitive != PrimitiveKind::String16 &&
118
+ indirect != 1)
119
+ return nullptr;
80
120
 
81
- instance->types_map.Set(type);
121
+ TypeInfo *copy = instance->types.AppendDefault();
122
+
123
+ memcpy((void *)copy, (const void *)type, RG_SIZE(*type));
124
+ copy->name = "<anonymous>";
125
+ copy->members.allocator = GetNullAllocator();
126
+ copy->dispose = [](Napi::Env, const TypeInfo *, const void *ptr) { free((void *)ptr); };
127
+
128
+ type = copy;
82
129
  }
83
130
 
131
+ if (out_directions) {
132
+ *out_directions = 1;
133
+ }
84
134
  return type;
85
135
  }
86
136
 
137
+ const TypeInfo *MakePointerType(InstanceData *instance, const TypeInfo *ref, int count)
138
+ {
139
+ RG_ASSERT(count >= 1);
140
+
141
+ for (int i = 0; i < count; i++) {
142
+ char name_buf[256];
143
+ Fmt(name_buf, "%1%2*", ref->name, EndsWith(ref->name, "*") ? "" : " ");
144
+
145
+ TypeInfo *type = (TypeInfo *)instance->types_map.FindValue(name_buf, nullptr);
146
+
147
+ if (!type) {
148
+ type = instance->types.AppendDefault();
149
+
150
+ type->name = DuplicateString(name_buf, &instance->str_alloc).ptr;
151
+
152
+ if (ref->primitive != PrimitiveKind::Prototype) {
153
+ type->primitive = PrimitiveKind::Pointer;
154
+ type->size = RG_SIZE(void *);
155
+ type->align = RG_SIZE(void *);
156
+ type->ref.type = ref;
157
+ } else {
158
+ type->primitive = PrimitiveKind::Callback;
159
+ type->size = RG_SIZE(void *);
160
+ type->align = RG_SIZE(void *);
161
+ type->ref.proto = ref->ref.proto;
162
+ }
163
+
164
+ instance->types_map.Set(type->name, type);
165
+ }
166
+
167
+ ref = type;
168
+ }
169
+
170
+ return ref;
171
+ }
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
+
87
207
  const char *GetValueType(const InstanceData *instance, Napi::Value value)
88
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";
89
218
  for (const TypeInfo &type: instance->types) {
90
- if (CheckValueTag(instance, value, &type))
219
+ if (type.ref.marker && CheckValueTag(instance, value, type.ref.marker))
91
220
  return type.name;
92
221
  }
93
222
 
@@ -130,6 +259,8 @@ const char *GetValueType(const InstanceData *instance, Napi::Value value)
130
259
 
131
260
  void SetValueTag(const InstanceData *instance, Napi::Value value, const void *marker)
132
261
  {
262
+ RG_ASSERT(marker);
263
+
133
264
  napi_type_tag tag = { instance->tag_lower, (uint64_t)marker };
134
265
  napi_status status = napi_type_tag_object(value.Env(), value, &tag);
135
266
  RG_ASSERT(status == napi_ok);
@@ -137,6 +268,8 @@ void SetValueTag(const InstanceData *instance, Napi::Value value, const void *ma
137
268
 
138
269
  bool CheckValueTag(const InstanceData *instance, Napi::Value value, const void *marker)
139
270
  {
271
+ RG_ASSERT(marker);
272
+
140
273
  bool match = false;
141
274
 
142
275
  if (!IsNullOrUndefined(value)) {
@@ -174,8 +307,8 @@ static int AnalyseFlatRec(const TypeInfo *type, int offset, int count, FunctionR
174
307
  }
175
308
  }
176
309
  } else if (type->primitive == PrimitiveKind::Array) {
177
- count *= type->size / type->ref->size;
178
- offset = AnalyseFlatRec(type->ref, offset, count, func);
310
+ count *= type->size / type->ref.type->size;
311
+ offset = AnalyseFlatRec(type->ref.type, offset, count, func);
179
312
  } else {
180
313
  func(type, offset, count);
181
314
  offset += count;
package/src/util.hh CHANGED
@@ -67,8 +67,13 @@ static inline bool IsFloat(const TypeInfo *type)
67
67
  return fp;
68
68
  }
69
69
 
70
- const TypeInfo *ResolveType(const InstanceData *instance, Napi::Value value, int *out_directions = nullptr);
71
- const TypeInfo *GetPointerType(InstanceData *instance, const TypeInfo *type);
70
+ const TypeInfo *ResolveType(Napi::Value value, int *out_directions = nullptr);
71
+ const TypeInfo *ResolveType(InstanceData *instance, Span<const char> str, int *out_directions = nullptr);
72
+ const TypeInfo *MakePointerType(InstanceData *instance, const TypeInfo *type, int count = 1);
73
+
74
+ bool CanPassType(const TypeInfo *type);
75
+ bool CanReturnType(const TypeInfo *type);
76
+ bool CanStoreType(const TypeInfo *type);
72
77
 
73
78
  // Can be slow, only use for error messages
74
79
  const char *GetValueType(const InstanceData *instance, Napi::Value value);
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',
@@ -32,13 +31,14 @@ const BFG = koffi.struct('BFG', {
32
31
  const SimpleCallback = koffi.callback('int SimpleCallback(const char *str)');
33
32
  const RecursiveCallback = koffi.callback('RecursiveCallback', 'float', ['int', 'str', 'double']);
34
33
  const BigCallback = koffi.callback('BFG BigCallback(BFG bfg)');
34
+ const SuperCallback = koffi.callback('void SuperCallback(int i, int v1, double v2, int v3, int v4, int v5, int v6, float v7, int v8)');
35
35
  const ApplyCallback = koffi.callback('int __stdcall ApplyCallback(int a, int b, int c)');
36
36
  const IntCallback = koffi.callback('int IntCallback(int x)');
37
37
 
38
38
  const StructCallbacks = koffi.struct('StructCallbacks', {
39
- first: IntCallback,
40
- second: IntCallback,
41
- third: IntCallback
39
+ first: koffi.pointer(IntCallback),
40
+ second: 'IntCallback *',
41
+ third: 'IntCallback *'
42
42
  });
43
43
 
44
44
  main();
@@ -54,15 +54,18 @@ async function main() {
54
54
  }
55
55
 
56
56
  async function test() {
57
- const lib_filename = path.dirname(__filename) + '/build/misc' + koffi.extension;
57
+ const lib_filename = __dirname + '/build/misc' + koffi.extension;
58
58
  const lib = koffi.load(lib_filename);
59
59
 
60
- const CallJS = lib.func('int CallJS(const char *str, SimpleCallback cb)');
61
- const CallRecursiveJS = lib.func('float CallRecursiveJS(int i, RecursiveCallback func)');
62
- const ModifyBFG = lib.func('BFG ModifyBFG(int x, double y, const char *str, BigCallback func, _Out_ BFG *p)');
63
- const ApplyStd = lib.func('int ApplyStd(int a, int b, int c, ApplyCallback func)');
64
- const ApplyMany = lib.func('int ApplyMany(int x, IntCallback *funcs, int length)');
60
+ const CallJS = lib.func('int CallJS(const char *str, SimpleCallback *cb)');
61
+ const CallRecursiveJS = lib.func('float CallRecursiveJS(int i, RecursiveCallback *func)');
62
+ const ModifyBFG = lib.func('BFG ModifyBFG(int x, double y, const char *str, BigCallback *func, _Out_ BFG *p)');
63
+ const Recurse8 = lib.func('void Recurse8(int i, SuperCallback *func)');
64
+ const ApplyStd = lib.func('int ApplyStd(int a, int b, int c, ApplyCallback *func)');
65
+ const ApplyMany = lib.func('int ApplyMany(int x, IntCallback **funcs, int length)');
65
66
  const ApplyStruct = lib.func('int ApplyStruct(int x, StructCallbacks callbacks)');
67
+ const SetCallback = lib.func('void SetCallback(IntCallback *func)');
68
+ const CallCallback = lib.func('int CallCallback(int x)');
66
69
 
67
70
  // Simple test similar to README example
68
71
  {
@@ -96,6 +99,35 @@ async function test() {
96
99
  assert.deepEqual(out, { a: 2, b: 4, c: -25, d: 'X/Yo!/X', e: 54, inner: { f: 10, g: 3 } });
97
100
  }
98
101
 
102
+ // With many parameters
103
+ {
104
+ let a = [], b = [], c = [], d = [], e = [], f = [], g = [], h = [];
105
+
106
+ let fn = (i, v1, v2, v3, v4, v5, v6, v7, v8) => {
107
+ a.push(v1);
108
+ b.push(v2);
109
+ c.push(v3);
110
+ d.push(v4);
111
+ e.push(v5);
112
+ f.push(v6);
113
+ g.push(v7);
114
+ h.push(v8);
115
+
116
+ if (i)
117
+ Recurse8(i - 1, fn);
118
+ };
119
+ Recurse8(3, fn);
120
+
121
+ assert.deepEqual(a, [3, 2, 1, 0]);
122
+ assert.deepEqual(b, [6, 4, 2, 0]);
123
+ assert.deepEqual(c, [4, 3, 2, 1]);
124
+ assert.deepEqual(d, [7, 5, 3, 1]);
125
+ assert.deepEqual(e, [0, 1, 2, 3]);
126
+ assert.deepEqual(f, [103, 102, 101, 100]);
127
+ assert.deepEqual(g, [1, 0, 1, 0]);
128
+ assert.deepEqual(h, [-4, -3, -2, -1]);
129
+ }
130
+
99
131
  // Stdcall callbacks
100
132
  {
101
133
  let ret = ApplyStd(1, 5, 9, (a, b, c) => a + b * c);
@@ -115,4 +147,17 @@ async function test() {
115
147
  let ret = ApplyStruct(27, callbacks);
116
148
  assert.equal(ret, -177);
117
149
  }
150
+
151
+ // Persistent callback
152
+ {
153
+ SetCallback(x => -x);
154
+ assert.throws(() => CallCallback(27), { message: /non-registered callback/ });
155
+
156
+ let cb = koffi.register(x => -x, koffi.pointer(IntCallback));
157
+ SetCallback(cb);
158
+ assert.equal(CallCallback(27), -27);
159
+
160
+ assert.equal(koffi.unregister(cb), null);
161
+ assert.throws(() => koffi.unregister(cb));
162
+ }
118
163
  }