koffi 1.3.12 → 2.0.0

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 (89) hide show
  1. package/CMakeLists.txt +7 -2
  2. package/ChangeLog.md +42 -16
  3. package/README.md +6 -0
  4. package/build/qemu/2.0.0/koffi_darwin_arm64.tar.gz +0 -0
  5. package/build/qemu/2.0.0/koffi_darwin_x64.tar.gz +0 -0
  6. package/build/qemu/2.0.0/koffi_freebsd_arm64.tar.gz +0 -0
  7. package/build/qemu/2.0.0/koffi_freebsd_ia32.tar.gz +0 -0
  8. package/build/qemu/2.0.0/koffi_freebsd_x64.tar.gz +0 -0
  9. package/build/qemu/2.0.0/koffi_linux_arm32hf.tar.gz +0 -0
  10. package/build/qemu/2.0.0/koffi_linux_arm64.tar.gz +0 -0
  11. package/build/qemu/2.0.0/koffi_linux_ia32.tar.gz +0 -0
  12. package/build/qemu/2.0.0/koffi_linux_riscv64hf64.tar.gz +0 -0
  13. package/build/qemu/2.0.0/koffi_linux_x64.tar.gz +0 -0
  14. package/build/qemu/2.0.0/koffi_openbsd_ia32.tar.gz +0 -0
  15. package/build/qemu/2.0.0/koffi_openbsd_x64.tar.gz +0 -0
  16. package/build/qemu/2.0.0/koffi_win32_arm64.tar.gz +0 -0
  17. package/build/qemu/2.0.0/koffi_win32_ia32.tar.gz +0 -0
  18. package/build/qemu/2.0.0/koffi_win32_x64.tar.gz +0 -0
  19. package/doc/benchmarks.md +2 -2
  20. package/doc/changes.md +156 -1
  21. package/doc/contribute.md +0 -1
  22. package/doc/dist/doctrees/changes.doctree +0 -0
  23. package/doc/dist/doctrees/environment.pickle +0 -0
  24. package/doc/dist/doctrees/functions.doctree +0 -0
  25. package/doc/dist/doctrees/types.doctree +0 -0
  26. package/doc/dist/html/_sources/changes.md.txt +156 -1
  27. package/doc/dist/html/_sources/functions.md.txt +8 -4
  28. package/doc/dist/html/_sources/types.md.txt +9 -0
  29. package/doc/dist/html/benchmarks.html +1 -1
  30. package/doc/dist/html/changes.html +226 -14
  31. package/doc/dist/html/contribute.html +1 -1
  32. package/doc/dist/html/functions.html +15 -12
  33. package/doc/dist/html/genindex.html +1 -1
  34. package/doc/dist/html/index.html +6 -16
  35. package/doc/dist/html/memory.html +3 -3
  36. package/doc/dist/html/objects.inv +0 -0
  37. package/doc/dist/html/platforms.html +1 -1
  38. package/doc/dist/html/search.html +1 -1
  39. package/doc/dist/html/searchindex.js +1 -1
  40. package/doc/dist/html/start.html +1 -1
  41. package/doc/dist/html/types.html +11 -3
  42. package/doc/functions.md +137 -13
  43. package/doc/types.md +35 -10
  44. package/package.json +1 -1
  45. package/qemu/registry/machines.json +5 -5
  46. package/qemu/registry/sha256sum.txt +16 -16
  47. package/src/abi_arm32.cc +90 -18
  48. package/src/abi_arm32_fwd.S +121 -57
  49. package/src/abi_arm64.cc +90 -18
  50. package/src/abi_arm64_fwd.S +96 -0
  51. package/src/abi_arm64_fwd.asm +128 -0
  52. package/src/abi_riscv64.cc +88 -18
  53. package/src/abi_riscv64_fwd.S +96 -0
  54. package/src/abi_x64_sysv.cc +93 -21
  55. package/src/abi_x64_sysv_fwd.S +96 -0
  56. package/src/abi_x64_win.cc +88 -18
  57. package/src/abi_x64_win_fwd.asm +128 -0
  58. package/src/abi_x86.cc +93 -18
  59. package/src/abi_x86_fwd.S +96 -0
  60. package/src/abi_x86_fwd.asm +128 -0
  61. package/src/call.cc +97 -63
  62. package/src/call.hh +2 -1
  63. package/src/ffi.cc +452 -140
  64. package/src/ffi.hh +23 -9
  65. package/src/parser.cc +18 -41
  66. package/src/util.cc +117 -27
  67. package/src/util.hh +3 -2
  68. package/test/callbacks.js +54 -8
  69. package/test/misc.c +29 -14
  70. package/test/raylib.js +1 -1
  71. package/test/sqlite.js +24 -16
  72. package/test/sync.js +41 -31
  73. package/vendor/libcc/libcc.cc +18 -5
  74. package/vendor/libcc/libcc.hh +70 -23
  75. package/build/qemu/1.3.12/koffi_darwin_arm64.tar.gz +0 -0
  76. package/build/qemu/1.3.12/koffi_darwin_x64.tar.gz +0 -0
  77. package/build/qemu/1.3.12/koffi_freebsd_arm64.tar.gz +0 -0
  78. package/build/qemu/1.3.12/koffi_freebsd_ia32.tar.gz +0 -0
  79. package/build/qemu/1.3.12/koffi_freebsd_x64.tar.gz +0 -0
  80. package/build/qemu/1.3.12/koffi_linux_arm32hf.tar.gz +0 -0
  81. package/build/qemu/1.3.12/koffi_linux_arm64.tar.gz +0 -0
  82. package/build/qemu/1.3.12/koffi_linux_ia32.tar.gz +0 -0
  83. package/build/qemu/1.3.12/koffi_linux_riscv64hf64.tar.gz +0 -0
  84. package/build/qemu/1.3.12/koffi_linux_x64.tar.gz +0 -0
  85. package/build/qemu/1.3.12/koffi_openbsd_ia32.tar.gz +0 -0
  86. package/build/qemu/1.3.12/koffi_openbsd_x64.tar.gz +0 -0
  87. package/build/qemu/1.3.12/koffi_win32_arm64.tar.gz +0 -0
  88. package/build/qemu/1.3.12/koffi_win32_ia32.tar.gz +0 -0
  89. package/build/qemu/1.3.12/koffi_win32_x64.tar.gz +0 -0
