koffi 2.5.21-beta.2 → 2.5.21-beta.4

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.
Binary file
Binary file
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": "2.5.21-beta.2",
3
+ "version": "2.5.21-beta.4",
4
4
  "stable": "2.5.20",
5
5
  "description": "Fast and simple C FFI (foreign function interface) for Node.js",
6
6
  "keywords": [
@@ -976,6 +976,8 @@ public:
976
976
  void *Resize(void *ptr, Size old_size, Size new_size, unsigned int flags = 0) override;
977
977
  void Release(const void *ptr, Size size) override;
978
978
 
979
+ bool IsUsed() const { return list.next; }
980
+
979
981
  private:
980
982
  static Bucket *PointerToBucket(void *ptr);
981
983
  };
@@ -1002,6 +1004,12 @@ public:
1002
1004
  void *Resize(void *ptr, Size old_size, Size new_size, unsigned int flags = 0) override;
1003
1005
  void Release(const void *ptr, Size size) override;
1004
1006
 
1007
+ bool IsUsed() const
1008
+ {
1009
+ LinkedAllocator *alloc = ((BlockAllocatorBase *)this)->GetAllocator();
1010
+ return alloc->IsUsed();
1011
+ }
1012
+
1005
1013
  protected:
1006
1014
  void CopyFrom(BlockAllocatorBase *other);
1007
1015
  void ForgetCurrentBlock();
