koffi 1.1.0-beta.6 → 1.1.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.
- package/README.md +29 -2
- package/build/qemu/1.1.0/koffi_darwin_x64.tar.gz +0 -0
- package/build/qemu/1.1.0/koffi_freebsd_arm64.tar.gz +0 -0
- package/build/qemu/1.1.0/koffi_freebsd_ia32.tar.gz +0 -0
- package/build/qemu/1.1.0/koffi_freebsd_x64.tar.gz +0 -0
- package/build/qemu/1.1.0/koffi_linux_arm.tar.gz +0 -0
- package/build/qemu/1.1.0/koffi_linux_arm64.tar.gz +0 -0
- package/build/qemu/1.1.0/koffi_linux_ia32.tar.gz +0 -0
- package/build/qemu/1.1.0/koffi_linux_x64.tar.gz +0 -0
- package/build/qemu/1.1.0/koffi_win32_ia32.tar.gz +0 -0
- package/build/qemu/1.1.0/koffi_win32_x64.tar.gz +0 -0
- package/package.json +1 -1
- package/src/call.cc +109 -338
- package/src/ffi.cc +31 -9
- package/src/ffi.hh +6 -0
- package/build/qemu/1.1.0-beta.6/koffi_darwin_x64.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.6/koffi_freebsd_arm64.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.6/koffi_freebsd_ia32.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.6/koffi_freebsd_x64.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.6/koffi_linux_arm.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.6/koffi_linux_arm64.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.6/koffi_linux_ia32.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.6/koffi_linux_x64.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.6/koffi_win32_ia32.tar.gz +0 -0
- package/build/qemu/1.1.0-beta.6/koffi_win32_x64.tar.gz +0 -0
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,33 @@ 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
|
+
Special rules apply for arrays of primitive integer and float types (uint32_t, double, etc...):
|
|
235
|
+
- 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
|
+
- 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')`
|
|
237
|
+
|
|
238
|
+
See the example below:
|
|
239
|
+
|
|
240
|
+
```js
|
|
241
|
+
const koffi = require('koffi');
|
|
242
|
+
|
|
243
|
+
// Those two structs are exactly the same, only the array conversion hint is different
|
|
244
|
+
const Foo1 = koffi.struct('Foo', {
|
|
245
|
+
i: 'int',
|
|
246
|
+
a16: koffi.array('int16_t', 8)
|
|
247
|
+
});
|
|
248
|
+
const Foo2 = koffi.struct('Foo', {
|
|
249
|
+
i: 'int',
|
|
250
|
+
a16: koffi.array('int16_t', 8, 'array')
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// Uses an hypothetical C function that just returns the struct passed as a parameter
|
|
254
|
+
const ReturnFoo1 = lib.func('Foo1 ReturnFoo(Foo1 p)');
|
|
255
|
+
const ReturnFoo2 = lib.func('Foo2 ReturnFoo(Foo2 p)');
|
|
256
|
+
|
|
257
|
+
console.log(ReturnFoo1({ i: 5, a16: [6, 8] })) // Prints { i: 5, a16: Int16Array(2) [6, 8] }
|
|
258
|
+
console.log(ReturnFoo2({ i: 5, a16: [6, 8] })) // Prints { i: 5, a16: [6, 8] }
|
|
259
|
+
```
|
|
260
|
+
|
|
234
261
|
## Variadic functions
|
|
235
262
|
|
|
236
263
|
Variadic functions are declared with an ellipsis as the last argument.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
package/src/call.cc
CHANGED
|
@@ -246,31 +246,38 @@ bool CallData::PushArray(const Napi::Object &obj, const TypeInfo *type, uint8_t
|
|
|
246
246
|
if (obj.IsArray()) {
|
|
247
247
|
Napi::Array array = obj.As<Napi::Array>();
|
|
248
248
|
|
|
249
|
-
if (array.Length() != len) {
|
|
249
|
+
if (RG_UNLIKELY(array.Length() != len)) {
|
|
250
250
|
ThrowError<Napi::Error>(env, "Expected array of length %1, got %2", len, array.Length());
|
|
251
251
|
return false;
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
#define PUSH_ARRAY(Check, Expected, GetCode) \
|
|
255
|
+
do { \
|
|
256
|
+
for (uint32_t i = 0; i < len; i++) { \
|
|
257
|
+
Napi::Value value = array[i]; \
|
|
258
|
+
\
|
|
259
|
+
int16_t align = std::max(type->ref->align, realign); \
|
|
260
|
+
dest = AlignUp(dest, align); \
|
|
261
|
+
\
|
|
262
|
+
if (RG_UNLIKELY(!(Check))) { \
|
|
263
|
+
ThrowError<Napi::TypeError>(env, "Unexpected value %1 in array, expected %2", GetValueType(instance, value), (Expected)); \
|
|
264
|
+
return false; \
|
|
265
|
+
} \
|
|
266
|
+
\
|
|
267
|
+
GetCode \
|
|
268
|
+
\
|
|
269
|
+
dest += type->ref->size; \
|
|
270
|
+
} \
|
|
271
|
+
} while (false)
|
|
272
|
+
|
|
254
273
|
switch (type->ref->primitive) {
|
|
255
274
|
case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
|
|
256
275
|
|
|
257
276
|
case PrimitiveKind::Bool: {
|
|
258
|
-
|
|
259
|
-
Napi::Value value = array[i];
|
|
260
|
-
|
|
261
|
-
int16_t align = std::max(type->ref->align, realign);
|
|
262
|
-
dest = AlignUp(dest, align);
|
|
263
|
-
|
|
264
|
-
if (RG_UNLIKELY(!value.IsBoolean())) {
|
|
265
|
-
ThrowError<Napi::TypeError>(env, "Unexpected value %1 in array, expected boolean", GetValueType(instance, value));
|
|
266
|
-
return false;
|
|
267
|
-
}
|
|
268
|
-
|
|
277
|
+
PUSH_ARRAY(value.IsBoolean(), "boolean", {
|
|
269
278
|
bool b = value.As<Napi::Boolean>();
|
|
270
279
|
*(bool *)dest = b;
|
|
271
|
-
|
|
272
|
-
dest += type->ref->size;
|
|
273
|
-
}
|
|
280
|
+
});
|
|
274
281
|
} break;
|
|
275
282
|
case PrimitiveKind::Int8:
|
|
276
283
|
case PrimitiveKind::UInt8:
|
|
@@ -280,161 +287,67 @@ bool CallData::PushArray(const Napi::Object &obj, const TypeInfo *type, uint8_t
|
|
|
280
287
|
case PrimitiveKind::UInt32:
|
|
281
288
|
case PrimitiveKind::Int64:
|
|
282
289
|
case PrimitiveKind::UInt64: {
|
|
283
|
-
|
|
284
|
-
Napi::Value value = array[i];
|
|
285
|
-
|
|
286
|
-
int16_t align = std::max(type->ref->align, realign);
|
|
287
|
-
dest = AlignUp(dest, align);
|
|
288
|
-
|
|
289
|
-
if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
|
|
290
|
-
ThrowError<Napi::TypeError>(env, "Unexpected value %1 in array, expected number", GetValueType(instance, value));
|
|
291
|
-
return false;
|
|
292
|
-
}
|
|
293
|
-
|
|
290
|
+
PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
|
|
294
291
|
int64_t v = CopyNumber<int64_t>(value);
|
|
295
292
|
memcpy(dest, &v, type->ref->size); // Little Endian
|
|
296
|
-
|
|
297
|
-
dest += type->ref->size;
|
|
298
|
-
}
|
|
293
|
+
});
|
|
299
294
|
} break;
|
|
300
295
|
case PrimitiveKind::String: {
|
|
301
|
-
|
|
302
|
-
Napi::Value value = array[i];
|
|
303
|
-
|
|
304
|
-
int16_t align = std::max(type->ref->align, realign);
|
|
305
|
-
dest = AlignUp(dest, align);
|
|
306
|
-
|
|
307
|
-
if (RG_UNLIKELY(!value.IsString())) {
|
|
308
|
-
ThrowError<Napi::TypeError>(env, "Unexpected value %1 in array, expected string", GetValueType(instance, value));
|
|
309
|
-
return false;
|
|
310
|
-
}
|
|
311
|
-
|
|
296
|
+
PUSH_ARRAY(value.IsString(), "string", {
|
|
312
297
|
const char *str = PushString(value);
|
|
313
298
|
if (RG_UNLIKELY(!str))
|
|
314
299
|
return false;
|
|
315
300
|
*(const char **)dest = str;
|
|
316
|
-
|
|
317
|
-
dest += type->ref->size;
|
|
318
|
-
}
|
|
301
|
+
});
|
|
319
302
|
} break;
|
|
320
303
|
case PrimitiveKind::String16: {
|
|
321
|
-
|
|
322
|
-
Napi::Value value = array[i];
|
|
323
|
-
|
|
324
|
-
int16_t align = std::max(type->ref->align, realign);
|
|
325
|
-
dest = AlignUp(dest, align);
|
|
326
|
-
|
|
327
|
-
if (RG_UNLIKELY(!value.IsString())) {
|
|
328
|
-
ThrowError<Napi::TypeError>(env, "Unexpected value %1 in array, expected string", GetValueType(instance, value));
|
|
329
|
-
return false;
|
|
330
|
-
}
|
|
331
|
-
|
|
304
|
+
PUSH_ARRAY(value.IsString(), "string", {
|
|
332
305
|
const char16_t *str16 = PushString16(value);
|
|
333
306
|
if (RG_UNLIKELY(!str16))
|
|
334
307
|
return false;
|
|
335
308
|
*(const char16_t **)dest = str16;
|
|
336
|
-
|
|
337
|
-
dest += type->ref->size;
|
|
338
|
-
}
|
|
309
|
+
});
|
|
339
310
|
} break;
|
|
340
311
|
case PrimitiveKind::Pointer: {
|
|
341
|
-
|
|
342
|
-
Napi::Value value = array[i];
|
|
343
|
-
|
|
344
|
-
int16_t align = std::max(type->ref->align, realign);
|
|
345
|
-
dest = AlignUp(dest, align);
|
|
346
|
-
|
|
347
|
-
if (RG_UNLIKELY(!CheckValueTag(instance, value, type->ref))) {
|
|
348
|
-
ThrowError<Napi::TypeError>(env, "Unexpected value %1 in array, expected %2", GetValueType(instance, value), type->ref->name);
|
|
349
|
-
return false;
|
|
350
|
-
}
|
|
351
|
-
|
|
312
|
+
PUSH_ARRAY(CheckValueTag(instance, value, type->ref), type->ref->name, {
|
|
352
313
|
Napi::External external = value.As<Napi::External<void>>();
|
|
353
314
|
*(void **)dest = external.Data();
|
|
354
|
-
|
|
355
|
-
dest += type->ref->size;
|
|
356
|
-
}
|
|
315
|
+
});
|
|
357
316
|
} break;
|
|
358
317
|
case PrimitiveKind::Record: {
|
|
359
|
-
|
|
360
|
-
Napi::Value value = array[i];
|
|
361
|
-
|
|
362
|
-
int16_t align = std::max(type->ref->align, realign);
|
|
363
|
-
dest = AlignUp(dest, align);
|
|
364
|
-
|
|
365
|
-
if (RG_UNLIKELY(!IsObject(value))) {
|
|
366
|
-
ThrowError<Napi::TypeError>(env, "Unexpected value %1 in array, expected object", GetValueType(instance, value));
|
|
367
|
-
return false;
|
|
368
|
-
}
|
|
369
|
-
|
|
318
|
+
PUSH_ARRAY(IsObject(value), "object", {
|
|
370
319
|
Napi::Object obj = value.As<Napi::Object>();
|
|
371
320
|
if (!PushObject(obj, type->ref, dest, realign))
|
|
372
321
|
return false;
|
|
373
|
-
|
|
374
|
-
dest += type->ref->size;
|
|
375
|
-
}
|
|
322
|
+
});
|
|
376
323
|
} break;
|
|
377
324
|
case PrimitiveKind::Array: {
|
|
378
|
-
|
|
379
|
-
Napi::
|
|
380
|
-
|
|
381
|
-
int16_t align = std::max(type->ref->align, realign);
|
|
382
|
-
dest = AlignUp(dest, align);
|
|
383
|
-
|
|
384
|
-
if (RG_UNLIKELY(!value.IsArray() && !value.IsTypedArray())) {
|
|
385
|
-
ThrowError<Napi::TypeError>(env, "Unexpected value %1 in array, expected array", GetValueType(instance, value));
|
|
386
|
-
return false;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
Napi::Array array = value.As<Napi::Array>();
|
|
325
|
+
PUSH_ARRAY(value.IsArray() || value.IsTypedArray(), "array", {
|
|
326
|
+
Napi::Object array = value.As<Napi::Array>();
|
|
390
327
|
if (!PushArray(array, type->ref, dest, realign))
|
|
391
328
|
return false;
|
|
392
|
-
|
|
393
|
-
dest += type->ref->size;
|
|
394
|
-
}
|
|
329
|
+
});
|
|
395
330
|
} break;
|
|
396
331
|
case PrimitiveKind::Float32: {
|
|
397
|
-
|
|
398
|
-
Napi::Value value = array[i];
|
|
399
|
-
|
|
400
|
-
int16_t align = std::max(type->ref->align, realign);
|
|
401
|
-
dest = AlignUp(dest, align);
|
|
402
|
-
|
|
403
|
-
if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
|
|
404
|
-
ThrowError<Napi::TypeError>(env, "Unexpected value %1 in array, expected number", GetValueType(instance, value));
|
|
405
|
-
return false;
|
|
406
|
-
}
|
|
407
|
-
|
|
332
|
+
PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
|
|
408
333
|
float f = CopyNumber<float>(value);
|
|
409
334
|
*(float *)dest = f;
|
|
410
|
-
|
|
411
|
-
dest += type->ref->size;
|
|
412
|
-
}
|
|
335
|
+
});
|
|
413
336
|
} break;
|
|
414
337
|
case PrimitiveKind::Float64: {
|
|
415
|
-
|
|
416
|
-
Napi::Value value = array[i];
|
|
417
|
-
|
|
418
|
-
int16_t align = std::max(type->ref->align, realign);
|
|
419
|
-
dest = AlignUp(dest, align);
|
|
420
|
-
|
|
421
|
-
if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
|
|
422
|
-
ThrowError<Napi::TypeError>(env, "Unexpected value %1 in array, expected number", GetValueType(instance, value));
|
|
423
|
-
return false;
|
|
424
|
-
}
|
|
425
|
-
|
|
338
|
+
PUSH_ARRAY(value.IsNumber() || value.IsBigInt(), "number", {
|
|
426
339
|
double d = CopyNumber<double>(value);
|
|
427
340
|
*(double *)dest = d;
|
|
428
|
-
|
|
429
|
-
dest += type->ref->size;
|
|
430
|
-
}
|
|
341
|
+
});
|
|
431
342
|
} break;
|
|
432
343
|
}
|
|
344
|
+
|
|
345
|
+
#undef PUSH_ARRAY
|
|
433
346
|
} else {
|
|
434
347
|
Napi::TypedArray array = obj.As<Napi::TypedArray>();
|
|
435
348
|
const uint8_t *buf = (const uint8_t *)array.ArrayBuffer().Data();
|
|
436
349
|
|
|
437
|
-
if (array.ElementLength() != len) {
|
|
350
|
+
if (RG_UNLIKELY(array.ElementLength() != len)) {
|
|
438
351
|
ThrowError<Napi::Error>(env, "Expected array of length %1, got %2", len, array.ElementLength());
|
|
439
352
|
return false;
|
|
440
353
|
}
|
|
@@ -452,7 +365,7 @@ bool CallData::PushArray(const Napi::Object &obj, const TypeInfo *type, uint8_t
|
|
|
452
365
|
|
|
453
366
|
default: { match = false; } break;
|
|
454
367
|
}
|
|
455
|
-
if (!match) {
|
|
368
|
+
if (RG_UNLIKELY(!match)) {
|
|
456
369
|
ThrowError<Napi::TypeError>(env, "TypedArray is not approriate for %1 array", type->ref->name);
|
|
457
370
|
return false;
|
|
458
371
|
}
|
|
@@ -570,255 +483,113 @@ Napi::Object CallData::PopArray(const uint8_t *src, const TypeInfo *type, int16_
|
|
|
570
483
|
|
|
571
484
|
uint32_t len = type->size / type->ref->size;
|
|
572
485
|
|
|
486
|
+
#define POP_ARRAY(SetCode) \
|
|
487
|
+
do { \
|
|
488
|
+
Napi::Array array = Napi::Array::New(env); \
|
|
489
|
+
\
|
|
490
|
+
for (uint32_t i = 0; i < len; i++) { \
|
|
491
|
+
int16_t align = std::max(realign, type->ref->align); \
|
|
492
|
+
src = AlignUp(src, align); \
|
|
493
|
+
\
|
|
494
|
+
SetCode \
|
|
495
|
+
\
|
|
496
|
+
src += type->ref->size; \
|
|
497
|
+
} \
|
|
498
|
+
\
|
|
499
|
+
return array; \
|
|
500
|
+
} while (false)
|
|
501
|
+
#define POP_NUMBER_ARRAY(TypedArrayType, CType) \
|
|
502
|
+
do { \
|
|
503
|
+
if (type->hint == TypeInfo::ArrayHint::TypedArray) { \
|
|
504
|
+
Napi::TypedArrayType array = Napi::TypedArrayType::New(env, len); \
|
|
505
|
+
\
|
|
506
|
+
for (uint32_t i = 0; i < len; i++) { \
|
|
507
|
+
int16_t align = std::max(realign, type->ref->align); \
|
|
508
|
+
src = AlignUp(src, align); \
|
|
509
|
+
\
|
|
510
|
+
CType f = *(CType *)src; \
|
|
511
|
+
array[i] = f; \
|
|
512
|
+
\
|
|
513
|
+
src += type->ref->size; \
|
|
514
|
+
} \
|
|
515
|
+
\
|
|
516
|
+
return array; \
|
|
517
|
+
} else { \
|
|
518
|
+
POP_ARRAY({ \
|
|
519
|
+
double d = (double)*(CType *)src; \
|
|
520
|
+
array.Set(i, Napi::Number::New(env, d)); \
|
|
521
|
+
}); \
|
|
522
|
+
} \
|
|
523
|
+
} while (false)
|
|
524
|
+
|
|
573
525
|
switch (type->ref->primitive) {
|
|
574
526
|
case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
|
|
575
527
|
|
|
576
528
|
case PrimitiveKind::Bool: {
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
for (uint32_t i = 0; i < len; i++) {
|
|
580
|
-
int16_t align = std::max(realign, type->ref->align);
|
|
581
|
-
src = AlignUp(src, align);
|
|
582
|
-
|
|
529
|
+
POP_ARRAY({
|
|
583
530
|
bool b = *(bool *)src;
|
|
584
531
|
array.Set(i, Napi::Boolean::New(env, b));
|
|
585
|
-
|
|
586
|
-
src += type->ref->size;
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
return array;
|
|
590
|
-
} break;
|
|
591
|
-
case PrimitiveKind::Int8: {
|
|
592
|
-
Napi::Int8Array array = Napi::Int8Array::New(env, len);
|
|
593
|
-
|
|
594
|
-
for (uint32_t i = 0; i < len; i++) {
|
|
595
|
-
int16_t align = std::max(realign, type->ref->align);
|
|
596
|
-
src = AlignUp(src, align);
|
|
597
|
-
|
|
598
|
-
int8_t v = *(int8_t *)src;
|
|
599
|
-
array[i] = v;
|
|
600
|
-
|
|
601
|
-
src += type->ref->size;
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
return array;
|
|
605
|
-
} break;
|
|
606
|
-
case PrimitiveKind::UInt8: {
|
|
607
|
-
Napi::Uint8Array array = Napi::Uint8Array::New(env, len);
|
|
608
|
-
|
|
609
|
-
for (uint32_t i = 0; i < len; i++) {
|
|
610
|
-
int16_t align = std::max(realign, type->ref->align);
|
|
611
|
-
src = AlignUp(src, align);
|
|
612
|
-
|
|
613
|
-
uint8_t v = *(uint8_t *)src;
|
|
614
|
-
array[i] = v;
|
|
615
|
-
|
|
616
|
-
src += type->ref->size;
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
return array;
|
|
620
|
-
} break;
|
|
621
|
-
case PrimitiveKind::Int16: {
|
|
622
|
-
Napi::Int16Array array = Napi::Int16Array::New(env, len);
|
|
623
|
-
|
|
624
|
-
for (uint32_t i = 0; i < len; i++) {
|
|
625
|
-
int16_t align = std::max(realign, type->ref->align);
|
|
626
|
-
src = AlignUp(src, align);
|
|
627
|
-
|
|
628
|
-
int16_t v = *(int16_t *)src;
|
|
629
|
-
array[i] = v;
|
|
630
|
-
|
|
631
|
-
src += type->ref->size;
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
return array;
|
|
635
|
-
} break;
|
|
636
|
-
case PrimitiveKind::UInt16: {
|
|
637
|
-
Napi::Uint16Array array = Napi::Uint16Array::New(env, len);
|
|
638
|
-
|
|
639
|
-
for (uint32_t i = 0; i < len; i++) {
|
|
640
|
-
int16_t align = std::max(realign, type->ref->align);
|
|
641
|
-
src = AlignUp(src, align);
|
|
642
|
-
|
|
643
|
-
uint16_t v = *(uint16_t *)src;
|
|
644
|
-
array[i] = v;
|
|
645
|
-
|
|
646
|
-
src += type->ref->size;
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
return array;
|
|
650
|
-
} break;
|
|
651
|
-
case PrimitiveKind::Int32: {
|
|
652
|
-
Napi::Int32Array array = Napi::Int32Array::New(env, len);
|
|
653
|
-
|
|
654
|
-
for (uint32_t i = 0; i < len; i++) {
|
|
655
|
-
int16_t align = std::max(realign, type->ref->align);
|
|
656
|
-
src = AlignUp(src, align);
|
|
657
|
-
|
|
658
|
-
int32_t v = *(int32_t *)src;
|
|
659
|
-
array[i] = v;
|
|
660
|
-
|
|
661
|
-
src += type->ref->size;
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
return array;
|
|
665
|
-
} break;
|
|
666
|
-
case PrimitiveKind::UInt32: {
|
|
667
|
-
Napi::Uint32Array array = Napi::Uint32Array::New(env, len);
|
|
668
|
-
|
|
669
|
-
for (uint32_t i = 0; i < len; i++) {
|
|
670
|
-
int16_t align = std::max(realign, type->ref->align);
|
|
671
|
-
src = AlignUp(src, align);
|
|
672
|
-
|
|
673
|
-
uint32_t v = *(uint32_t *)src;
|
|
674
|
-
array[i] = v;
|
|
675
|
-
|
|
676
|
-
src += type->ref->size;
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
return array;
|
|
532
|
+
});
|
|
680
533
|
} break;
|
|
534
|
+
case PrimitiveKind::Int8: { POP_NUMBER_ARRAY(Int8Array, int8_t); } break;
|
|
535
|
+
case PrimitiveKind::UInt8: { POP_NUMBER_ARRAY(Uint8Array, uint8_t); } break;
|
|
536
|
+
case PrimitiveKind::Int16: { POP_NUMBER_ARRAY(Int16Array, int16_t); } break;
|
|
537
|
+
case PrimitiveKind::UInt16: { POP_NUMBER_ARRAY(Uint16Array, uint16_t); } break;
|
|
538
|
+
case PrimitiveKind::Int32: { POP_NUMBER_ARRAY(Int32Array, int32_t); } break;
|
|
539
|
+
case PrimitiveKind::UInt32: { POP_NUMBER_ARRAY(Uint32Array, uint32_t); } break;
|
|
681
540
|
case PrimitiveKind::Int64: {
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
for (uint32_t i = 0; i < len; i++) {
|
|
685
|
-
int16_t align = std::max(realign, type->ref->align);
|
|
686
|
-
src = AlignUp(src, align);
|
|
687
|
-
|
|
541
|
+
POP_ARRAY({
|
|
688
542
|
int64_t v = *(int64_t *)src;
|
|
689
543
|
array.Set(i, Napi::BigInt::New(env, v));
|
|
690
|
-
|
|
691
|
-
src += type->ref->size;
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
return array;
|
|
544
|
+
});
|
|
695
545
|
} break;
|
|
696
546
|
case PrimitiveKind::UInt64: {
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
for (uint32_t i = 0; i < len; i++) {
|
|
700
|
-
int16_t align = std::max(realign, type->ref->align);
|
|
701
|
-
src = AlignUp(src, align);
|
|
702
|
-
|
|
547
|
+
POP_ARRAY({
|
|
703
548
|
uint64_t v = *(uint64_t *)src;
|
|
704
549
|
array.Set(i, Napi::BigInt::New(env, v));
|
|
705
|
-
|
|
706
|
-
src += type->ref->size;
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
return array;
|
|
550
|
+
});
|
|
710
551
|
} break;
|
|
711
552
|
case PrimitiveKind::String: {
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
for (uint32_t i = 0; i < len; i++) {
|
|
715
|
-
int16_t align = std::max(realign, type->ref->align);
|
|
716
|
-
src = AlignUp(src, align);
|
|
717
|
-
|
|
553
|
+
POP_ARRAY({
|
|
718
554
|
const char *str = *(const char **)src;
|
|
719
555
|
array.Set(i, Napi::String::New(env, str));
|
|
720
|
-
|
|
721
|
-
src += type->ref->size;
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
return array;
|
|
556
|
+
});
|
|
725
557
|
} break;
|
|
726
558
|
case PrimitiveKind::String16: {
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
for (uint32_t i = 0; i < len; i++) {
|
|
730
|
-
int16_t align = std::max(realign, type->ref->align);
|
|
731
|
-
src = AlignUp(src, align);
|
|
732
|
-
|
|
559
|
+
POP_ARRAY({
|
|
733
560
|
const char16_t *str16 = *(const char16_t **)src;
|
|
734
561
|
array.Set(i, Napi::String::New(env, str16));
|
|
735
|
-
|
|
736
|
-
src += type->ref->size;
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
return array;
|
|
562
|
+
});
|
|
740
563
|
} break;
|
|
741
564
|
case PrimitiveKind::Pointer: {
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
for (uint32_t i = 0; i < len; i++) {
|
|
745
|
-
int16_t align = std::max(realign, type->ref->align);
|
|
746
|
-
src = AlignUp(src, align);
|
|
747
|
-
|
|
565
|
+
POP_ARRAY({
|
|
748
566
|
void *ptr2 = *(void **)src;
|
|
749
567
|
|
|
750
568
|
Napi::External<void> external = Napi::External<void>::New(env, ptr2);
|
|
751
569
|
SetValueTag(instance, external, type->ref);
|
|
752
570
|
|
|
753
571
|
array.Set(i, external);
|
|
754
|
-
|
|
755
|
-
src += type->ref->size;
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
return array;
|
|
572
|
+
});
|
|
759
573
|
} break;
|
|
760
574
|
case PrimitiveKind::Record: {
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
for (uint32_t i = 0; i < len; i++) {
|
|
764
|
-
int16_t align = std::max(realign, type->ref->align);
|
|
765
|
-
src = AlignUp(src, align);
|
|
766
|
-
|
|
575
|
+
POP_ARRAY({
|
|
767
576
|
Napi::Object obj = PopObject(src, type->ref, realign);
|
|
768
577
|
array.Set(i, obj);
|
|
769
|
-
|
|
770
|
-
src += type->ref->size;
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
return array;
|
|
578
|
+
});
|
|
774
579
|
} break;
|
|
775
580
|
case PrimitiveKind::Array: {
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
for (uint32_t i = 0; i < len; i++) {
|
|
779
|
-
int16_t align = std::max(realign, type->ref->align);
|
|
780
|
-
src = AlignUp(src, align);
|
|
781
|
-
|
|
581
|
+
POP_ARRAY({
|
|
782
582
|
Napi::Object obj = PopArray(src, type->ref, realign);
|
|
783
583
|
array.Set(i, obj);
|
|
784
|
-
|
|
785
|
-
src += type->ref->size;
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
return array;
|
|
789
|
-
} break;
|
|
790
|
-
case PrimitiveKind::Float32: {
|
|
791
|
-
Napi::Float32Array array = Napi::Float32Array::New(env, len);
|
|
792
|
-
|
|
793
|
-
for (uint32_t i = 0; i < len; i++) {
|
|
794
|
-
int16_t align = std::max(realign, type->ref->align);
|
|
795
|
-
src = AlignUp(src, align);
|
|
796
|
-
|
|
797
|
-
float f = *(float *)src;
|
|
798
|
-
array[i] = f;
|
|
799
|
-
|
|
800
|
-
src += type->ref->size;
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
return array;
|
|
804
|
-
} break;
|
|
805
|
-
case PrimitiveKind::Float64: {
|
|
806
|
-
Napi::Float64Array array = Napi::Float64Array::New(env, len);
|
|
807
|
-
|
|
808
|
-
for (uint32_t i = 0; i < len; i++) {
|
|
809
|
-
int16_t align = std::max(realign, type->ref->align);
|
|
810
|
-
src = AlignUp(src, align);
|
|
811
|
-
|
|
812
|
-
double d = *(double *)src;
|
|
813
|
-
array[i] = d;
|
|
814
|
-
|
|
815
|
-
src += type->ref->size;
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
return array;
|
|
584
|
+
});
|
|
819
585
|
} break;
|
|
586
|
+
case PrimitiveKind::Float32: { POP_NUMBER_ARRAY(Float32Array, float); } break;
|
|
587
|
+
case PrimitiveKind::Float64: { POP_NUMBER_ARRAY(Float64Array, double); } break;
|
|
820
588
|
}
|
|
821
589
|
|
|
590
|
+
#undef POP_NUMBER_ARRAY
|
|
591
|
+
#undef POP_ARRAY
|
|
592
|
+
|
|
822
593
|
RG_UNREACHABLE();
|
|
823
594
|
}
|
|
824
595
|
|
package/src/ffi.cc
CHANGED
|
@@ -261,6 +261,27 @@ static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
|
|
|
261
261
|
return env.Null();
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
+
TypeInfo::ArrayHint hint;
|
|
265
|
+
if (info.Length() >= 3 && !IsNullOrUndefined(info[2])) {
|
|
266
|
+
if (!info[2].IsString()) {
|
|
267
|
+
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for hint, expected string", GetValueType(instance, info[2]));
|
|
268
|
+
return env.Null();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
std::string to = info[2].As<Napi::String>();
|
|
272
|
+
|
|
273
|
+
if (to == "typed") {
|
|
274
|
+
hint = TypeInfo::ArrayHint::TypedArray;
|
|
275
|
+
} else if (to == "array") {
|
|
276
|
+
hint = TypeInfo::ArrayHint::Array;
|
|
277
|
+
} else {
|
|
278
|
+
ThrowError<Napi::Error>(env, "Array conversion hint must be 'typed' or 'array'");
|
|
279
|
+
return env.Null();
|
|
280
|
+
}
|
|
281
|
+
} else {
|
|
282
|
+
hint = TypeInfo::ArrayHint::TypedArray;
|
|
283
|
+
}
|
|
284
|
+
|
|
264
285
|
const TypeInfo *ref = ResolveType(instance, info[0]);
|
|
265
286
|
int64_t len = (uint16_t)info[1].As<Napi::Number>().Int64Value();
|
|
266
287
|
|
|
@@ -284,6 +305,7 @@ static Napi::Value CreateArrayType(const Napi::CallbackInfo &info)
|
|
|
284
305
|
type->align = ref->align;
|
|
285
306
|
type->size = (int16_t)(len * ref->size);
|
|
286
307
|
type->ref = ref;
|
|
308
|
+
type->hint = hint;
|
|
287
309
|
|
|
288
310
|
// If the insert succeeds, we cannot fail anymore
|
|
289
311
|
if (!instance->types_map.TrySet(type).second) {
|
|
@@ -395,7 +417,7 @@ static Napi::Value TranslateNormalCall(const Napi::CallbackInfo &info)
|
|
|
395
417
|
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
396
418
|
FunctionInfo *func = (FunctionInfo *)info.Data();
|
|
397
419
|
|
|
398
|
-
if (info.Length() < (uint32_t)func->parameters.len) {
|
|
420
|
+
if (RG_UNLIKELY(info.Length() < (uint32_t)func->parameters.len)) {
|
|
399
421
|
ThrowError<Napi::TypeError>(env, "Expected %1 arguments, got %2", func->parameters.len, info.Length());
|
|
400
422
|
return env.Null();
|
|
401
423
|
}
|
|
@@ -421,11 +443,11 @@ static Napi::Value TranslateVariadicCall(const Napi::CallbackInfo &info)
|
|
|
421
443
|
func.parameters.Leak();
|
|
422
444
|
};
|
|
423
445
|
|
|
424
|
-
if (info.Length() < (uint32_t)func.parameters.len) {
|
|
446
|
+
if (RG_UNLIKELY(info.Length() < (uint32_t)func.parameters.len)) {
|
|
425
447
|
ThrowError<Napi::TypeError>(env, "Expected %1 arguments or more, got %2", func.parameters.len, info.Length());
|
|
426
448
|
return env.Null();
|
|
427
449
|
}
|
|
428
|
-
if ((info.Length() - func.parameters.len) % 2) {
|
|
450
|
+
if (RG_UNLIKELY((info.Length() - func.parameters.len) % 2)) {
|
|
429
451
|
ThrowError<Napi::Error>(env, "Missing value argument for variadic call");
|
|
430
452
|
return env.Null();
|
|
431
453
|
}
|
|
@@ -434,19 +456,19 @@ static Napi::Value TranslateVariadicCall(const Napi::CallbackInfo &info)
|
|
|
434
456
|
ParameterInfo param = {};
|
|
435
457
|
|
|
436
458
|
param.type = ResolveType(instance, info[i], ¶m.directions);
|
|
437
|
-
if (!param.type)
|
|
459
|
+
if (RG_UNLIKELY(!param.type))
|
|
438
460
|
return env.Null();
|
|
439
|
-
if (param.type->primitive == PrimitiveKind::Void ||
|
|
440
|
-
|
|
461
|
+
if (RG_UNLIKELY(param.type->primitive == PrimitiveKind::Void ||
|
|
462
|
+
param.type->primitive == PrimitiveKind::Array)) {
|
|
441
463
|
ThrowError<Napi::TypeError>(env, "Type %1 cannot be used as a parameter", PrimitiveKindNames[(int)param.type->primitive]);
|
|
442
464
|
return env.Null();
|
|
443
465
|
}
|
|
444
466
|
|
|
445
|
-
if (func.parameters.len >= MaxParameters) {
|
|
467
|
+
if (RG_UNLIKELY(func.parameters.len >= MaxParameters)) {
|
|
446
468
|
ThrowError<Napi::TypeError>(env, "Functions cannot have more than %1 parameters", MaxParameters);
|
|
447
469
|
return env.Null();
|
|
448
470
|
}
|
|
449
|
-
if ((param.directions & 2) && ++func.out_parameters >= MaxOutParameters) {
|
|
471
|
+
if (RG_UNLIKELY((param.directions & 2) && ++func.out_parameters >= MaxOutParameters)) {
|
|
450
472
|
ThrowError<Napi::TypeError>(env, "Functions cannot have more than out %1 parameters", MaxOutParameters);
|
|
451
473
|
return env.Null();
|
|
452
474
|
}
|
|
@@ -457,7 +479,7 @@ static Napi::Value TranslateVariadicCall(const Napi::CallbackInfo &info)
|
|
|
457
479
|
func.parameters.Append(param);
|
|
458
480
|
}
|
|
459
481
|
|
|
460
|
-
if (!AnalyseFunction(instance, &func))
|
|
482
|
+
if (RG_UNLIKELY(!AnalyseFunction(instance, &func)))
|
|
461
483
|
return env.Null();
|
|
462
484
|
|
|
463
485
|
InstanceMemory *mem = AllocateCallMemory(instance);
|
package/src/ffi.hh
CHANGED
|
@@ -67,6 +67,11 @@ struct TypeInfo;
|
|
|
67
67
|
struct RecordMember;
|
|
68
68
|
|
|
69
69
|
struct TypeInfo {
|
|
70
|
+
enum class ArrayHint {
|
|
71
|
+
Array,
|
|
72
|
+
TypedArray
|
|
73
|
+
};
|
|
74
|
+
|
|
70
75
|
const char *name;
|
|
71
76
|
napi_type_tag tag;
|
|
72
77
|
|
|
@@ -78,6 +83,7 @@ struct TypeInfo {
|
|
|
78
83
|
|
|
79
84
|
HeapArray<RecordMember> members; // Record only
|
|
80
85
|
const TypeInfo *ref; // Pointer or array
|
|
86
|
+
ArrayHint hint; // Array only
|
|
81
87
|
|
|
82
88
|
RG_HASHTABLE_HANDLER(TypeInfo, name);
|
|
83
89
|
};
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|