node-ctypes 1.2.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 -214
- package/lib/core/types.js +7 -0
- package/lib/index.d.ts +23 -1
- package/lib/index.js +56 -59
- package/lib/memory/pointer.js +243 -25
- 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/primitives.js +1 -1
- package/package.json +1 -1
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,255 +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 {Function|number|CType} returnType - Tipo di ritorno (SimpleCData class, CType value, o CType)
|
|
134
|
-
* @param {Array<Function|number|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(",")}`;
|
|
146
148
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
// =========================================================================
|
|
149
|
+
if (this._cache.has(cacheKey)) {
|
|
150
|
+
return this._cache.get(cacheKey);
|
|
151
|
+
}
|
|
151
152
|
|
|
152
|
-
|
|
153
|
+
const ffiFunc = this._lib.func(name, _toNativeType(returnType, native), _toNativeTypes(argTypes, native), options);
|
|
153
154
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const typeName = t._type;
|
|
159
|
-
return typeName !== native.CType.POINTER && typeName !== native.CType.STRING && typeName !== native.CType.WSTRING;
|
|
160
|
-
}
|
|
161
|
-
return false;
|
|
162
|
-
});
|
|
155
|
+
// =========================================================================
|
|
156
|
+
// OPTIMIZATION: Create specialized wrapper based on argument types
|
|
157
|
+
// For primitive-only args, bypass the processing loop entirely
|
|
158
|
+
// =========================================================================
|
|
163
159
|
|
|
164
|
-
|
|
160
|
+
const argCount = argTypes.length;
|
|
165
161
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
callMethod = function (a0) {
|
|
176
|
-
return ffiFunc.call(a0);
|
|
177
|
-
};
|
|
178
|
-
break;
|
|
179
|
-
case 2:
|
|
180
|
-
callMethod = function (a0, a1) {
|
|
181
|
-
return ffiFunc.call(a0, a1);
|
|
182
|
-
};
|
|
183
|
-
break;
|
|
184
|
-
case 3:
|
|
185
|
-
callMethod = function (a0, a1, a2) {
|
|
186
|
-
return ffiFunc.call(a0, a1, a2);
|
|
187
|
-
};
|
|
188
|
-
break;
|
|
189
|
-
case 4:
|
|
190
|
-
callMethod = function (a0, a1, a2, a3) {
|
|
191
|
-
return ffiFunc.call(a0, a1, a2, a3);
|
|
192
|
-
};
|
|
193
|
-
break;
|
|
194
|
-
default:
|
|
195
|
-
// Fallback for many args
|
|
196
|
-
callMethod = function (...args) {
|
|
197
|
-
return ffiFunc.call(...args);
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
} else {
|
|
201
|
-
// SLOW PATH: Has buffer/struct args - need to extract _buffer
|
|
202
|
-
callMethod = function (...args) {
|
|
203
|
-
const processedArgs = [];
|
|
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
|
+
});
|
|
204
171
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
172
|
+
let callMethod;
|
|
173
|
+
|
|
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
|
+
}
|
|
218
230
|
} else {
|
|
219
|
-
//
|
|
231
|
+
// Primitive value (number, string, bigint, null, undefined)
|
|
220
232
|
processedArgs.push(arg);
|
|
221
233
|
}
|
|
222
|
-
} else {
|
|
223
|
-
// Primitive value (number, string, bigint, null, undefined)
|
|
224
|
-
processedArgs.push(arg);
|
|
225
234
|
}
|
|
226
|
-
}
|
|
227
235
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
236
|
+
return ffiFunc.call(...processedArgs);
|
|
237
|
+
};
|
|
238
|
+
}
|
|
231
239
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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,
|
|
245
256
|
},
|
|
246
|
-
|
|
247
|
-
configurable: true,
|
|
248
|
-
},
|
|
249
|
-
});
|
|
257
|
+
});
|
|
250
258
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
259
|
+
this._cache.set(cacheKey, callMethod);
|
|
260
|
+
return callMethod;
|
|
261
|
+
}
|
|
254
262
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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
|
+
}
|
|
263
271
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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
|
+
}
|
|
274
282
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
283
|
+
/**
|
|
284
|
+
* Chiude la libreria
|
|
285
|
+
*/
|
|
286
|
+
close() {
|
|
287
|
+
this._lib.close();
|
|
288
|
+
this._cache.clear();
|
|
289
|
+
}
|
|
282
290
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
291
|
+
get path() {
|
|
292
|
+
return this._lib.path;
|
|
293
|
+
}
|
|
286
294
|
|
|
287
|
-
|
|
288
|
-
|
|
295
|
+
get loaded() {
|
|
296
|
+
return this._lib.loaded;
|
|
297
|
+
}
|
|
289
298
|
}
|
|
290
|
-
}
|
|
291
299
|
|
|
292
|
-
/**
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
class WinDLL extends CDLL {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
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
|
+
}
|
|
301
310
|
}
|
|
302
|
-
}
|
|
303
311
|
|
|
304
312
|
return { FunctionWrapper, CDLL, WinDLL };
|
|
305
313
|
}
|