package/src/ffi.hh CHANGED
@@ -51,6 +51,7 @@ enum class PrimitiveKind {
51
51
  Array,
52
52
  Float32,
53
53
  Float64,
54
+ Prototype,
54
55
  Callback
55
56
  };
56
57
  static const char *const PrimitiveKindNames[] = {
@@ -71,6 +72,7 @@ static const char *const PrimitiveKindNames[] = {
71
72
  "Array",
72
73
  "Float32",
73
74
  "Float64",
75
+ "Prototype",
74
76
  "Callback"
75
77
  };
76
78
 
@@ -78,6 +80,8 @@ struct TypeInfo;
78
80
  struct RecordMember;
79
81
  struct FunctionInfo;
80
82
 
83
+ typedef void DisposeFunc (Napi::Env env, const TypeInfo *type, const void *ptr);
84
+
81
85
  struct TypeInfo {
82
86
  enum class ArrayHint {
83
87
  Array,
@@ -87,16 +91,22 @@ struct TypeInfo {
87
91
 
88
92
  const char *name;
89
93
 
90
- Napi::ObjectReference defn;
91
-
92
94
  PrimitiveKind primitive;
93
95
  int16_t size;
94
96
  int16_t align;
95
97
 
98
+ DisposeFunc *dispose;
99
+ Napi::FunctionReference dispose_ref;
100
+
96
101
  HeapArray<RecordMember> members; // Record only
97
- const TypeInfo *ref; // Pointer or array
102
+ union {
103
+ const void *marker;
104
+ const TypeInfo *type; // Pointer or array
105
+ const FunctionInfo *proto; // Callback only
106
+ } ref;
98
107
  ArrayHint hint; // Array only
99
- const FunctionInfo *proto; // Callback only
108
+
109
+ mutable Napi::ObjectReference defn;
100
110
 
101
111
  RG_HASHTABLE_HANDLER(TypeInfo, name);
102
112
  };
@@ -104,7 +114,7 @@ struct TypeInfo {
104
114
  struct RecordMember {
105
115
  const char *name;
106
116
  const TypeInfo *type;
107
- int16_t align;
117
+ int16_t offset;
108
118
  };
109
119
 
110
120
  struct LibraryHolder {
@@ -199,6 +209,7 @@ struct InstanceMemory {
199
209
  Span<uint8_t> heap;
200
210
 
201
211
  uint16_t generation; // Can wrap without risk
212
+
202
213
  int16_t depth;
203
214
  bool temporary;
204
215
  };
@@ -206,14 +217,15 @@ struct InstanceMemory {
206
217
  struct TrampolineInfo {
207
218
  const FunctionInfo *proto;
208
219
  Napi::FunctionReference func;
209
- uint16_t generation;
220
+
221
+ int32_t generation;
210
222
  };
211
223
 
212
224
  struct InstanceData {
213
225
  ~InstanceData();
214
226
 
215
227
  BucketArray<TypeInfo> types;
216
- HashTable<const char *, TypeInfo *> types_map;
228
+ HashMap<const char *, const TypeInfo *> types_map;
217
229
  BucketArray<FunctionInfo> callbacks;
218
230
 
219
231
  bool debug;
@@ -222,8 +234,10 @@ struct InstanceData {
222
234
  LocalArray<InstanceMemory *, 9> memories;
223
235
  int temporaries = 0;
224
236
 
225
- TrampolineInfo trampolines[MaxTrampolines];
226
- uint32_t free_trampolines = UINT32_MAX;
237
+ TrampolineInfo trampolines[MaxTrampolines * 2];
238
+ int16_t next_trampoline = 0;
239
+ int16_t temp_trampolines = 0;
240
+ uint32_t registered_trampolines = 0;
227
241
 
228
242
  BlockAllocator str_alloc;
229
243
 
package/src/parser.cc CHANGED
@@ -66,8 +66,9 @@ bool PrototypeParser::Parse(const char *str, FunctionInfo *out_func)
66
66
 
67
67
  param.type = ParseType();
68
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);
69
+ param.type->primitive == PrimitiveKind::Array ||
70
+ param.type->primitive == PrimitiveKind::Prototype) {
71
+ MarkError("Type %1 cannot be used as a parameter (maybe try %1 *)", param.type->name);
71
72
  return false;
72
73
  }
73
74
 
@@ -144,60 +145,36 @@ void PrototypeParser::Tokenize(const char *str)
144
145
 
145
146
  const TypeInfo *PrototypeParser::ParseType()
146
147
  {
147
- HeapArray<char> buf(&instance->str_alloc);
148
-
149
- Size indirect = 0;
150
-
151
148
  Size start = offset;
152
- while (offset < tokens.len && IsIdentifier(tokens[offset])) {
153
- Span<const char> tok = tokens[offset++];
154
149
 
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
- }
150
+ if (offset >= tokens.len) {
151
+ MarkError("Unexpected end of prototype, expected type");
152
+ return instance->types_map.FindValue("void", nullptr);
153
+ } else if (!IsIdentifier(tokens[offset])) {
154
+ MarkError("Unexpected token '%1', expected type", tokens[offset]);
166
155
  return instance->types_map.FindValue("void", nullptr);
167
156
  }
168
- while (offset < tokens.len && tokens[offset] == "*") {
157
+
158
+ while (offset < tokens.len && (IsIdentifier(tokens[offset]) ||
159
+ tokens[offset] == '*')) {
169
160
  offset++;
170
- indirect++;
171
161
  }
172
- buf.ptr[--buf.len] = 0;
162
+ offset += (offset < tokens.len && tokens[offset] == "!");
173
163
 
174
- while (buf.len) {
175
- const TypeInfo *type = instance->types_map.FindValue(buf.ptr, nullptr);
164
+ while (offset >= start) {
165
+ Span<const char> str = MakeSpan(tokens[start].ptr, tokens[offset].end() - tokens[start].ptr);
166
+ const TypeInfo *type = ResolveType(instance, str);
176
167
 
177
168
  if (type) {
178
- for (Size i = 0; i < indirect; i++) {
179
- type = GetPointerType(instance, type);
180
- RG_ASSERT(type);
181
- }
169
+ offset++;
182
170
  return type;
183
171
  }
184
172
 
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
173
  offset--;
198
174
  }
175
+ offset = start;
199
176
 
200
- MarkError("Unknown type '%1'", tokens[start]);
177
+ MarkError("Unknown or invalid type name '%1'", tokens[offset]);
201
178
  return instance->types_map.FindValue("void", nullptr);
202
179
  }
203
180
 
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,132 @@ 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
+ }
80
109
 
81
- instance->types_map.Set(type);
110
+ if (indirect) {
111
+ type = MakePointerType(instance, type, indirect);
112
+ RG_ASSERT(type);
82
113
  }
83
114
 
115
+ if (dispose) {
116
+ if (type->primitive != PrimitiveKind::String &&
117
+ type->primitive != PrimitiveKind::String16 &&
118
+ indirect != 1)
119
+ return nullptr;
120
+
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;
129
+ }
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
+
87
173
  const char *GetValueType(const InstanceData *instance, Napi::Value value)
88
174
  {
89
175
  for (const TypeInfo &type: instance->types) {
90
- if (CheckValueTag(instance, value, &type))
176
+ if (CheckValueTag(instance, value, type.ref.marker))
91
177
  return type.name;
92
178
  }
93
179
 
@@ -130,6 +216,8 @@ const char *GetValueType(const InstanceData *instance, Napi::Value value)
130
216
 
131
217
  void SetValueTag(const InstanceData *instance, Napi::Value value, const void *marker)
132
218
  {
219
+ RG_ASSERT(marker);
220
+
133
221
  napi_type_tag tag = { instance->tag_lower, (uint64_t)marker };
134
222
  napi_status status = napi_type_tag_object(value.Env(), value, &tag);
135
223
  RG_ASSERT(status == napi_ok);
@@ -137,6 +225,8 @@ void SetValueTag(const InstanceData *instance, Napi::Value value, const void *ma
137
225
 
138
226
  bool CheckValueTag(const InstanceData *instance, Napi::Value value, const void *marker)
139
227
  {
228
+ RG_ASSERT(marker);
229
+
140
230
  bool match = false;
141
231
 
142
232
  if (!IsNullOrUndefined(value)) {
@@ -174,8 +264,8 @@ static int AnalyseFlatRec(const TypeInfo *type, int offset, int count, FunctionR
174
264
  }
175
265
  }
176
266
  } else if (type->primitive == PrimitiveKind::Array) {
177
- count *= type->size / type->ref->size;
178
- offset = AnalyseFlatRec(type->ref, offset, count, func);
267
+ count *= type->size / type->ref.type->size;
268
+ offset = AnalyseFlatRec(type->ref.type, offset, count, func);
179
269
  } else {
180
270
  func(type, offset, count);
181
271
  offset += count;
package/src/util.hh CHANGED
@@ -67,8 +67,9 @@ 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);
72
73
 
73
74
  // Can be slow, only use for error messages
74
75
  const char *GetValueType(const InstanceData *instance, Napi::Value value);
package/test/callbacks.js CHANGED
@@ -32,13 +32,14 @@ const BFG = koffi.struct('BFG', {
32
32
  const SimpleCallback = koffi.callback('int SimpleCallback(const char *str)');
33
33
  const RecursiveCallback = koffi.callback('RecursiveCallback', 'float', ['int', 'str', 'double']);
34
34
  const BigCallback = koffi.callback('BFG BigCallback(BFG bfg)');
35
+ 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
36
  const ApplyCallback = koffi.callback('int __stdcall ApplyCallback(int a, int b, int c)');
36
37
  const IntCallback = koffi.callback('int IntCallback(int x)');
37
38
 
38
39
  const StructCallbacks = koffi.struct('StructCallbacks', {
39
- first: IntCallback,
40
- second: IntCallback,
41
- third: IntCallback
40
+ first: koffi.pointer(IntCallback),
41
+ second: 'IntCallback *',
42
+ third: 'IntCallback *'
42
43
  });
43
44
 
44
45
  main();
@@ -57,12 +58,15 @@ async function test() {
57
58
  const lib_filename = path.dirname(__filename) + '/build/misc' + koffi.extension;
58
59
  const lib = koffi.load(lib_filename);
59
60
 
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)');
61
+ const CallJS = lib.func('int CallJS(const char *str, SimpleCallback *cb)');
62
+ const CallRecursiveJS = lib.func('float CallRecursiveJS(int i, RecursiveCallback *func)');
63
+ const ModifyBFG = lib.func('BFG ModifyBFG(int x, double y, const char *str, BigCallback *func, _Out_ BFG *p)');
64
+ const Recurse8 = lib.func('void Recurse8(int i, SuperCallback *func)');
65
+ const ApplyStd = lib.func('int ApplyStd(int a, int b, int c, ApplyCallback *func)');
66
+ const ApplyMany = lib.func('int ApplyMany(int x, IntCallback **funcs, int length)');
65
67
  const ApplyStruct = lib.func('int ApplyStruct(int x, StructCallbacks callbacks)');
68
+ const SetCallback = lib.func('void SetCallback(IntCallback *func)');
69
+ const CallCallback = lib.func('int CallCallback(int x)');
66
70
 
67
71
  // Simple test similar to README example
68
72
  {
@@ -96,6 +100,35 @@ async function test() {
96
100
  assert.deepEqual(out, { a: 2, b: 4, c: -25, d: 'X/Yo!/X', e: 54, inner: { f: 10, g: 3 } });
97
101
  }
98
102
 
103
+ // With many parameters
104
+ {
105
+ let a = [], b = [], c = [], d = [], e = [], f = [], g = [], h = [];
106
+
107
+ let fn = (i, v1, v2, v3, v4, v5, v6, v7, v8) => {
108
+ a.push(v1);
109
+ b.push(v2);
110
+ c.push(v3);
111
+ d.push(v4);
112
+ e.push(v5);
113
+ f.push(v6);
114
+ g.push(v7);
115
+ h.push(v8);
116
+
117
+ if (i)
118
+ Recurse8(i - 1, fn);
119
+ };
120
+ Recurse8(3, fn);
121
+
122
+ assert.deepEqual(a, [3, 2, 1, 0]);
123
+ assert.deepEqual(b, [6, 4, 2, 0]);
124
+ assert.deepEqual(c, [4, 3, 2, 1]);
125
+ assert.deepEqual(d, [7, 5, 3, 1]);
126
+ assert.deepEqual(e, [0, 1, 2, 3]);
127
+ assert.deepEqual(f, [103, 102, 101, 100]);
128
+ assert.deepEqual(g, [1, 0, 1, 0]);
129
+ assert.deepEqual(h, [-4, -3, -2, -1]);
130
+ }
131
+
99
132
  // Stdcall callbacks
100
133
  {
101
134
  let ret = ApplyStd(1, 5, 9, (a, b, c) => a + b * c);
@@ -115,4 +148,17 @@ async function test() {
115
148
  let ret = ApplyStruct(27, callbacks);
116
149
  assert.equal(ret, -177);
117
150
  }
151
+
152
+ // Persistent callback
153
+ {
154
+ SetCallback(x => -x);
155
+ assert.throws(() => CallCallback(27), { message: /non-registered callback/ });
156
+
157
+ let cb = koffi.register(x => -x, koffi.pointer(IntCallback));
158
+ SetCallback(cb);
159
+ assert.equal(CallCallback(27), -27);
160
+
161
+ assert.equal(koffi.unregister(cb), null);
162
+ assert.throws(() => koffi.unregister(cb));
163
+ }
118
164
  }
package/test/misc.c CHANGED
@@ -406,24 +406,21 @@ const char *ReturnBigString(const char *str)
406
406
  EXPORT const char *ReturnBigString(const char *str)
407
407
  #endif
408
408
  {
409
- static char buf[16 * 1024 * 1024];
410
-
411
- size_t len = strlen(str);
412
- memcpy(buf, str, len + 1);
413
-
414
- return buf;
409
+ const char *copy = strdup(str);
410
+ return copy;
415
411
  }
416
412
 
417
413
  EXPORT const char *PrintFmt(const char *fmt, ...)
418
414
  {
419
- static char buf[256];
415
+ const int size = 256;
416
+ char *ptr = malloc(size);
420
417
 
421
418
  va_list ap;
422
419
  va_start(ap, fmt);
423
- vsnprintf(buf, sizeof(buf), fmt, ap);
420
+ vsnprintf(ptr, size, fmt, ap);
424
421
  va_end(ap);
425
422
 
426
- return buf;
423
+ return ptr;
427
424
  }
428
425
 
429
426
  size_t Length16(const char16_t *str)
@@ -437,16 +434,17 @@ size_t Length16(const char16_t *str)
437
434
 
438
435
  EXPORT const char16_t *Concat16(const char16_t *str1, const char16_t *str2)
439
436
  {
440
- static char16_t buf[1024];
437
+ const int size = 1024;
438
+ char16_t *ptr = malloc(size * 2);
441
439
 
442
440
  size_t len1 = Length16(str1);
443
441
  size_t len2 = Length16(str2);
444
442
 
445
- memcpy(buf, str1, len1 * 2);
446
- memcpy(buf + len1, str2, len2 * 2);
447
- buf[(len1 + len2) * 2] = 0;
443
+ memcpy(ptr, str1, len1 * 2);
444
+ memcpy(ptr + len1, str2, len2 * 2);
445
+ ptr[len1 + len2] = 0;
448
446
 
449
- return buf;
447
+ return ptr;
450
448
  }
451
449
 
452
450
  EXPORT FixedString ReturnFixedStr(FixedString str)
@@ -549,6 +547,11 @@ EXPORT BFG ModifyBFG(int x, double y, const char *str, BFG (*func)(BFG bfg), BFG
549
547
  return bfg;
550
548
  }
551
549
 
550
+ EXPORT void Recurse8(int i, void (*func)(int i, int v1, double v2, int v3, int v4, int v5, int v6, float v7, int v8))
551
+ {
552
+ func(i, i, (double)(i * 2), i + 1, i * 2 + 1, 3 - i, 100 + i, (float)(i % 2), -i - 1);
553
+ }
554
+
552
555
  EXPORT int ApplyStd(int a, int b, int c, ApplyCallback *func)
553
556
  {
554
557
  int ret = func(a, b, c);
@@ -618,3 +621,15 @@ EXPORT int ApplyStruct(int x, StructCallbacks callbacks)
618
621
 
619
622
  return x;
620
623
  }
624
+
625
+ static IntCallback *callback;
626
+
627
+ EXPORT void SetCallback(IntCallback *cb)
628
+ {
629
+ callback = cb;
630
+ }
631
+
632
+ EXPORT int CallCallback(int x)
633
+ {
634
+ return callback(x);
635
+ }
package/test/raylib.js CHANGED
@@ -55,7 +55,7 @@ const Rectangle = koffi.struct('Rectangle', {
55
55
  });
56
56
 
57
57
  const Texture = koffi.struct('Texture', {
58
- id: 'uint',
58
+ id: 'unsigned int', // Extra space is on purpose, leave it!
59
59
  width: 'int',
60
60
  height: 'int',
61
61
  mipmaps: 'int',
package/test/sqlite.js CHANGED
@@ -39,17 +39,17 @@ async function test() {
39
39
  let lib_filename = path.dirname(__filename) + '/build/sqlite3' + koffi.extension;
40
40
  let lib = koffi.load(lib_filename);
41
41
 
42
- const sqlite3_open_v2 = lib.func('sqlite3_open_v2', 'int', ['str', koffi.out(koffi.pointer(sqlite3_db)), 'int', 'str']);
43
- const sqlite3_exec = lib.func('sqlite3_exec', 'int', [sqlite3_db, 'str', 'void *', 'void *', 'void *']);
44
- const sqlite3_prepare_v2 = lib.func('sqlite3_prepare_v2', 'int', [sqlite3_db, 'str', 'int', koffi.out(koffi.pointer(sqlite3_stmt)), 'string']);
45
- const sqlite3_reset = lib.func('sqlite3_reset', 'int', [sqlite3_stmt]);
46
- const sqlite3_bind_text = lib.func('sqlite3_bind_text', 'int', [sqlite3_stmt, 'int', 'str', 'int', 'void *']);
47
- const sqlite3_bind_int = lib.func('sqlite3_bind_int', 'int', [sqlite3_stmt, 'int', 'int']);
48
- const sqlite3_column_text = lib.func('sqlite3_column_text', 'str', [sqlite3_stmt, 'int']);
49
- const sqlite3_column_int = lib.func('sqlite3_column_int', 'int', [sqlite3_stmt, 'int']);
50
- const sqlite3_step = lib.func('sqlite3_step', 'int', [sqlite3_stmt]);
51
- const sqlite3_finalize = lib.func('sqlite3_finalize', 'int', [sqlite3_stmt]);
52
- const sqlite3_close_v2 = lib.func('sqlite3_close_v2', 'int', [sqlite3_db]);
42
+ const sqlite3_open_v2 = lib.func('sqlite3_open_v2', 'int', ['str', koffi.out(koffi.pointer(sqlite3_db, 2)), 'int', 'str']);
43
+ const sqlite3_exec = lib.func('sqlite3_exec', 'int', [koffi.pointer(sqlite3_db), 'str', 'void *', 'void *', 'void *']);
44
+ const sqlite3_prepare_v2 = lib.func('sqlite3_prepare_v2', 'int', [koffi.pointer(sqlite3_db), 'str', 'int', koffi.out(koffi.pointer(sqlite3_stmt, 2)), 'string']);
45
+ const sqlite3_reset = lib.func('sqlite3_reset', 'int', [koffi.pointer(sqlite3_stmt)]);
46
+ const sqlite3_bind_text = lib.func('sqlite3_bind_text', 'int', [koffi.pointer(sqlite3_stmt), 'int', 'str', 'int', 'void *']);
47
+ const sqlite3_bind_int = lib.func('sqlite3_bind_int', 'int', [koffi.pointer(sqlite3_stmt), 'int', 'int']);
48
+ const sqlite3_column_text = lib.func('sqlite3_column_text', 'str', [koffi.pointer(sqlite3_stmt), 'int']);
49
+ const sqlite3_column_int = lib.func('sqlite3_column_int', 'int', [koffi.pointer(sqlite3_stmt), 'int']);
50
+ const sqlite3_step = lib.func('sqlite3_step', 'int', [koffi.pointer(sqlite3_stmt)]);
51
+ const sqlite3_finalize = lib.func('sqlite3_finalize', 'int', [koffi.pointer(sqlite3_stmt)]);
52
+ const sqlite3_close_v2 = lib.func('sqlite3_close_v2', 'int', [koffi.pointer(sqlite3_db)]);
53
53
 
54
54
  const SQLITE_OPEN_READWRITE = 0x2;
55
55
  const SQLITE_OPEN_CREATE = 0x4;
@@ -57,20 +57,27 @@ async function test() {
57
57
  const SQLITE_DONE = 101;
58
58
 
59
59
  let filename = await create_temporary_file(path.join(os.tmpdir(), 'test_sqlite'));
60
- let db = {};
60
+ let db = null;
61
61
 
62
62
  let expected = Array.from(Array(200).keys()).map(i => [`TXT ${i}`, i % 7]);
63
63
 
64
64
  try {
65
- if (sqlite3_open_v2(filename, db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, null) != 0)
65
+ let ptr = [null];
66
+
67
+ // Open database
68
+ if (sqlite3_open_v2(filename, ptr, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, null) != 0)
66
69
  throw new Error('Failed to open database');
70
+ db = ptr[0];
71
+
67
72
  if (sqlite3_exec(db, 'CREATE TABLE foo (id INTEGER PRIMARY KEY, bar TEXT, value INT);', null, null, null) != 0)
68
73
  throw new Error('Failed to create table');
69
74
 
70
- let stmt = {};
75
+ let stmt = null;
71
76
 
72
- if (sqlite3_prepare_v2(db, "INSERT INTO foo (bar, value) VALUES (?1, ?2)", -1, stmt, null) != 0)
77
+ if (sqlite3_prepare_v2(db, "INSERT INTO foo (bar, value) VALUES (?1, ?2)", -1, ptr, null) != 0)
73
78
  throw new Error('Failed to prepare insert statement for table foo');
79
+ stmt = ptr[0];
80
+
74
81
  for (let it of expected) {
75
82
  sqlite3_reset(stmt);
76
83
 
@@ -82,8 +89,9 @@ async function test() {
82
89
  }
83
90
  sqlite3_finalize(stmt);
84
91
 
85
- if (sqlite3_prepare_v2(db, "SELECT id, bar, value FROM foo ORDER BY id", -1, stmt, null) != 0)
92
+ if (sqlite3_prepare_v2(db, "SELECT id, bar, value FROM foo ORDER BY id", -1, ptr, null) != 0)
86
93
  throw new Error('Failed to prepare select statement for table foo');
94
+ stmt = ptr[0];
87
95
  for (let i = 0; i < expected.length; i++) {
88
96
  let it = expected[i];
89
97