node-ctypes 1.1.0 → 1.3.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 +125 -10
- package/build/Release/ctypes-darwin-arm64.node +0 -0
- package/build/Release/ctypes-darwin-x64.node +0 -0
- package/build/Release/ctypes-linux-arm64.node +0 -0
- package/build/Release/ctypes-linux-x64.node +0 -0
- package/build/Release/ctypes-win32-arm64.node +0 -0
- package/build/Release/ctypes-win32-x64.node +0 -0
- package/lib/core/Library.js +222 -221
- package/lib/core/callback.js +4 -4
- package/lib/core/types.js +41 -25
- package/lib/index.d.ts +57 -7
- package/lib/index.js +57 -60
- package/lib/memory/pointer.js +244 -27
- package/lib/structures/Structure.js +261 -230
- package/lib/structures/Union.js +10 -2
- package/lib/structures/helpers/common.js +9 -1
- package/lib/structures/helpers/struct.js +6 -1
- package/lib/structures/helpers/union.js +9 -19
- package/lib/types/SimpleCData.js +5 -5
- package/lib/types/primitives.js +37 -23
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -221,15 +221,15 @@ console.log(u.f); // 42 reinterpreted as float
|
|
|
221
221
|
### Bit Fields - Compact Data Structures
|
|
222
222
|
|
|
223
223
|
```javascript
|
|
224
|
-
import { Structure,
|
|
224
|
+
import { Structure, c_uint32 } from 'node-ctypes';
|
|
225
225
|
|
|
226
|
-
// Bit fields
|
|
226
|
+
// Bit fields using Python-style syntax: [name, type, bits]
|
|
227
227
|
class Flags extends Structure {
|
|
228
228
|
static _fields_ = [
|
|
229
|
-
["enabled",
|
|
230
|
-
["mode",
|
|
231
|
-
["priority",
|
|
232
|
-
["reserved",
|
|
229
|
+
["enabled", c_uint32, 1], // 1 bit
|
|
230
|
+
["mode", c_uint32, 3], // 3 bits
|
|
231
|
+
["priority", c_uint32, 4], // 4 bits
|
|
232
|
+
["reserved", c_uint32, 24] // 24 bits
|
|
233
233
|
];
|
|
234
234
|
}
|
|
235
235
|
|
|
@@ -244,6 +244,19 @@ console.log(flags.mode); // 5
|
|
|
244
244
|
console.log(flags.priority); // 12
|
|
245
245
|
```
|
|
246
246
|
|
|
247
|
+
**Alternative syntax with `bitfield()` helper:**
|
|
248
|
+
|
|
249
|
+
```javascript
|
|
250
|
+
import { Structure, bitfield, c_uint32 } from 'node-ctypes';
|
|
251
|
+
|
|
252
|
+
class Flags extends Structure {
|
|
253
|
+
static _fields_ = [
|
|
254
|
+
["enabled", bitfield(c_uint32, 1)],
|
|
255
|
+
["mode", bitfield(c_uint32, 3)],
|
|
256
|
+
];
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
247
260
|
### Arrays - Fixed-size and Dynamic
|
|
248
261
|
|
|
249
262
|
```javascript
|
|
@@ -508,9 +521,33 @@ Benchmarked on Windows with Node.js v24.11.0:
|
|
|
508
521
|
**Key Insights:**
|
|
509
522
|
- koffi excels at simple operations and struct access
|
|
510
523
|
- node-ctypes competitive on complex argument handling
|
|
511
|
-
- **Struct performance gap**: koffi
|
|
524
|
+
- **Struct performance gap**: koffi ~13x faster due to plain object vs Proxy+N-API
|
|
525
|
+
- **Workaround**: Use `toObject()` for repeated struct reads (see below)
|
|
512
526
|
- **Callback overhead**: koffi 1.5x faster at callback creation
|
|
513
527
|
|
|
528
|
+
### Struct Performance Tip
|
|
529
|
+
|
|
530
|
+
When reading struct fields repeatedly (e.g., in a tight loop), use `toObject()` to convert to a plain JavaScript object:
|
|
531
|
+
|
|
532
|
+
```javascript
|
|
533
|
+
const point = Point.create({ x: 10, y: 20 });
|
|
534
|
+
|
|
535
|
+
// ❌ Slow: Each access goes through Proxy → N-API → buffer read
|
|
536
|
+
for (let i = 0; i < 1000000; i++) {
|
|
537
|
+
const x = point.x; // ~80ns per access
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// ✅ Fast: Convert once, then use plain object access
|
|
541
|
+
const obj = point.toObject(); // { x: 10, y: 20 }
|
|
542
|
+
for (let i = 0; i < 1000000; i++) {
|
|
543
|
+
const x = obj.x; // ~6ns per access (same as koffi!)
|
|
544
|
+
}
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
**When to use direct access vs toObject():**
|
|
548
|
+
- **Direct access (`point.x`)**: Always synchronized with underlying buffer; required when C code modifies the buffer
|
|
549
|
+
- **`toObject()`**: Snapshot copy; use for read-only loops or when passing data to JS-only code
|
|
550
|
+
|
|
514
551
|
**Transparent API overhead**: Only **3.5%** for auto `._buffer` extraction!
|
|
515
552
|
|
|
516
553
|
*See `tests/benchmarks/` for full benchmark suite.*
|
|
@@ -623,7 +660,8 @@ puts(s);
|
|
|
623
660
|
|
|
624
661
|
**Types and helpers**
|
|
625
662
|
- `sizeof(type)` → `number` : size in bytes of a type.
|
|
626
|
-
- `POINTER(baseType)` : creates a pointer type
|
|
663
|
+
- `POINTER(baseType)` : creates a pointer type factory (see [POINTER and pointer() API](#pointer-and-pointer-api) below).
|
|
664
|
+
- `pointer(obj)` : creates a pointer to an existing ctypes instance (Python-compatible).
|
|
627
665
|
- `byref(buffer)` : passes a buffer by reference (Python ctypes compatibility).
|
|
628
666
|
- `cast(ptr, targetType)` : interprets a pointer as another type (returns wrapper for struct).
|
|
629
667
|
|
|
@@ -700,6 +738,82 @@ Write a value to memory.
|
|
|
700
738
|
#### `sizeof(type)` → `number`
|
|
701
739
|
Get the size of a type in bytes.
|
|
702
740
|
|
|
741
|
+
#### POINTER and pointer() API
|
|
742
|
+
|
|
743
|
+
`node-ctypes` provides a Python ctypes-compatible pointer API:
|
|
744
|
+
|
|
745
|
+
```javascript
|
|
746
|
+
import { POINTER, pointer, c_int32, Structure } from 'node-ctypes';
|
|
747
|
+
|
|
748
|
+
// Create a pointer type
|
|
749
|
+
const IntPtr = POINTER(c_int32);
|
|
750
|
+
|
|
751
|
+
// Create NULL pointer
|
|
752
|
+
const p1 = IntPtr.create();
|
|
753
|
+
console.log(p1.isNull); // true
|
|
754
|
+
|
|
755
|
+
// Create pointer from buffer
|
|
756
|
+
const buf = Buffer.alloc(12);
|
|
757
|
+
buf.writeInt32LE(10, 0);
|
|
758
|
+
buf.writeInt32LE(20, 4);
|
|
759
|
+
buf.writeInt32LE(30, 8);
|
|
760
|
+
|
|
761
|
+
const p2 = IntPtr.fromBuffer(buf);
|
|
762
|
+
|
|
763
|
+
// .contents property (Python-compatible)
|
|
764
|
+
console.log(p2.contents); // 10
|
|
765
|
+
p2.contents = 100;
|
|
766
|
+
console.log(buf.readInt32LE(0)); // 100
|
|
767
|
+
|
|
768
|
+
// Pointer indexing (Python-compatible)
|
|
769
|
+
console.log(p2[0]); // 100
|
|
770
|
+
console.log(p2[1]); // 20
|
|
771
|
+
console.log(p2[2]); // 30
|
|
772
|
+
|
|
773
|
+
p2[1] = 200;
|
|
774
|
+
console.log(buf.readInt32LE(4)); // 200
|
|
775
|
+
|
|
776
|
+
// pointer() function - create pointer to existing object
|
|
777
|
+
const x = new c_int32(42);
|
|
778
|
+
const px = pointer(x);
|
|
779
|
+
console.log(px.contents); // 42
|
|
780
|
+
px.contents = 100;
|
|
781
|
+
console.log(x.value); // 100
|
|
782
|
+
|
|
783
|
+
// Works with structures too
|
|
784
|
+
class Point extends Structure {
|
|
785
|
+
static _fields_ = [["x", c_int32], ["y", c_int32]];
|
|
786
|
+
}
|
|
787
|
+
const pt = new Point({ x: 10, y: 20 });
|
|
788
|
+
const ppt = pointer(pt);
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
**POINTER type methods:**
|
|
792
|
+
- `POINTER(baseType)` - creates a pointer type factory
|
|
793
|
+
- `PointerType.create()` - creates a NULL pointer instance
|
|
794
|
+
- `PointerType.fromBuffer(buf)` - creates pointer to buffer
|
|
795
|
+
- `PointerType.fromAddress(addr)` - creates pointer from address
|
|
796
|
+
|
|
797
|
+
**Pointer instance properties:**
|
|
798
|
+
- `.contents` - get/set dereferenced value (Python-compatible)
|
|
799
|
+
- `.address` - get raw address as BigInt
|
|
800
|
+
- `.isNull` - check if pointer is NULL
|
|
801
|
+
- `[n]` - array-style indexing with pointer arithmetic
|
|
802
|
+
- `.deref()` - alias for `.contents` getter
|
|
803
|
+
- `.set(value)` - update pointer target
|
|
804
|
+
|
|
805
|
+
**Using POINTER in function definitions:**
|
|
806
|
+
```javascript
|
|
807
|
+
// POINTER types can be used as argtypes and restype
|
|
808
|
+
const IntPtr = POINTER(c_int32);
|
|
809
|
+
|
|
810
|
+
// As argument type
|
|
811
|
+
const memset = msvcrt.func("memset", c_void_p, [IntPtr, c_int32, c_size_t]);
|
|
812
|
+
|
|
813
|
+
// As return type
|
|
814
|
+
const memchr = msvcrt.func("memchr", IntPtr, [c_void_p, c_int32, c_size_t]);
|
|
815
|
+
```
|
|
816
|
+
|
|
703
817
|
#### `struct(fields)` → `StructDefinition`
|
|
704
818
|
Create a simple struct definition.
|
|
705
819
|
|
|
@@ -720,10 +834,11 @@ Base class for Python-like union definitions. Subclasses should define `static _
|
|
|
720
834
|
| **Structs** | `class Point(Structure):`<br> `_fields_ = [("x", c_int)]` | `class Point extends Structure`<br> `{ static _fields_ = [["x", c_int]] }` |
|
|
721
835
|
| **Unions** | `class U(Union):`<br> `_fields_ = [("i", c_int)]` | `class U extends Union`<br> `{ static _fields_ = [["i", c_int]] }` |
|
|
722
836
|
| **Arrays** | `c_int * 5` | `array(c_int, 5)` |
|
|
723
|
-
| **Bit fields** | `("flags", c_uint, 3)` | `bitfield(c_uint32, 3)` |
|
|
837
|
+
| **Bit fields** | `("flags", c_uint, 3)` | `["flags", c_uint32, 3]`<br>**or** `bitfield(c_uint32, 3)` |
|
|
724
838
|
| **Callbacks** | `CFUNCTYPE(c_int, c_int)` | `callback(fn, c_int, [c_int])` |
|
|
725
839
|
| **Strings** | `c_char_p(b"hello")` | `create_string_buffer("hello")`<br>**or**<br>`new c_char_p(b"hello")` |
|
|
726
|
-
| **Pointers** | `POINTER(c_int)` | `
|
|
840
|
+
| **Pointers** | `POINTER(c_int)`<br>`p.contents`<br>`p[0]` | `POINTER(c_int)`<br>`p.contents`<br>`p[0]` |
|
|
841
|
+
| **pointer()** | `pointer(obj)` | `pointer(obj)` |
|
|
727
842
|
| **Variadic** | `sprintf(buf, b"%d", 42)` | `sprintf(buf, fmt, 42)` (auto) |
|
|
728
843
|
| **Sizeof** | `sizeof(c_int)` | `sizeof(c_int)` |
|
|
729
844
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/lib/core/Library.js
CHANGED
|
@@ -51,262 +51,263 @@
|
|
|
51
51
|
* @private
|
|
52
52
|
*/
|
|
53
53
|
export function createLibraryClasses(Library, LRUCache, _toNativeType, _toNativeTypes, native) {
|
|
54
|
-
class FunctionWrapper {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
class FunctionWrapper {
|
|
55
|
+
constructor(cdll, name) {
|
|
56
|
+
const argtypes = [];
|
|
57
|
+
let restype = undefined;
|
|
58
|
+
let cachedFunc = null;
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
if (!cachedFunc) {
|
|
66
|
-
cachedFunc = cdll.func(name, restype, argtypes);
|
|
67
|
-
}
|
|
68
|
-
return cachedFunc(...args);
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
// Proxy the function to intercept property access
|
|
72
|
-
return new Proxy(func, {
|
|
73
|
-
get(target, prop) {
|
|
74
|
-
if (prop === "argtypes") return argtypes;
|
|
75
|
-
if (prop === "restype") return restype;
|
|
76
|
-
if (prop === "errcheck") return cachedFunc ? cachedFunc.errcheck : undefined;
|
|
77
|
-
return target[prop];
|
|
78
|
-
},
|
|
79
|
-
set(target, prop, value) {
|
|
80
|
-
if (prop === "argtypes") {
|
|
81
|
-
argtypes.splice(0, argtypes.length, ...value);
|
|
82
|
-
cachedFunc = null; // Invalidate cache
|
|
83
|
-
return true;
|
|
60
|
+
// Create a function and proxy it to make it callable
|
|
61
|
+
const func = (...args) => {
|
|
62
|
+
if (restype === undefined) {
|
|
63
|
+
throw new Error(`Function ${name}: restype not set`);
|
|
84
64
|
}
|
|
85
|
-
if (
|
|
86
|
-
|
|
87
|
-
cachedFunc = null; // Invalidate cache
|
|
88
|
-
return true;
|
|
65
|
+
if (!cachedFunc) {
|
|
66
|
+
cachedFunc = cdll.func(name, restype, argtypes);
|
|
89
67
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
68
|
+
return cachedFunc(...args);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Proxy the function to intercept property access
|
|
72
|
+
return new Proxy(func, {
|
|
73
|
+
get(target, prop) {
|
|
74
|
+
if (prop === "argtypes") return argtypes;
|
|
75
|
+
if (prop === "restype") return restype;
|
|
76
|
+
if (prop === "errcheck") return cachedFunc ? cachedFunc.errcheck : undefined;
|
|
77
|
+
return target[prop];
|
|
78
|
+
},
|
|
79
|
+
set(target, prop, value) {
|
|
80
|
+
if (prop === "argtypes") {
|
|
81
|
+
argtypes.splice(0, argtypes.length, ...value);
|
|
82
|
+
cachedFunc = null; // Invalidate cache
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
if (prop === "restype") {
|
|
86
|
+
restype = value;
|
|
87
|
+
cachedFunc = null; // Invalidate cache
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
if (prop === "errcheck") {
|
|
91
|
+
if (!cachedFunc) {
|
|
92
|
+
if (restype === undefined) {
|
|
93
|
+
throw new Error(`Function ${name}: restype not set`);
|
|
94
|
+
}
|
|
95
|
+
cachedFunc = cdll.func(name, restype, argtypes);
|
|
94
96
|
}
|
|
95
|
-
cachedFunc =
|
|
97
|
+
cachedFunc.errcheck = value;
|
|
98
|
+
return true;
|
|
96
99
|
}
|
|
97
|
-
|
|
100
|
+
target[prop] = value;
|
|
98
101
|
return true;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
},
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Wrapper conveniente per CDLL (come in Python ctypes)
|
|
109
|
-
*/
|
|
110
|
-
class CDLL {
|
|
111
|
-
constructor(libPath, options = {}) {
|
|
112
|
-
this._lib = new Library(libPath);
|
|
113
|
-
// Use LRU cache to prevent unbounded memory growth
|
|
114
|
-
const cacheSize = options.cacheSize ?? 1000;
|
|
115
|
-
this._cache = new LRUCache(cacheSize);
|
|
116
|
-
|
|
117
|
-
// Return a proxy to enable Python-like syntax: libc.abs.argtypes = [...]; libc.abs.restype = ...; libc.abs(...)
|
|
118
|
-
return new Proxy(this, {
|
|
119
|
-
get(target, prop, receiver) {
|
|
120
|
-
// Allow access to existing properties/methods
|
|
121
|
-
if (prop in target || typeof prop !== "string") {
|
|
122
|
-
return Reflect.get(target, prop, receiver);
|
|
123
|
-
}
|
|
124
|
-
// Assume it's a function name from the library
|
|
125
|
-
return new FunctionWrapper(target, prop);
|
|
126
|
-
},
|
|
127
|
-
});
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
}
|
|
128
105
|
}
|
|
129
106
|
|
|
130
107
|
/**
|
|
131
|
-
*
|
|
132
|
-
* @param {string} name - Nome della funzione
|
|
133
|
-
* @param {string|CType} returnType - Tipo di ritorno
|
|
134
|
-
* @param {Array<string|CType>} argTypes - Tipi degli argomenti
|
|
135
|
-
* @param {Object} [options] - Opzioni aggiuntive (es. { abi: 'stdcall' })
|
|
136
|
-
* @returns {Function} Funzione callable
|
|
108
|
+
* Wrapper conveniente per CDLL (come in Python ctypes)
|
|
137
109
|
*/
|
|
138
|
-
|
|
139
|
-
|
|
110
|
+
class CDLL {
|
|
111
|
+
constructor(libPath, options = {}) {
|
|
112
|
+
this._lib = new Library(libPath);
|
|
113
|
+
// Use LRU cache to prevent unbounded memory growth
|
|
114
|
+
const cacheSize = options.cacheSize ?? 1000;
|
|
115
|
+
this._cache = new LRUCache(cacheSize);
|
|
116
|
+
// Cache for FunctionWrapper instances (Python-like syntax)
|
|
117
|
+
this._funcWrapperCache = new Map();
|
|
140
118
|
|
|
141
|
-
|
|
142
|
-
return this
|
|
119
|
+
// Return a proxy to enable Python-like syntax: libc.abs.argtypes = [...]; libc.abs.restype = ...; libc.abs(...)
|
|
120
|
+
return new Proxy(this, {
|
|
121
|
+
get(target, prop, receiver) {
|
|
122
|
+
// Allow access to existing properties/methods
|
|
123
|
+
if (prop in target || typeof prop !== "string") {
|
|
124
|
+
return Reflect.get(target, prop, receiver);
|
|
125
|
+
}
|
|
126
|
+
// Check if we already have a FunctionWrapper for this function name
|
|
127
|
+
if (target._funcWrapperCache.has(prop)) {
|
|
128
|
+
return target._funcWrapperCache.get(prop);
|
|
129
|
+
}
|
|
130
|
+
// Create and cache a new FunctionWrapper
|
|
131
|
+
const wrapper = new FunctionWrapper(target, prop);
|
|
132
|
+
target._funcWrapperCache.set(prop, wrapper);
|
|
133
|
+
return wrapper;
|
|
134
|
+
},
|
|
135
|
+
});
|
|
143
136
|
}
|
|
144
137
|
|
|
145
|
-
|
|
138
|
+
/**
|
|
139
|
+
* Ottiene una funzione dalla libreria
|
|
140
|
+
* @param {string} name - Nome della funzione
|
|
141
|
+
* @param {Function|number|CType} returnType - Tipo di ritorno (SimpleCData class, CType value, o CType)
|
|
142
|
+
* @param {Array<Function|number|CType>} argTypes - Tipi degli argomenti
|
|
143
|
+
* @param {Object} [options] - Opzioni aggiuntive (es. { abi: 'stdcall' })
|
|
144
|
+
* @returns {Function} Funzione callable
|
|
145
|
+
*/
|
|
146
|
+
func(name, returnType, argTypes = [], options = {}) {
|
|
147
|
+
const cacheKey = `${name}:${returnType}:${argTypes.join(",")}`;
|
|
148
|
+
|
|
149
|
+
if (this._cache.has(cacheKey)) {
|
|
150
|
+
return this._cache.get(cacheKey);
|
|
151
|
+
}
|
|
146
152
|
|
|
147
|
-
|
|
148
|
-
// OPTIMIZATION: Create specialized wrapper based on argument types
|
|
149
|
-
// For primitive-only args, bypass the processing loop entirely
|
|
150
|
-
// =========================================================================
|
|
153
|
+
const ffiFunc = this._lib.func(name, _toNativeType(returnType, native), _toNativeTypes(argTypes, native), options);
|
|
151
154
|
|
|
152
|
-
|
|
155
|
+
// =========================================================================
|
|
156
|
+
// OPTIMIZATION: Create specialized wrapper based on argument types
|
|
157
|
+
// For primitive-only args, bypass the processing loop entirely
|
|
158
|
+
// =========================================================================
|
|
153
159
|
|
|
154
|
-
|
|
155
|
-
const allPrimitive = argTypes.every((t) => {
|
|
156
|
-
if (typeof t === "function" && t._isSimpleCData) {
|
|
157
|
-
// SimpleCData types that are NOT pointers are primitive
|
|
158
|
-
const typeName = t._type;
|
|
159
|
-
return typeName !== "pointer" && typeName !== "char_p" && typeName !== "wchar_p";
|
|
160
|
-
}
|
|
161
|
-
if (typeof t === "string") {
|
|
162
|
-
return t !== "pointer" && t !== "char_p" && t !== "wchar_p" && t !== "void_p";
|
|
163
|
-
}
|
|
164
|
-
// CType objects from native - check if it's a pointer type
|
|
165
|
-
if (t && typeof t === "object" && t.name) {
|
|
166
|
-
return !t.name.includes("pointer") && !t.name.includes("char_p");
|
|
167
|
-
}
|
|
168
|
-
return false;
|
|
169
|
-
});
|
|
160
|
+
const argCount = argTypes.length;
|
|
170
161
|
|
|
171
|
-
|
|
162
|
+
// Check if all args are primitive types (no structs/buffers that need _buffer extraction)
|
|
163
|
+
const allPrimitive = argTypes.every((t) => {
|
|
164
|
+
if (typeof t === "function" && t._isSimpleCData) {
|
|
165
|
+
// SimpleCData types that are NOT pointers are primitive
|
|
166
|
+
const typeName = t._type;
|
|
167
|
+
return typeName !== native.CType.POINTER && typeName !== native.CType.STRING && typeName !== native.CType.WSTRING;
|
|
168
|
+
}
|
|
169
|
+
return false;
|
|
170
|
+
});
|
|
172
171
|
|
|
173
|
-
|
|
174
|
-
// FAST PATH: No arguments - direct call
|
|
175
|
-
callMethod = function () {
|
|
176
|
-
return ffiFunc.call();
|
|
177
|
-
};
|
|
178
|
-
} else if (allPrimitive) {
|
|
179
|
-
// FAST PATH: All primitive args - direct call without processing
|
|
180
|
-
switch (argCount) {
|
|
181
|
-
case 1:
|
|
182
|
-
callMethod = function (a0) {
|
|
183
|
-
return ffiFunc.call(a0);
|
|
184
|
-
};
|
|
185
|
-
break;
|
|
186
|
-
case 2:
|
|
187
|
-
callMethod = function (a0, a1) {
|
|
188
|
-
return ffiFunc.call(a0, a1);
|
|
189
|
-
};
|
|
190
|
-
break;
|
|
191
|
-
case 3:
|
|
192
|
-
callMethod = function (a0, a1, a2) {
|
|
193
|
-
return ffiFunc.call(a0, a1, a2);
|
|
194
|
-
};
|
|
195
|
-
break;
|
|
196
|
-
case 4:
|
|
197
|
-
callMethod = function (a0, a1, a2, a3) {
|
|
198
|
-
return ffiFunc.call(a0, a1, a2, a3);
|
|
199
|
-
};
|
|
200
|
-
break;
|
|
201
|
-
default:
|
|
202
|
-
// Fallback for many args
|
|
203
|
-
callMethod = function (...args) {
|
|
204
|
-
return ffiFunc.call(...args);
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
} else {
|
|
208
|
-
// SLOW PATH: Has buffer/struct args - need to extract _buffer
|
|
209
|
-
callMethod = function (...args) {
|
|
210
|
-
const processedArgs = [];
|
|
172
|
+
let callMethod;
|
|
211
173
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
174
|
+
if (argCount === 0) {
|
|
175
|
+
// FAST PATH: No arguments - direct call
|
|
176
|
+
callMethod = function () {
|
|
177
|
+
return ffiFunc.call();
|
|
178
|
+
};
|
|
179
|
+
} else if (allPrimitive) {
|
|
180
|
+
// FAST PATH: All primitive args - direct call without processing
|
|
181
|
+
switch (argCount) {
|
|
182
|
+
case 1:
|
|
183
|
+
callMethod = function (a0) {
|
|
184
|
+
return ffiFunc.call(a0);
|
|
185
|
+
};
|
|
186
|
+
break;
|
|
187
|
+
case 2:
|
|
188
|
+
callMethod = function (a0, a1) {
|
|
189
|
+
return ffiFunc.call(a0, a1);
|
|
190
|
+
};
|
|
191
|
+
break;
|
|
192
|
+
case 3:
|
|
193
|
+
callMethod = function (a0, a1, a2) {
|
|
194
|
+
return ffiFunc.call(a0, a1, a2);
|
|
195
|
+
};
|
|
196
|
+
break;
|
|
197
|
+
case 4:
|
|
198
|
+
callMethod = function (a0, a1, a2, a3) {
|
|
199
|
+
return ffiFunc.call(a0, a1, a2, a3);
|
|
200
|
+
};
|
|
201
|
+
break;
|
|
202
|
+
default:
|
|
203
|
+
// Fallback for many args
|
|
204
|
+
callMethod = function (...args) {
|
|
205
|
+
return ffiFunc.call(...args);
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
} else {
|
|
209
|
+
// SLOW PATH: Has buffer/struct args - need to extract _buffer
|
|
210
|
+
callMethod = function (...args) {
|
|
211
|
+
const processedArgs = [];
|
|
212
|
+
|
|
213
|
+
for (let i = 0; i < args.length; i++) {
|
|
214
|
+
const arg = args[i];
|
|
215
|
+
// Check for _buffer FIRST (handles Proxy-wrapped structs)
|
|
216
|
+
// This avoids calling Buffer.isBuffer() on Proxy which can cause issues
|
|
217
|
+
if (arg && typeof arg === "object") {
|
|
218
|
+
// Check for struct proxy FIRST - accessing _buffer on Proxy is safe
|
|
219
|
+
// but Buffer.isBuffer(proxy) can cause hangs
|
|
220
|
+
if (arg._buffer !== undefined && Buffer.isBuffer(arg._buffer)) {
|
|
221
|
+
// Struct proxy or object with _buffer
|
|
222
|
+
processedArgs.push(arg._buffer);
|
|
223
|
+
} else if (Buffer.isBuffer(arg)) {
|
|
224
|
+
// Already a buffer, use directly
|
|
225
|
+
processedArgs.push(arg);
|
|
226
|
+
} else {
|
|
227
|
+
// Other object, pass as-is
|
|
228
|
+
processedArgs.push(arg);
|
|
229
|
+
}
|
|
225
230
|
} else {
|
|
226
|
-
//
|
|
231
|
+
// Primitive value (number, string, bigint, null, undefined)
|
|
227
232
|
processedArgs.push(arg);
|
|
228
233
|
}
|
|
229
|
-
} else {
|
|
230
|
-
// Primitive value (number, string, bigint, null, undefined)
|
|
231
|
-
processedArgs.push(arg);
|
|
232
234
|
}
|
|
233
|
-
}
|
|
234
235
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
236
|
+
return ffiFunc.call(...processedArgs);
|
|
237
|
+
};
|
|
238
|
+
}
|
|
238
239
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
240
|
+
// Aggiungi metadata come proprietà non-enumerable per non interferire
|
|
241
|
+
Object.defineProperties(callMethod, {
|
|
242
|
+
funcName: { value: name },
|
|
243
|
+
address: { value: ffiFunc.address },
|
|
244
|
+
_ffi: { value: ffiFunc },
|
|
245
|
+
// Esponi errcheck come setter/getter
|
|
246
|
+
errcheck: {
|
|
247
|
+
get() {
|
|
248
|
+
return ffiFunc._errcheck;
|
|
249
|
+
},
|
|
250
|
+
set(callback) {
|
|
251
|
+
ffiFunc._errcheck = callback;
|
|
252
|
+
ffiFunc.setErrcheck(callback);
|
|
253
|
+
},
|
|
254
|
+
enumerable: false,
|
|
255
|
+
configurable: true,
|
|
248
256
|
},
|
|
249
|
-
|
|
250
|
-
ffiFunc._errcheck = callback;
|
|
251
|
-
ffiFunc.setErrcheck(callback);
|
|
252
|
-
},
|
|
253
|
-
enumerable: false,
|
|
254
|
-
configurable: true,
|
|
255
|
-
},
|
|
256
|
-
});
|
|
257
|
+
});
|
|
257
258
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
259
|
+
this._cache.set(cacheKey, callMethod);
|
|
260
|
+
return callMethod;
|
|
261
|
+
}
|
|
261
262
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
263
|
+
/**
|
|
264
|
+
* Ottiene l'indirizzo di un simbolo
|
|
265
|
+
* @param {string} name - Nome del simbolo
|
|
266
|
+
* @returns {BigInt} Indirizzo del simbolo
|
|
267
|
+
*/
|
|
268
|
+
symbol(name) {
|
|
269
|
+
return this._lib.symbol(name);
|
|
270
|
+
}
|
|
270
271
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
272
|
+
/**
|
|
273
|
+
* Crea un callback JS chiamabile da C
|
|
274
|
+
* @param {Function} fn - Funzione JavaScript
|
|
275
|
+
* @param {Function|number|CType} returnType - Tipo di ritorno (SimpleCData class, CType value, o CType)
|
|
276
|
+
* @param {Array<Function|number|CType>} argTypes - Tipi degli argomenti
|
|
277
|
+
* @returns {Object} Oggetto callback con proprietà pointer e metodi
|
|
278
|
+
*/
|
|
279
|
+
callback(fn, returnType, argTypes = []) {
|
|
280
|
+
return this._lib.callback(_toNativeType(returnType, native), _toNativeTypes(argTypes, native), fn);
|
|
281
|
+
}
|
|
281
282
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
283
|
+
/**
|
|
284
|
+
* Chiude la libreria
|
|
285
|
+
*/
|
|
286
|
+
close() {
|
|
287
|
+
this._lib.close();
|
|
288
|
+
this._cache.clear();
|
|
289
|
+
}
|
|
289
290
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
291
|
+
get path() {
|
|
292
|
+
return this._lib.path;
|
|
293
|
+
}
|
|
293
294
|
|
|
294
|
-
|
|
295
|
-
|
|
295
|
+
get loaded() {
|
|
296
|
+
return this._lib.loaded;
|
|
297
|
+
}
|
|
296
298
|
}
|
|
297
|
-
}
|
|
298
299
|
|
|
299
|
-
/**
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
class WinDLL extends CDLL {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
300
|
+
/**
|
|
301
|
+
* WinDLL - come CDLL ma con stdcall di default (per Windows)
|
|
302
|
+
*/
|
|
303
|
+
class WinDLL extends CDLL {
|
|
304
|
+
func(name, returnType, argTypes = [], options = {}) {
|
|
305
|
+
return super.func(name, returnType, argTypes, {
|
|
306
|
+
abi: "stdcall",
|
|
307
|
+
...options,
|
|
308
|
+
});
|
|
309
|
+
}
|
|
308
310
|
}
|
|
309
|
-
}
|
|
310
311
|
|
|
311
312
|
return { FunctionWrapper, CDLL, WinDLL };
|
|
312
313
|
}
|