koffi 1.1.0-beta.8 → 1.1.2
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.
- package/CMakeLists.txt +3 -0
- package/README.md +13 -2
- package/build/qemu/1.1.2/koffi_darwin_x64.tar.gz +0 -0
- package/build/qemu/1.1.2/koffi_freebsd_arm64.tar.gz +0 -0
- package/build/qemu/1.1.2/koffi_freebsd_ia32.tar.gz +0 -0
- package/build/qemu/1.1.2/koffi_freebsd_x64.tar.gz +0 -0
- package/build/qemu/1.1.2/koffi_linux_arm.tar.gz +0 -0
- package/build/qemu/1.1.2/koffi_linux_arm64.tar.gz +0 -0
- package/build/qemu/1.1.2/koffi_linux_ia32.tar.gz +0 -0
- package/build/qemu/1.1.2/koffi_linux_x64.tar.gz +0 -0
- package/build/qemu/1.1.2/koffi_win32_ia32.tar.gz +0 -0
- package/build/qemu/1.1.2/koffi_win32_x64.tar.gz +0 -0
- package/package.json +2 -2
- package/qemu/qemu.js +1 -1
- package/src/call.cc +213 -44
- package/src/call.hh +2 -2
- package/src/ffi.cc +23 -18
- package/src/ffi.hh +3 -2
- package/src/parser.cc +30 -20
- package/src/parser.hh +3 -1
- package/src/util.hh +1 -1
- package/test/misc.c +17 -0
- package/build/qemu/1.1.0-beta.8/koffi_darwin_x64.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.8/koffi_freebsd_arm64.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.8/koffi_freebsd_ia32.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.8/koffi_freebsd_x64.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.8/koffi_linux_arm.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.8/koffi_linux_arm64.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.8/koffi_linux_ia32.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.8/koffi_linux_x64.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.8/koffi_win32_ia32.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.8/koffi_win32_x64.tar.gz +0 -0
package/CMakeLists.txt
CHANGED
|
@@ -23,6 +23,9 @@ if(MSVC)
|
|
|
23
23
|
else()
|
|
24
24
|
add_compile_options(-Wall -Wextra -Wno-missing-field-initializers
|
|
25
25
|
-Wno-unused-parameter -Wno-class-memaccess)
|
|
26
|
+
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
|
27
|
+
add_compile_options(-Wno-unknown-warning-option)
|
|
28
|
+
endif()
|
|
26
29
|
endif()
|
|
27
30
|
|
|
28
31
|
# ---- Koffi ----
|
package/README.md
CHANGED
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
|
|
21
21
|
Koffi is a fast and easy-to-use FFI module for Node.js, with support for primitive and aggregate data types (structs), both by reference (pointer) and by value.
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
The following features are planned in the near future:
|
|
24
24
|
|
|
25
|
-
* 1.1: Asynchronous calls and fixed-size array types
|
|
26
25
|
* 1.2: C to JS callbacks
|
|
26
|
+
* 1.3: Type parser
|
|
27
27
|
|
|
28
28
|
The following platforms __are officially supported and tested__ at the moment:
|
|
29
29
|
|
|
@@ -231,6 +231,8 @@ Koffi exposes three functions to explore type information:
|
|
|
231
231
|
Fixed-size arrays are declared with `koffi.array(type, length)`. Just like in C, they cannot be passed
|
|
232
232
|
as functions parameters (they degenerate to pointers), or returned by value. You can however embed them in struct types.
|
|
233
233
|
|
|
234
|
+
### JS typed arrays
|
|
235
|
+
|
|
234
236
|
Special rules apply for arrays of primitive integer and float types (uint32_t, double, etc...):
|
|
235
237
|
- When converting from JS to C, Koffi can take a normal Array (e.g. `[1, 2]`) or a TypedArray of the correct type (e.g. `Uint8Array` for an array of `uint8_t` numbers)
|
|
236
238
|
- When converting from C to JS (for return value or output parameters), Koffi will by default use a TypedArray. But you can change this behavior when you create the array type with the optional hint argument: `koffi.array('uint8_t', 64, 'array')`
|
|
@@ -258,6 +260,14 @@ console.log(ReturnFoo1({ i: 5, a16: [6, 8] })) // Prints { i: 5, a16: Int16Array
|
|
|
258
260
|
console.log(ReturnFoo2({ i: 5, a16: [6, 8] })) // Prints { i: 5, a16: [6, 8] }
|
|
259
261
|
```
|
|
260
262
|
|
|
263
|
+
### C strings
|
|
264
|
+
|
|
265
|
+
Koffi can also convert JS strings to fixed-sized arrays in the following cases:
|
|
266
|
+
- char (or int8_t) arrays are filled with the UTF-8 encoded string, truncated if needed. The buffer is always NUL-terminated.
|
|
267
|
+
- char16 (or int16_t) arrays are filled with the UTF-16 encoded string, truncated if needed. The buffer is always NUL-terminated.
|
|
268
|
+
|
|
269
|
+
The reverse case is also true, Koffi can convert a C fixed-size buffer to a JS string. Use the `string` array hint to do this (e.g. `koffi.array('char', 8, 'string')`).
|
|
270
|
+
|
|
261
271
|
## Variadic functions
|
|
262
272
|
|
|
263
273
|
Variadic functions are declared with an ellipsis as the last argument.
|
|
@@ -267,6 +277,7 @@ In order to call a variadic function, you must provide two Javascript arguments
|
|
|
267
277
|
```js
|
|
268
278
|
const printf = lib.func('printf', 'int', ['string', '...']);
|
|
269
279
|
|
|
280
|
+
// The variadic arguments are: 6 (int), 8.5 (double), 'THE END' (const char *)
|
|
270
281
|
printf('Integer %d, double %g, string %s', 'int', 6, 'double', 8.5, 'string', 'THE END');
|
|
271
282
|
```
|
|
272
283
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koffi",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "Fast and simple FFI (foreign function interface) for Node.js",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"foreign",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"license": "AGPL-3.0",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"cnoke": "^1.0.
|
|
27
|
+
"cnoke": "^1.0.9"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"chalk": "^4.1.2",
|
package/qemu/qemu.js
CHANGED
package/src/call.cc
CHANGED
|
@@ -142,21 +142,77 @@ bool CallData::PushObject(const Napi::Object &obj, const TypeInfo *type, uint8_t
|
|
|
142
142
|
bool b = value.As<Napi::Boolean>();
|
|
143
143
|
*(bool *)dest = b;
|
|
144
144
|
} break;
|
|
145
|
-
case PrimitiveKind::Int8:
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
145
|
+
case PrimitiveKind::Int8: {
|
|
146
|
+
if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
|
|
147
|
+
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for member '%2', expected number", GetValueType(instance, value), member.name);
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
int8_t v = CopyNumber<int8_t>(value);
|
|
152
|
+
*(int8_t *)dest = v;
|
|
153
|
+
} break;
|
|
154
|
+
case PrimitiveKind::UInt8: {
|
|
155
|
+
if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
|
|
156
|
+
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for member '%2', expected number", GetValueType(instance, value), member.name);
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
uint8_t v = CopyNumber<uint8_t>(value);
|
|
161
|
+
*(uint8_t *)dest = v;
|
|
162
|
+
} break;
|
|
163
|
+
case PrimitiveKind::Int16: {
|
|
164
|
+
if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
|
|
165
|
+
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for member '%2', expected number", GetValueType(instance, value), member.name);
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
int16_t v = CopyNumber<int16_t>(value);
|
|
170
|
+
*(int16_t *)dest = v;
|
|
171
|
+
} break;
|
|
172
|
+
case PrimitiveKind::UInt16: {
|
|
173
|
+
if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
|
|
174
|
+
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for member '%2', expected number", GetValueType(instance, value), member.name);
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
uint16_t v = CopyNumber<uint16_t>(value);
|
|
179
|
+
*(uint16_t *)dest = v;
|
|
180
|
+
} break;
|
|
181
|
+
case PrimitiveKind::Int32: {
|
|
182
|
+
if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
|
|
183
|
+
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for member '%2', expected number", GetValueType(instance, value), member.name);
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
int32_t v = CopyNumber<int32_t>(value);
|
|
188
|
+
*(int32_t *)dest = v;
|
|
189
|
+
} break;
|
|
190
|
+
case PrimitiveKind::UInt32: {
|
|
191
|
+
if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
|
|
192
|
+
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for member '%2', expected number", GetValueType(instance, value), member.name);
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
uint32_t v = CopyNumber<uint32_t>(value);
|
|
197
|
+
*(uint32_t *)dest = v;
|
|
198
|
+
} break;
|
|
199
|
+
case PrimitiveKind::Int64: {
|
|
153
200
|
if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
|
|
154
201
|
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for member '%2', expected number", GetValueType(instance, value), member.name);
|
|
155
202
|
return false;
|
|
156
203
|
}
|
|
157
204
|
|
|
158
205
|
int64_t v = CopyNumber<int64_t>(value);
|
|
159
|
-
|
|
206
|
+
*(int64_t *)dest = v;
|
|
207
|
+
} break;
|
|
208
|
+
case PrimitiveKind::UInt64: {
|
|
209
|
+
if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
|
|
210
|
+
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for member '%2', expected number", GetValueType(instance, value), member.name);
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
uint64_t v = CopyNumber<uint64_t>(value);
|
|
215
|
+
*(uint64_t *)dest = v;
|
|
160
216
|
} break;
|
|
161
217
|
case PrimitiveKind::String: {
|
|
162
218
|
if (RG_UNLIKELY(!value.IsString())) {
|
|
@@ -196,12 +252,12 @@ bool CallData::PushObject(const Napi::Object &obj, const TypeInfo *type, uint8_t
|
|
|
196
252
|
return false;
|
|
197
253
|
}
|
|
198
254
|
|
|
199
|
-
Napi::Object
|
|
200
|
-
if (!PushObject(
|
|
255
|
+
Napi::Object obj2 = value.As<Napi::Object>();
|
|
256
|
+
if (!PushObject(obj2, member.type, dest, realign))
|
|
201
257
|
return false;
|
|
202
258
|
} break;
|
|
203
259
|
case PrimitiveKind::Array: {
|
|
204
|
-
if (RG_UNLIKELY(!value.IsArray() && !value.IsTypedArray())) {
|
|
260
|
+
if (RG_UNLIKELY(!value.IsArray() && !value.IsTypedArray() && !value.IsString())) {
|
|
205
261
|
ThrowError<Napi::TypeError>(env, "Unexpected value %1 for member '%2', expected array", GetValueType(instance, value), member.name);
|
|
206
262
|
return false;
|
|
207
263
|
}
|
|
@@ -236,9 +292,9 @@ bool CallData::PushObject(const Napi::Object &obj, const TypeInfo *type, uint8_t
|
|
|
236
292
|
return true;
|
|
237
293
|
}
|
|
238
294
|
|
|
239
|
-
bool CallData::PushArray(const Napi::
|
|
295
|
+
bool CallData::PushArray(const Napi::Value &obj, const TypeInfo *type, uint8_t *dest, int16_t realign)
|
|
240
296
|
{
|
|
241
|
-
RG_ASSERT(obj.IsArray() || obj.IsTypedArray());
|
|
297
|
+
RG_ASSERT(obj.IsArray() || obj.IsTypedArray() || obj.IsString());
|
|
242
298
|
RG_ASSERT(type->primitive == PrimitiveKind::Array);
|
|
243
299
|
|
|
244
300
|
uint32_t len = type->size / type->ref->size;
|
|
@@ -279,17 +335,52 @@ bool CallData::PushArray(const Napi::Object &obj, const TypeInfo *type, uint8_t
|
|
|
279
335
|
*(bool *)dest = b;
|
|
280
336
|
});
|
|
281
337
|
} break;
|
|
282
|
-
case PrimitiveKind::Int8:
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
case PrimitiveKind::
|
|
289
|
-
|
|
338
|
+
case PrimitiveKind::Int8: {
|
|
339
|
+
PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
|
|
340
|
+
int8_t v = CopyNumber<int8_t>(value);
|
|
341
|
+
*(int8_t *)dest = v;
|
|
342
|
+
});
|
|
343
|
+
} break;
|
|
344
|
+
case PrimitiveKind::UInt8: {
|
|
345
|
+
PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
|
|
346
|
+
uint8_t v = CopyNumber<uint8_t>(value);
|
|
347
|
+
*(uint8_t *)dest = v;
|
|
348
|
+
});
|
|
349
|
+
} break;
|
|
350
|
+
case PrimitiveKind::Int16: {
|
|
351
|
+
PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
|
|
352
|
+
int16_t v = CopyNumber<int16_t>(value);
|
|
353
|
+
*(int16_t *)dest = v;
|
|
354
|
+
});
|
|
355
|
+
} break;
|
|
356
|
+
case PrimitiveKind::UInt16: {
|
|
357
|
+
PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
|
|
358
|
+
uint16_t v = CopyNumber<uint16_t>(value);
|
|
359
|
+
*(uint16_t *)dest = v;
|
|
360
|
+
});
|
|
361
|
+
} break;
|
|
362
|
+
case PrimitiveKind::Int32: {
|
|
363
|
+
PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
|
|
364
|
+
int32_t v = CopyNumber<int32_t>(value);
|
|
365
|
+
*(int32_t *)dest = v;
|
|
366
|
+
});
|
|
367
|
+
} break;
|
|
368
|
+
case PrimitiveKind::UInt32: {
|
|
369
|
+
PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
|
|
370
|
+
uint32_t v = CopyNumber<uint32_t>(value);
|
|
371
|
+
*(uint32_t *)dest = v;
|
|
372
|
+
});
|
|
373
|
+
} break;
|
|
374
|
+
case PrimitiveKind::Int64: {
|
|
290
375
|
PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
|
|
291
376
|
int64_t v = CopyNumber<int64_t>(value);
|
|
292
|
-
|
|
377
|
+
*(int64_t *)dest = v;
|
|
378
|
+
});
|
|
379
|
+
} break;
|
|
380
|
+
case PrimitiveKind::UInt64: {
|
|
381
|
+
PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
|
|
382
|
+
uint64_t v = CopyNumber<uint64_t>(value);
|
|
383
|
+
*(uint64_t *)dest = v;
|
|
293
384
|
});
|
|
294
385
|
} break;
|
|
295
386
|
case PrimitiveKind::String: {
|
|
@@ -316,15 +407,15 @@ bool CallData::PushArray(const Napi::Object &obj, const TypeInfo *type, uint8_t
|
|
|
316
407
|
} break;
|
|
317
408
|
case PrimitiveKind::Record: {
|
|
318
409
|
PUSH_ARRAY(IsObject(value), "object", {
|
|
319
|
-
Napi::Object
|
|
320
|
-
if (!PushObject(
|
|
410
|
+
Napi::Object obj2 = value.As<Napi::Object>();
|
|
411
|
+
if (!PushObject(obj2, type->ref, dest, realign))
|
|
321
412
|
return false;
|
|
322
413
|
});
|
|
323
414
|
} break;
|
|
324
415
|
case PrimitiveKind::Array: {
|
|
325
|
-
PUSH_ARRAY(value.IsArray() || value.IsTypedArray(), "array", {
|
|
326
|
-
Napi::Object
|
|
327
|
-
if (!PushArray(
|
|
416
|
+
PUSH_ARRAY(value.IsArray() || value.IsTypedArray() || value.IsString(), "array", {
|
|
417
|
+
Napi::Object array2 = value.As<Napi::Array>();
|
|
418
|
+
if (!PushArray(array2, type->ref, dest, realign))
|
|
328
419
|
return false;
|
|
329
420
|
});
|
|
330
421
|
} break;
|
|
@@ -343,7 +434,7 @@ bool CallData::PushArray(const Napi::Object &obj, const TypeInfo *type, uint8_t
|
|
|
343
434
|
}
|
|
344
435
|
|
|
345
436
|
#undef PUSH_ARRAY
|
|
346
|
-
} else {
|
|
437
|
+
} else if (obj.IsTypedArray()) {
|
|
347
438
|
Napi::TypedArray array = obj.As<Napi::TypedArray>();
|
|
348
439
|
const uint8_t *buf = (const uint8_t *)array.ArrayBuffer().Data();
|
|
349
440
|
|
|
@@ -378,6 +469,25 @@ bool CallData::PushArray(const Napi::Object &obj, const TypeInfo *type, uint8_t
|
|
|
378
469
|
|
|
379
470
|
dest += type->ref->size;
|
|
380
471
|
}
|
|
472
|
+
} else if (obj.IsString()) {
|
|
473
|
+
size_t encoded = 0;
|
|
474
|
+
|
|
475
|
+
if (type->ref->primitive == PrimitiveKind::Int8 || type->ref->primitive == PrimitiveKind::UInt8) {
|
|
476
|
+
napi_status status = napi_get_value_string_utf8(env, obj, (char *)dest, type->size, &encoded);
|
|
477
|
+
RG_ASSERT(status == napi_ok);
|
|
478
|
+
} else if (type->ref->primitive == PrimitiveKind::Int16 || type->ref->primitive == PrimitiveKind::UInt16) {
|
|
479
|
+
napi_status status = napi_get_value_string_utf16(env, obj, (char16_t *)dest, type->size / 2, &encoded);
|
|
480
|
+
RG_ASSERT(status == napi_ok);
|
|
481
|
+
|
|
482
|
+
encoded *= 2;
|
|
483
|
+
} else {
|
|
484
|
+
ThrowError<Napi::TypeError>(env, "Strings cannot be converted to %1 array", type->ref->name);
|
|
485
|
+
return false;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
memset_safe(dest + encoded, 0, type->size - encoded);
|
|
489
|
+
} else {
|
|
490
|
+
RG_UNREACHABLE();
|
|
381
491
|
}
|
|
382
492
|
|
|
383
493
|
return true;
|
|
@@ -451,8 +561,8 @@ void CallData::PopObject(Napi::Object obj, const uint8_t *src, const TypeInfo *t
|
|
|
451
561
|
obj.Set(member.name, obj2);
|
|
452
562
|
} break;
|
|
453
563
|
case PrimitiveKind::Array: {
|
|
454
|
-
Napi::
|
|
455
|
-
obj.Set(member.name,
|
|
564
|
+
Napi::Value value = PopArray(src, member.type, realign);
|
|
565
|
+
obj.Set(member.name, value);
|
|
456
566
|
} break;
|
|
457
567
|
case PrimitiveKind::Float32: {
|
|
458
568
|
float f;
|
|
@@ -477,7 +587,18 @@ Napi::Object CallData::PopObject(const uint8_t *src, const TypeInfo *type, int16
|
|
|
477
587
|
return obj;
|
|
478
588
|
}
|
|
479
589
|
|
|
480
|
-
|
|
590
|
+
Size WideStringLength(const char16_t *str16, Size max)
|
|
591
|
+
{
|
|
592
|
+
Size len = 0;
|
|
593
|
+
|
|
594
|
+
while (len < max && str16[len]) {
|
|
595
|
+
len++;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
return len;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
Napi::Value CallData::PopArray(const uint8_t *src, const TypeInfo *type, int16_t realign)
|
|
481
602
|
{
|
|
482
603
|
RG_ASSERT(type->primitive == PrimitiveKind::Array);
|
|
483
604
|
|
|
@@ -500,7 +621,12 @@ Napi::Object CallData::PopArray(const uint8_t *src, const TypeInfo *type, int16_
|
|
|
500
621
|
} while (false)
|
|
501
622
|
#define POP_NUMBER_ARRAY(TypedArrayType, CType) \
|
|
502
623
|
do { \
|
|
503
|
-
if (type->hint == TypeInfo::ArrayHint::
|
|
624
|
+
if (type->hint == TypeInfo::ArrayHint::Array) { \
|
|
625
|
+
POP_ARRAY({ \
|
|
626
|
+
double d = (double)*(CType *)src; \
|
|
627
|
+
array.Set(i, Napi::Number::New(env, d)); \
|
|
628
|
+
}); \
|
|
629
|
+
} else { \
|
|
504
630
|
Napi::TypedArrayType array = Napi::TypedArrayType::New(env, len); \
|
|
505
631
|
\
|
|
506
632
|
for (uint32_t i = 0; i < len; i++) { \
|
|
@@ -514,11 +640,6 @@ Napi::Object CallData::PopArray(const uint8_t *src, const TypeInfo *type, int16_
|
|
|
514
640
|
} \
|
|
515
641
|
\
|
|
516
642
|
return array; \
|
|
517
|
-
} else { \
|
|
518
|
-
POP_ARRAY({ \
|
|
519
|
-
double d = (double)*(CType *)src; \
|
|
520
|
-
array.Set(i, Napi::Number::New(env, d)); \
|
|
521
|
-
}); \
|
|
522
643
|
} \
|
|
523
644
|
} while (false)
|
|
524
645
|
|
|
@@ -531,10 +652,58 @@ Napi::Object CallData::PopArray(const uint8_t *src, const TypeInfo *type, int16_
|
|
|
531
652
|
array.Set(i, Napi::Boolean::New(env, b));
|
|
532
653
|
});
|
|
533
654
|
} break;
|
|
534
|
-
case PrimitiveKind::Int8: {
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
655
|
+
case PrimitiveKind::Int8: {
|
|
656
|
+
if (type->hint == TypeInfo::ArrayHint::String) {
|
|
657
|
+
RG_ASSERT(!realign);
|
|
658
|
+
|
|
659
|
+
const char *ptr = (const char *)src;
|
|
660
|
+
size_t count = strnlen(ptr, (size_t)len);
|
|
661
|
+
|
|
662
|
+
Napi::String str = Napi::String::New(env, ptr, count);
|
|
663
|
+
return str;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
POP_NUMBER_ARRAY(Int8Array, int8_t);
|
|
667
|
+
} break;
|
|
668
|
+
case PrimitiveKind::UInt8: {
|
|
669
|
+
if (type->hint == TypeInfo::ArrayHint::String) {
|
|
670
|
+
RG_ASSERT(!realign);
|
|
671
|
+
|
|
672
|
+
const char *ptr = (const char *)src;
|
|
673
|
+
size_t count = strnlen(ptr, (size_t)len);
|
|
674
|
+
|
|
675
|
+
Napi::String str = Napi::String::New(env, ptr, count);
|
|
676
|
+
return str;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
POP_NUMBER_ARRAY(Uint8Array, uint8_t);
|
|
680
|
+
} break;
|
|
681
|
+
case PrimitiveKind::Int16: {
|
|
682
|
+
if (type->hint == TypeInfo::ArrayHint::String) {
|
|
683
|
+
RG_ASSERT(!realign);
|
|
684
|
+
|
|
685
|
+
const char16_t *ptr = (const char16_t *)src;
|
|
686
|
+
Size count = WideStringLength(ptr, len);
|
|
687
|
+
|
|
688
|
+
Napi::String str = Napi::String::New(env, ptr, count);
|
|
689
|
+
return str;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
POP_NUMBER_ARRAY(Int16Array, int16_t);
|
|
693
|
+
} break;
|
|
694
|
+
case PrimitiveKind::UInt16: {
|
|
695
|
+
if (type->hint == TypeInfo::ArrayHint::String) {
|
|
696
|
+
RG_ASSERT(!realign);
|
|
697
|
+
|
|
698
|
+
const char16_t *ptr = (const char16_t *)src;
|
|
699
|
+
Size count = WideStringLength(ptr, len);
|
|
700
|
+
|
|
701
|
+
Napi::String str = Napi::String::New(env, ptr, count);
|
|
702
|
+
return str;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
POP_NUMBER_ARRAY(Uint16Array, uint16_t);
|
|
706
|
+
} break;
|
|
538
707
|
case PrimitiveKind::Int32: { POP_NUMBER_ARRAY(Int32Array, int32_t); } break;
|
|
539
708
|
case PrimitiveKind::UInt32: { POP_NUMBER_ARRAY(Uint32Array, uint32_t); } break;
|
|
540
709
|
case PrimitiveKind::Int64: {
|
|
@@ -579,8 +748,8 @@ Napi::Object CallData::PopArray(const uint8_t *src, const TypeInfo *type, int16_
|
|
|
579
748
|
} break;
|
|
580
749
|
case PrimitiveKind::Array: {
|
|
581
750
|
POP_ARRAY({
|
|
582
|
-
Napi::
|
|
583
|
-
array.Set(i,
|
|
751
|
+
Napi::Value value = PopArray(src, type->ref, realign);
|
|
752
|
+
array.Set(i, value);
|
|
584
753
|
});
|
|
585
754
|
} break;
|
|
586
755
|
case PrimitiveKind::Float32: { POP_NUMBER_ARRAY(Float32Array, float); } break;
|
package/src/call.hh
CHANGED
|
@@ -78,11 +78,11 @@ private:
|
|
|
78
78
|
const char *PushString(const Napi::Value &value);
|
|
79
79
|
const char16_t *PushString16(const Napi::Value &value);
|
|
80
80
|
bool PushObject(const Napi::Object &obj, const TypeInfo *type, uint8_t *dest, int16_t realign = 0);
|
|
81
|
-
bool PushArray(const Napi::
|
|
81
|
+
bool PushArray(const Napi::Value &obj, const TypeInfo *type, uint8_t *dest, int16_t realign = 0);
|
|
82
82
|
|
|
83
83
|
void PopObject(Napi::Object obj, const uint8_t *src, const TypeInfo *type, int16_t realign = 0);
|
|
84
84
|
Napi::Object PopObject(const uint8_t *src, const TypeInfo *type, int16_t realign = 0);
|
|
85
|
-
Napi::
|
|
85
|
+
Napi::Value PopArray(const uint8_t *src, const TypeInfo *type, int16_t realign = 0);
|
|
86
86
|
};
|
|
87
87
|
|
|
88
88
|
template <typename T>
|
package/src/ffi.cc
CHANGED
|
@@ -274,6 +274,8 @@ static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
|
|
|
274
274
|
hint = TypeInfo::ArrayHint::TypedArray;
|
|
275
275
|
} else if (to == "array") {
|
|
276
276
|
hint = TypeInfo::ArrayHint::Array;
|
|
277
|
+
} else if (to == "string") {
|
|
278
|
+
hint = TypeInfo::ArrayHint::String;
|
|
277
279
|
} else {
|
|
278
280
|
ThrowError<Napi::Error>(env, "Array conversion hint must be 'typed' or 'array'");
|
|
279
281
|
return env.Null();
|
|
@@ -297,7 +299,6 @@ static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
|
|
|
297
299
|
}
|
|
298
300
|
|
|
299
301
|
TypeInfo *type = instance->types.AppendDefault();
|
|
300
|
-
RG_DEFER_N(err_guard) { instance->types.RemoveLast(1); };
|
|
301
302
|
|
|
302
303
|
type->name = Fmt(&instance->str_alloc, "%1[%2]", ref->name, len).ptr;
|
|
303
304
|
|
|
@@ -307,13 +308,6 @@ static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
|
|
|
307
308
|
type->ref = ref;
|
|
308
309
|
type->hint = hint;
|
|
309
310
|
|
|
310
|
-
// If the insert succeeds, we cannot fail anymore
|
|
311
|
-
if (!instance->types_map.TrySet(type).second) {
|
|
312
|
-
ThrowError<Napi::Error>(env, "Duplicate type name '%1'", type->name);
|
|
313
|
-
return env.Null();
|
|
314
|
-
}
|
|
315
|
-
err_guard.Disable();
|
|
316
|
-
|
|
317
311
|
Napi::External<TypeInfo> external = Napi::External<TypeInfo>::New(env, type);
|
|
318
312
|
SetValueTag(instance, external, &TypeInfoMarker);
|
|
319
313
|
|
|
@@ -390,9 +384,11 @@ static Span<uint8_t> AllocateAndAlign16(Allocator *alloc, Size size)
|
|
|
390
384
|
return MakeSpan(aligned, size - delta);
|
|
391
385
|
}
|
|
392
386
|
|
|
393
|
-
static InstanceMemory *
|
|
387
|
+
static InstanceMemory *AllocateAsyncMemory(InstanceData *instance)
|
|
394
388
|
{
|
|
395
|
-
for (
|
|
389
|
+
for (Size i = 1; i < instance->memories.len; i++) {
|
|
390
|
+
InstanceMemory *mem = instance->memories[i];
|
|
391
|
+
|
|
396
392
|
if (!mem->depth)
|
|
397
393
|
return mem;
|
|
398
394
|
}
|
|
@@ -422,7 +418,7 @@ static Napi::Value TranslateNormalCall(const Napi::CallbackInfo &info)
|
|
|
422
418
|
return env.Null();
|
|
423
419
|
}
|
|
424
420
|
|
|
425
|
-
InstanceMemory *mem =
|
|
421
|
+
InstanceMemory *mem = instance->memories[0];
|
|
426
422
|
CallData call(env, instance, func, mem);
|
|
427
423
|
|
|
428
424
|
return call.Run(info);
|
|
@@ -482,7 +478,7 @@ static Napi::Value TranslateVariadicCall(const Napi::CallbackInfo &info)
|
|
|
482
478
|
if (RG_UNLIKELY(!AnalyseFunction(instance, &func)))
|
|
483
479
|
return env.Null();
|
|
484
480
|
|
|
485
|
-
InstanceMemory *mem =
|
|
481
|
+
InstanceMemory *mem = instance->memories[0];
|
|
486
482
|
CallData call(env, instance, &func, mem);
|
|
487
483
|
|
|
488
484
|
return call.Run(info);
|
|
@@ -558,7 +554,7 @@ static Napi::Value TranslateAsyncCall(const Napi::CallbackInfo &info)
|
|
|
558
554
|
return env.Null();
|
|
559
555
|
}
|
|
560
556
|
|
|
561
|
-
InstanceMemory *mem =
|
|
557
|
+
InstanceMemory *mem = AllocateAsyncMemory(instance);
|
|
562
558
|
AsyncCall *async = new AsyncCall(env, instance, func, mem, callback);
|
|
563
559
|
|
|
564
560
|
if (async->Prepare(info) && instance->debug) {
|
|
@@ -657,9 +653,13 @@ static Napi::Value FindLibraryFunction(const Napi::CallbackInfo &info, CallConve
|
|
|
657
653
|
if (!ParseClassicFunction(env, info[0u].As<Napi::String>(), info[1u], info[2u].As<Napi::Array>(), func))
|
|
658
654
|
return env.Null();
|
|
659
655
|
} else if (info.Length() >= 1) {
|
|
660
|
-
|
|
656
|
+
if (!info[0].IsString()) {
|
|
657
|
+
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for prototype, expected string", GetValueType(instance, info[0]));
|
|
658
|
+
return env.Null();
|
|
659
|
+
}
|
|
661
660
|
|
|
662
|
-
|
|
661
|
+
std::string proto = info[0u].As<Napi::String>();
|
|
662
|
+
if (!ParsePrototype(env, proto.c_str(), func))
|
|
663
663
|
return env.Null();
|
|
664
664
|
} else {
|
|
665
665
|
ThrowError<Napi::TypeError>(env, "Expected 1 or 3 arguments, not %1", info.Length());
|
|
@@ -930,8 +930,12 @@ void FunctionInfo::Unref() const
|
|
|
930
930
|
|
|
931
931
|
InstanceData::InstanceData()
|
|
932
932
|
{
|
|
933
|
-
|
|
934
|
-
|
|
933
|
+
InstanceMemory *mem = new InstanceMemory();
|
|
934
|
+
|
|
935
|
+
mem->stack = AllocateAndAlign16(&mem->mem_alloc, Mebibytes(2));
|
|
936
|
+
mem->heap = AllocateAndAlign16(&mem->mem_alloc, Mebibytes(4));
|
|
937
|
+
|
|
938
|
+
memories.Append(mem);
|
|
935
939
|
}
|
|
936
940
|
|
|
937
941
|
InstanceData::~InstanceData()
|
|
@@ -957,7 +961,6 @@ static void SetExports(Napi::Env env, Func func)
|
|
|
957
961
|
func("out", Napi::Function::New(env, MarkOut));
|
|
958
962
|
func("inout", Napi::Function::New(env, MarkInOut));
|
|
959
963
|
|
|
960
|
-
func("internal", Napi::Boolean::New(env, true));
|
|
961
964
|
#if defined(_WIN32)
|
|
962
965
|
func("extension", Napi::String::New(env, ".dll"));
|
|
963
966
|
#elif defined(__APPLE__)
|
|
@@ -1009,6 +1012,7 @@ static void InitInternal(v8::Local<v8::Object> target, v8::Local<v8::Value>,
|
|
|
1009
1012
|
FillRandomSafe(&instance->tag_lower, RG_SIZE(instance->tag_lower));
|
|
1010
1013
|
|
|
1011
1014
|
SetExports(env_napi, [&](const char *name, Napi::Value value) { SetValue(env, target, name, value); });
|
|
1015
|
+
SetValue(env, target, "internal", Napi::Boolean::New(env_cxx, true));
|
|
1012
1016
|
}
|
|
1013
1017
|
|
|
1014
1018
|
#else
|
|
@@ -1024,6 +1028,7 @@ static Napi::Object InitModule(Napi::Env env, Napi::Object exports)
|
|
|
1024
1028
|
FillRandomSafe(&instance->tag_lower, RG_SIZE(instance->tag_lower));
|
|
1025
1029
|
|
|
1026
1030
|
SetExports(env, [&](const char *name, Napi::Value value) { exports.Set(name, value); });
|
|
1031
|
+
exports.Set("internal", Napi::Boolean::New(env, false));
|
|
1027
1032
|
|
|
1028
1033
|
return exports;
|
|
1029
1034
|
}
|
package/src/ffi.hh
CHANGED
|
@@ -69,7 +69,8 @@ struct RecordMember;
|
|
|
69
69
|
struct TypeInfo {
|
|
70
70
|
enum class ArrayHint {
|
|
71
71
|
Array,
|
|
72
|
-
TypedArray
|
|
72
|
+
TypedArray,
|
|
73
|
+
String
|
|
73
74
|
};
|
|
74
75
|
|
|
75
76
|
const char *name;
|
|
@@ -189,7 +190,7 @@ struct InstanceData {
|
|
|
189
190
|
bool debug;
|
|
190
191
|
uint64_t tag_lower;
|
|
191
192
|
|
|
192
|
-
LocalArray<InstanceMemory *,
|
|
193
|
+
LocalArray<InstanceMemory *, 6> memories;
|
|
193
194
|
|
|
194
195
|
BlockAllocator str_alloc;
|
|
195
196
|
};
|
package/src/parser.cc
CHANGED
|
@@ -19,34 +19,27 @@
|
|
|
19
19
|
|
|
20
20
|
namespace RG {
|
|
21
21
|
|
|
22
|
-
bool PrototypeParser::Parse(
|
|
22
|
+
bool PrototypeParser::Parse(const char *str, FunctionInfo *out_func)
|
|
23
23
|
{
|
|
24
|
-
if (!proto.IsString()) {
|
|
25
|
-
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for prototype, expected string", GetValueType(instance, proto));
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
std::string hold = proto;
|
|
30
|
-
|
|
31
24
|
tokens.Clear();
|
|
32
25
|
offset = 0;
|
|
33
26
|
valid = true;
|
|
34
27
|
|
|
35
|
-
Tokenize(
|
|
28
|
+
Tokenize(str);
|
|
36
29
|
|
|
37
|
-
|
|
38
|
-
if (
|
|
30
|
+
out_func->ret.type = ParseType();
|
|
31
|
+
if (out_func->ret.type->primitive == PrimitiveKind::Array) {
|
|
39
32
|
MarkError("You are not allowed to directly return C arrays");
|
|
40
33
|
return false;
|
|
41
34
|
}
|
|
42
35
|
if (Match("__cdecl")) {
|
|
43
|
-
|
|
36
|
+
out_func->convention = CallConvention::Cdecl;
|
|
44
37
|
} else if (Match("__stdcall")) {
|
|
45
|
-
|
|
38
|
+
out_func->convention = CallConvention::Stdcall;
|
|
46
39
|
} else if (Match("__fastcall")) {
|
|
47
|
-
|
|
40
|
+
out_func->convention = CallConvention::Fastcall;
|
|
48
41
|
}
|
|
49
|
-
|
|
42
|
+
out_func->name = ParseIdentifier();
|
|
50
43
|
|
|
51
44
|
Consume("(");
|
|
52
45
|
if (offset < tokens.len && tokens[offset] != ")") {
|
|
@@ -54,7 +47,7 @@ bool PrototypeParser::Parse(Napi::String proto, FunctionInfo *func)
|
|
|
54
47
|
ParameterInfo param = {};
|
|
55
48
|
|
|
56
49
|
if (Match("...")) {
|
|
57
|
-
|
|
50
|
+
out_func->variadic = true;
|
|
58
51
|
break;
|
|
59
52
|
}
|
|
60
53
|
|
|
@@ -83,18 +76,18 @@ bool PrototypeParser::Parse(Napi::String proto, FunctionInfo *func)
|
|
|
83
76
|
|
|
84
77
|
offset += (offset < tokens.len && IsIdentifier(tokens[offset]));
|
|
85
78
|
|
|
86
|
-
if (
|
|
79
|
+
if (out_func->parameters.len >= MaxParameters) {
|
|
87
80
|
MarkError("Functions cannot have more than %1 parameters", MaxParameters);
|
|
88
81
|
return false;
|
|
89
82
|
}
|
|
90
|
-
if ((param.directions & 2) && ++
|
|
83
|
+
if ((param.directions & 2) && ++out_func->out_parameters >= MaxOutParameters) {
|
|
91
84
|
MarkError("Functions cannot have more than out %1 parameters", MaxOutParameters);
|
|
92
85
|
return false;
|
|
93
86
|
}
|
|
94
87
|
|
|
95
|
-
param.offset =
|
|
88
|
+
param.offset = out_func->parameters.len;
|
|
96
89
|
|
|
97
|
-
|
|
90
|
+
out_func->parameters.Append(param);
|
|
98
91
|
|
|
99
92
|
if (offset >= tokens.len || tokens[offset] != ",")
|
|
100
93
|
break;
|
|
@@ -125,6 +118,17 @@ void PrototypeParser::Tokenize(const char *str)
|
|
|
125
118
|
Span<const char> tok = MakeSpan(str + i, j - i);
|
|
126
119
|
tokens.Append(tok);
|
|
127
120
|
|
|
121
|
+
i = j - 1;
|
|
122
|
+
} else if (IsAsciiDigit(c)) {
|
|
123
|
+
Size j = i;
|
|
124
|
+
while (str[++j] && IsAsciiDigit(str[j]));
|
|
125
|
+
if (str[j] == '.') {
|
|
126
|
+
while (str[++j] && IsAsciiDigit(str[j]));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
Span<const char> tok = MakeSpan(str + i, j - i);
|
|
130
|
+
tokens.Append(tok);
|
|
131
|
+
|
|
128
132
|
i = j - 1;
|
|
129
133
|
} else if (c == '.' && str[i + 1] == '.' && str[i + 2] == '.') {
|
|
130
134
|
tokens.Append("...");
|
|
@@ -250,4 +254,10 @@ bool PrototypeParser::IsIdentifier(Span<const char> tok) const
|
|
|
250
254
|
return IsAsciiAlpha(tok[0]) || tok[0] == '_';
|
|
251
255
|
}
|
|
252
256
|
|
|
257
|
+
bool ParsePrototype(Napi::Env env, const char *str, FunctionInfo *out_func)
|
|
258
|
+
{
|
|
259
|
+
PrototypeParser parser(env);
|
|
260
|
+
return parser.Parse(str, out_func);
|
|
261
|
+
}
|
|
262
|
+
|
|
253
263
|
}
|
package/src/parser.hh
CHANGED
|
@@ -36,7 +36,7 @@ class PrototypeParser {
|
|
|
36
36
|
public:
|
|
37
37
|
PrototypeParser(Napi::Env env) : env(env), instance(env.GetInstanceData<InstanceData>()) {}
|
|
38
38
|
|
|
39
|
-
bool Parse(
|
|
39
|
+
bool Parse(const char *str, FunctionInfo *out_func);
|
|
40
40
|
|
|
41
41
|
private:
|
|
42
42
|
void Tokenize(const char *str);
|
|
@@ -60,4 +60,6 @@ private:
|
|
|
60
60
|
}
|
|
61
61
|
};
|
|
62
62
|
|
|
63
|
+
bool ParsePrototype(Napi::Env env, const char *str, FunctionInfo *out_func);
|
|
64
|
+
|
|
63
65
|
}
|
package/src/util.hh
CHANGED
|
@@ -78,7 +78,7 @@ T CopyNumber(const Napi::Value &value)
|
|
|
78
78
|
RG_ASSERT(value.IsNumber() || value.IsBigInt());
|
|
79
79
|
|
|
80
80
|
if (RG_LIKELY(value.IsNumber())) {
|
|
81
|
-
return (T)value.As<Napi::Number>();
|
|
81
|
+
return (T)value.As<Napi::Number>().DoubleValue();
|
|
82
82
|
} else if (value.IsBigInt()) {
|
|
83
83
|
Napi::BigInt bigint = value.As<Napi::BigInt>();
|
|
84
84
|
|
package/test/misc.c
CHANGED
|
@@ -104,6 +104,13 @@ typedef struct PackedBFG {
|
|
|
104
104
|
} PackedBFG;
|
|
105
105
|
#pragma pack(pop)
|
|
106
106
|
|
|
107
|
+
typedef struct FixedString {
|
|
108
|
+
char buf[64];
|
|
109
|
+
} FixedString;
|
|
110
|
+
typedef struct FixedWide {
|
|
111
|
+
int16_t buf[64];
|
|
112
|
+
} FixedWide;
|
|
113
|
+
|
|
107
114
|
EXPORT void FillPack1(int a, Pack1 *p)
|
|
108
115
|
{
|
|
109
116
|
p->a = a;
|
|
@@ -359,3 +366,13 @@ EXPORT const char16_t *Concat16(const char16_t *str1, const char16_t *str2)
|
|
|
359
366
|
|
|
360
367
|
return buf;
|
|
361
368
|
}
|
|
369
|
+
|
|
370
|
+
EXPORT FixedString ReturnFixedStr(FixedString str)
|
|
371
|
+
{
|
|
372
|
+
return str;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
EXPORT FixedWide ReturnFixedWide(FixedWide str)
|
|
376
|
+
{
|
|
377
|
+
return str;
|
|
378
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|