koffi 1.1.0-beta.7 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
- After the release of version 1.0, the following features are planned:
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,11 +231,13 @@ 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 (int, int8_t, 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 `int8_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('int8_t', 6, 'array')`
234
+ ### JS typed arrays
237
235
 
238
- Example below:
236
+ Special rules apply for arrays of primitive integer and float types (uint32_t, double, etc...):
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)
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')`
239
+
240
+ See the example below:
239
241
 
240
242
  ```js
241
243
  const koffi = require('koffi');
@@ -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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koffi",
3
- "version": "1.1.0-beta.7",
3
+ "version": "1.1.1",
4
4
  "description": "Fast and simple FFI (foreign function interface) for Node.js",
5
5
  "keywords": [
6
6
  "foreign",
package/qemu/qemu.js CHANGED
@@ -395,7 +395,7 @@ async function pack() {
395
395
  }));
396
396
 
397
397
  let status = copied ? chalk.bold.green('[ok]') : chalk.bold.red('[error]');
398
- log(machine, 'Download', status);
398
+ log(machine, 'Pack', status);
399
399
  }));
400
400
  }
401
401
 
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
- case PrimitiveKind::UInt8:
147
- case PrimitiveKind::Int16:
148
- case PrimitiveKind::UInt16:
149
- case PrimitiveKind::Int32:
150
- case PrimitiveKind::UInt32:
151
- case PrimitiveKind::Int64:
152
- case PrimitiveKind::UInt64: {
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
- memcpy(dest, &v, member.type->size); // Little Endian
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())) {
@@ -201,7 +257,7 @@ bool CallData::PushObject(const Napi::Object &obj, const TypeInfo *type, uint8_t
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,15 +292,15 @@ 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::Object &obj, const TypeInfo *type, uint8_t *dest, int16_t realign)
295
+ bool CallData::PushArray(const Napi::Value &value, const TypeInfo *type, uint8_t *dest, int16_t realign)
240
296
  {
241
- RG_ASSERT(obj.IsArray() || obj.IsTypedArray());
297
+ RG_ASSERT(value.IsArray() || value.IsTypedArray() || value.IsString());
242
298
  RG_ASSERT(type->primitive == PrimitiveKind::Array);
243
299
 
244
300
  uint32_t len = type->size / type->ref->size;
245
301
 
246
- if (obj.IsArray()) {
247
- Napi::Array array = obj.As<Napi::Array>();
302
+ if (value.IsArray()) {
303
+ Napi::Array array = value.As<Napi::Array>();
248
304
 
249
305
  if (RG_UNLIKELY(array.Length() != len)) {
250
306
  ThrowError<Napi::Error>(env, "Expected array of length %1, got %2", len, array.Length());
@@ -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
- case PrimitiveKind::UInt8:
284
- case PrimitiveKind::Int16:
285
- case PrimitiveKind::UInt16:
286
- case PrimitiveKind::Int32:
287
- case PrimitiveKind::UInt32:
288
- case PrimitiveKind::Int64:
289
- case PrimitiveKind::UInt64: {
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
- memcpy(dest, &v, type->ref->size); // Little Endian
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: {
@@ -322,7 +413,7 @@ bool CallData::PushArray(const Napi::Object &obj, const TypeInfo *type, uint8_t
322
413
  });
323
414
  } break;
324
415
  case PrimitiveKind::Array: {
325
- PUSH_ARRAY(value.IsArray() || value.IsTypedArray(), "array", {
416
+ PUSH_ARRAY(value.IsArray() || value.IsTypedArray() || value.IsString(), "array", {
326
417
  Napi::Object array = value.As<Napi::Array>();
327
418
  if (!PushArray(array, type->ref, dest, realign))
328
419
  return false;
@@ -343,8 +434,8 @@ bool CallData::PushArray(const Napi::Object &obj, const TypeInfo *type, uint8_t
343
434
  }
344
435
 
345
436
  #undef PUSH_ARRAY
346
- } else {
347
- Napi::TypedArray array = obj.As<Napi::TypedArray>();
437
+ } else if (value.IsTypedArray()) {
438
+ Napi::TypedArray array = value.As<Napi::TypedArray>();
348
439
  const uint8_t *buf = (const uint8_t *)array.ArrayBuffer().Data();
349
440
 
350
441
  if (RG_UNLIKELY(array.ElementLength() != len)) {
@@ -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 (value.IsString()) {
473
+ size_t len = 0;
474
+
475
+ if (type->ref->primitive == PrimitiveKind::Int8 || type->ref->primitive == PrimitiveKind::UInt8) {
476
+ napi_status status = napi_get_value_string_utf8(env, value, (char *)dest, type->size, &len);
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, value, (char16_t *)dest, type->size / 2, &len);
480
+ RG_ASSERT(status == napi_ok);
481
+
482
+ len *= 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 + len, 0, type->size - len);
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::Object obj2 = PopArray(src, member.type, realign);
455
- obj.Set(member.name, obj2);
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
- Napi::Object CallData::PopArray(const uint8_t *src, const TypeInfo *type, int16_t realign)
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::TypedArray) { \
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: { 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;
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::Object obj = PopArray(src, type->ref, realign);
583
- array.Set(i, obj);
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::Object &obj, const TypeInfo *type, uint8_t *dest, int16_t realign = 0);
81
+ bool PushArray(const Napi::Value &value, 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::Object PopArray(const uint8_t *src, const TypeInfo *type, int16_t realign = 0);
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 *AllocateCallMemory(InstanceData *instance)
387
+ static InstanceMemory *AllocateAsyncMemory(InstanceData *instance)
394
388
  {
395
- for (InstanceMemory *mem: instance->memories) {
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 = AllocateCallMemory(instance);
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 = AllocateCallMemory(instance);
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 = AllocateCallMemory(instance);
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) {
@@ -930,8 +926,12 @@ void FunctionInfo::Unref() const
930
926
 
931
927
  InstanceData::InstanceData()
932
928
  {
933
- AllocateCallMemory(this);
934
- RG_ASSERT(memories.len == 1);
929
+ InstanceMemory *mem = new InstanceMemory();
930
+
931
+ mem->stack = AllocateAndAlign16(&mem->mem_alloc, Mebibytes(2));
932
+ mem->heap = AllocateAndAlign16(&mem->mem_alloc, Mebibytes(4));
933
+
934
+ memories.Append(mem);
935
935
  }
936
936
 
937
937
  InstanceData::~InstanceData()
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 *, 8> memories;
193
+ LocalArray<InstanceMemory *, 6> memories;
193
194
 
194
195
  BlockAllocator str_alloc;
195
196
  };
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
+ }