package/src/index.d.ts CHANGED
@@ -28,7 +28,7 @@ declare module 'koffi' {
28
28
 
29
29
  type PrimitiveKind = 'Void' | 'Bool' | 'Int8' | 'UInt8' | 'Int16' | 'Int16S' | 'UInt16' | 'UInt16S' |
30
30
  'Int32' | 'Int32S' | 'UInt32' | 'UInt32S' | 'Int64' | 'Int64S' | 'UInt64' | 'UInt64S' |
31
- 'String' | 'String16' | 'Pointer' | 'Record' /* | 'Union' */ | 'Array' | 'Float32' | 'Float64' |
31
+ 'String' | 'String16' | 'Pointer' | 'Record' | 'Union' | 'Array' | 'Float32' | 'Float64' |
32
32
  'Prototype' | 'Callback';
33
33
  type ArrayHint = 'Array' | 'Typed' | 'String';
34
34
 
@@ -80,6 +80,8 @@ declare module 'koffi' {
80
80
  thiscall(definition: string): KoffiFunction;
81
81
  thiscall(name: string, result: TypeSpec, arguments: TypeSpec[]): KoffiFunction;
82
82
 
83
+ symbol(name: string, type: TypeSpec): any;
84
+
83
85
  unload(): void;
84
86
  }
85
87
 
@@ -129,6 +131,12 @@ declare module 'koffi' {
129
131
  export function decode(value: any, offset: number, type: TypeSpec): any;
130
132
  export function decode(value: any, offset: number, type: TypeSpec, len: number): any;
131
133
  export function address(value: any): bigint;
134
+ export function encode(ref: any, type: TypeSpec): void;
135
+ export function encode(ref: any, type: TypeSpec, value: any): void;
136
+ export function encode(ref: any, type: TypeSpec, value: any, len: number): void;
137
+ export function encode(ref: any, offset: number, type: TypeSpec): void;
138
+ export function encode(ref: any, offset: number, type: TypeSpec, value: any): void;
139
+ export function encode(ref: any, offset: number, type: TypeSpec, value: any, len: number): void;
132
140
 
133
141
  export function sizeof(type: TypeSpec): number;
134
142
  export function alignof(type: TypeSpec): number;
package/src/index.js CHANGED
@@ -378,7 +378,7 @@ var require_package = __commonJS({
378
378
  "build/dist/src/koffi/package.json"(exports2, module2) {
379
379
  module2.exports = {
380
380
  name: "koffi",
381
- version: "2.5.21-beta.2",
381
+ version: "2.5.21-beta.4",
382
382
  stable: "2.5.20",
383
383
  description: "Fast and simple C FFI (foreign function interface) for Node.js",
384
384
  keywords: [
@@ -126,6 +126,8 @@ public:
126
126
 
127
127
  void *ReserveTrampoline(const FunctionInfo *proto, Napi::Function func);
128
128
 
129
+ BlockAllocator *GetAllocator() { return &call_alloc; }
130
+
129
131
  private:
130
132
  template <typename T>
131
133
  bool AllocStack(Size size, Size align, T **out_ptr);
@@ -1558,6 +1558,43 @@ static Napi::Value FindLibraryFunction(const Napi::CallbackInfo &info, CallConve
1558
1558
  return wrapper;
1559
1559
  }
1560
1560
 
1561
+ static Napi::Value FindSymbol(const Napi::CallbackInfo &info)
1562
+ {
1563
+ Napi::Env env = info.Env();
1564
+ InstanceData *instance = env.GetInstanceData<InstanceData>();
1565
+ LibraryHolder *lib = (LibraryHolder *)info.Data();
1566
+
1567
+ if (info.Length() < 2) {
1568
+ ThrowError<Napi::TypeError>(env, "Expected 2, got %1", info.Length());
1569
+ return env.Null();
1570
+ }
1571
+ if (!info[0].IsString()) {
1572
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for name, expected string", GetValueType(instance, info[0]));
1573
+ return env.Null();
1574
+ }
1575
+
1576
+ std::string name = info[0].As<Napi::String>();
1577
+
1578
+ const TypeInfo *type = ResolveType(info[1]);
1579
+ if (!type)
1580
+ return env.Null();
1581
+
1582
+ #ifdef _WIN32
1583
+ void *ptr = (void *)GetProcAddress((HMODULE)lib->module, name.c_str());
1584
+ #else
1585
+ void *ptr = (void *)dlsym(lib->module, name.c_str());
1586
+ #endif
1587
+ if (!ptr) {
1588
+ ThrowError<Napi::Error>(env, "Cannot find symbol '%1' in shared library", name.c_str());
1589
+ return env.Null();
1590
+ }
1591
+
1592
+ Napi::External<void> external = Napi::External<void>::New(env, ptr);
1593
+ SetValueTag(instance, external, &type);
1594
+
1595
+ return external;
1596
+ }
1597
+
1561
1598
  static Napi::Value UnloadLibrary(const Napi::CallbackInfo &info)
1562
1599
  {
1563
1600
  Napi::Env env = info.Env();
@@ -1607,6 +1644,18 @@ static Napi::Value LoadSharedLibrary(const Napi::CallbackInfo &info)
1607
1644
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for filename, expected string or null", GetValueType(instance, info[0]));
1608
1645
  return env.Null();
1609
1646
  }
1647
+ if (info.Length() >= 2 && !IsObject(info[1])) {
1648
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for options, expected object", GetValueType(instance, info[1]));
1649
+ return env.Null();
1650
+ }
1651
+
1652
+ #ifndef _WIN32
1653
+ bool lazy = false;
1654
+ if (info.Length() >= 2) {
1655
+ Napi::Object options = info[1].As<Napi::Object>();
1656
+ lazy = options.Get("lazy").ToBoolean();
1657
+ }
1658
+ #endif
1610
1659
 
1611
1660
  if (!instance->memories.len) {
1612
1661
  AllocateMemory(instance, instance->config.sync_stack_size, instance->config.sync_heap_size);
@@ -1628,8 +1677,10 @@ static Napi::Value LoadSharedLibrary(const Napi::CallbackInfo &info)
1628
1677
  }
1629
1678
  #else
1630
1679
  if (info[0].IsString()) {
1680
+ int flags = lazy ? RTLD_LAZY : RTLD_NOW;
1681
+
1631
1682
  std::string filename = info[0].As<Napi::String>();
1632
- module = dlopen(filename.c_str(), RTLD_NOW);
1683
+ module = dlopen(filename.c_str(), flags);
1633
1684
 
1634
1685
  if (!module) {
1635
1686
  const char *msg = dlerror();
@@ -1654,13 +1705,14 @@ static Napi::Value LoadSharedLibrary(const Napi::CallbackInfo &info)
1654
1705
 
1655
1706
  Napi::Object obj = Napi::Object::New(env);
1656
1707
 
1657
- #define ADD_CONVENTION(Name, Value) \
1708
+ #define ADD_METHOD(Name, Call) \
1658
1709
  do { \
1659
- const auto wrapper = [](const Napi::CallbackInfo &info) { return FindLibraryFunction(info, (Value)); }; \
1710
+ const auto wrapper = [](const Napi::CallbackInfo &info) { return Call; }; \
1660
1711
  Napi::Function func = Napi::Function::New(env, wrapper, (Name), (void *)lib->Ref()); \
1661
1712
  func.AddFinalizer([](Napi::Env, LibraryHolder *lib) { lib->Unref(); }, lib); \
1662
1713
  obj.Set((Name), func); \
1663
1714
  } while (false)
1715
+ #define ADD_CONVENTION(Name, Value) ADD_METHOD((Name), FindLibraryFunction(info, Value))
1664
1716
 
1665
1717
  ADD_CONVENTION("func", CallConvention::Cdecl);
1666
1718
  ADD_CONVENTION("cdecl", CallConvention::Cdecl);
@@ -1668,10 +1720,14 @@ static Napi::Value LoadSharedLibrary(const Napi::CallbackInfo &info)
1668
1720
  ADD_CONVENTION("fastcall", CallConvention::Fastcall);
1669
1721
  ADD_CONVENTION("thiscall", CallConvention::Thiscall);
1670
1722
 
1671
- #undef ADD_CONVENTION
1723
+ ADD_METHOD("symbol", FindSymbol(info));
1672
1724
 
1725
+ // We can't unref the library after unload, obviously
1673
1726
  obj.Set("unload", Napi::Function::New(env, UnloadLibrary, "unload", (void *)lib->Ref()));
1674
1727
 
1728
+ #undef ADD_CONVENTION
1729
+ #undef ADD_METHOD
1730
+
1675
1731
  return obj;
1676
1732
  }
1677
1733
 
@@ -287,6 +287,9 @@ struct InstanceData {
287
287
  void *main_stack_min;
288
288
  #endif
289
289
 
290
+ BucketArray<BlockAllocator> encode_allocators;
291
+ HashMap<void *, BlockAllocator *> encode_map;
292
+
290
293
  HashMap<void *, int16_t> trampolines_map;
291
294
 
292
295
  BlockAllocator str_alloc;
@@ -1230,7 +1230,6 @@ bool Encode(Napi::Value ref, Size offset, Napi::Value value, const TypeInfo *typ
1230
1230
  bool Encode(Napi::Env env, uint8_t *origin, Napi::Value value, const TypeInfo *type, const Size *len)
1231
1231
  {
1232
1232
  InstanceData *instance = env.GetInstanceData<InstanceData>();
1233
- CallData *call = GetThreadCall();
1234
1233
 
1235
1234
  if (len && type->primitive != PrimitiveKind::String &&
1236
1235
  type->primitive != PrimitiveKind::String16 &&
@@ -1243,10 +1242,8 @@ bool Encode(Napi::Env env, uint8_t *origin, Napi::Value value, const TypeInfo *t
1243
1242
  type = MakeArrayType(instance, type, *len);
1244
1243
  }
1245
1244
 
1246
- if (!call) [[unlikely]] {
1247
- ThrowError<Napi::Error>(env, "koffi.encode() can only be used inside callbacks");
1248
- return false;
1249
- }
1245
+ InstanceMemory mem = {};
1246
+ CallData call(env, instance, &mem);
1250
1247
 
1251
1248
  #define PUSH_INTEGER(CType) \
1252
1249
  do { \
@@ -1297,19 +1294,19 @@ bool Encode(Napi::Env env, uint8_t *origin, Napi::Value value, const TypeInfo *t
1297
1294
  case PrimitiveKind::UInt64S: { PUSH_INTEGER_SWAP(uint64_t); } break;
1298
1295
  case PrimitiveKind::String: {
1299
1296
  const char *str;
1300
- if (!call->PushString(value, 1, &str)) [[unlikely]]
1297
+ if (!call.PushString(value, 1, &str)) [[unlikely]]
1301
1298
  return false;
1302
1299
  *(const char **)origin = str;
1303
1300
  } break;
1304
1301
  case PrimitiveKind::String16: {
1305
1302
  const char16_t *str16;
1306
- if (!call->PushString16(value, 1, &str16)) [[unlikely]]
1303
+ if (!call.PushString16(value, 1, &str16)) [[unlikely]]
1307
1304
  return false;
1308
1305
  *(const char16_t **)origin = str16;
1309
1306
  } break;
1310
1307
  case PrimitiveKind::Pointer: {
1311
1308
  void *ptr;
1312
- if (!call->PushPointer(value, type, 1, &ptr)) [[unlikely]]
1309
+ if (!call.PushPointer(value, type, 1, &ptr)) [[unlikely]]
1313
1310
  return false;
1314
1311
  *(void **)origin = ptr;
1315
1312
  } break;
@@ -1322,7 +1319,7 @@ bool Encode(Napi::Env env, uint8_t *origin, Napi::Value value, const TypeInfo *t
1322
1319
 
1323
1320
  Napi::Object obj = value.As<Napi::Object>();
1324
1321
 
1325
- if (!call->PushObject(obj, type, origin))
1322
+ if (!call.PushObject(obj, type, origin))
1326
1323
  return false;
1327
1324
  } break;
1328
1325
  case PrimitiveKind::Array: {
@@ -1330,15 +1327,15 @@ bool Encode(Napi::Env env, uint8_t *origin, Napi::Value value, const TypeInfo *t
1330
1327
  Napi::Array array = value.As<Napi::Array>();
1331
1328
  Size len = (Size)type->size / type->ref.type->size;
1332
1329
 
1333
- if (!call->PushNormalArray(array, len, type, origin))
1330
+ if (!call.PushNormalArray(array, len, type, origin))
1334
1331
  return false;
1335
1332
  } else if (IsRawBuffer(value)) {
1336
1333
  Span<const uint8_t> buffer = GetRawBuffer(value);
1337
1334
 
1338
- if (!call->PushBuffer(buffer, type->size, type, origin))
1335
+ if (!call.PushBuffer(buffer, type->size, type, origin))
1339
1336
  return false;
1340
1337
  } else if (value.IsString()) {
1341
- if (!call->PushStringArray(value, type, origin))
1338
+ if (!call.PushStringArray(value, type, origin))
1342
1339
  return false;
1343
1340
  } else {
1344
1341
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value, expected array", GetValueType(instance, value));
@@ -1367,11 +1364,8 @@ bool Encode(Napi::Env env, uint8_t *origin, Napi::Value value, const TypeInfo *t
1367
1364
  void *ptr;
1368
1365
 
1369
1366
  if (value.IsFunction()) {
1370
- Napi::Function func = value.As<Napi::Function>();
1371
-
1372
- ptr = call->ReserveTrampoline(type->ref.proto, func);
1373
- if (!ptr) [[unlikely]]
1374
- return false;
1367
+ ThrowError<Napi::Error>(env, "Cannot encode non-registered callback");
1368
+ return false;
1375
1369
  } else if (CheckValueTag(instance, value, type->ref.marker)) {
1376
1370
  ptr = value.As<Napi::External<void>>().Data();
1377
1371
  } else if (IsNullOrUndefined(value)) {
@@ -1390,6 +1384,22 @@ bool Encode(Napi::Env env, uint8_t *origin, Napi::Value value, const TypeInfo *t
1390
1384
  #undef PUSH_INTEGER_SWAP
1391
1385
  #undef PUSH_INTEGER
1392
1386
 
1387
+ // Keep memory around if any was allocated
1388
+ {
1389
+ BlockAllocator *alloc = call.GetAllocator();
1390
+
1391
+ if (alloc->IsUsed()) {
1392
+ BlockAllocator *copy = instance->encode_map.FindValue(origin, nullptr);
1393
+
1394
+ if (!copy) {
1395
+ copy = instance->encode_allocators.AppendDefault();
1396
+ instance->encode_map.Set(origin, copy);
1397
+ }
1398
+
1399
+ std::swap(*alloc, *copy);
1400
+ }
1401
+ }
1402
+
1393
1403
  return true;
1394
1404
  }
1395
1405