Skip to content

Commit e850ee9

Browse files
ShogunPandaaduh95
authored andcommitted
lib: add new methods and error codes
Signed-off-by: Paolo Insogna <paolo@cowtech.it> PR-URL: #62762 Reviewed-By: Xuguang Mei <meixuguang@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Bryan English <bryan@bryanenglish.com>
1 parent 98f9bec commit e850ee9

13 files changed

Lines changed: 459 additions & 117 deletions

File tree

‎benchmark/ffi/getpid.js‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
'use strict';
2+
3+
constcommon=require('../common.js');
4+
constffi=require('node:ffi');
5+
6+
constbench=common.createBenchmark(main,{
7+
n: [1e7],
8+
},{
9+
flags: ['--experimental-ffi'],
10+
});
11+
12+
const{ lib, functions }=ffi.dlopen(null,{
13+
uv_os_getpid: {result: 'i32',parameters: []},
14+
});
15+
16+
constgetpid=functions.uv_os_getpid;
17+
18+
functionmain({ n }){
19+
bench.start();
20+
for(leti=0;i<n;++i)
21+
getpid();
22+
bench.end(n);
23+
24+
lib.close();
25+
}

‎doc/api/errors.md‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,6 +1332,24 @@ added: v14.0.0
13321332
Used when a feature that is not available
13331333
to the current platform which is running Node.js is used.
13341334

1335+
<aid="ERR_FFI_CALL_FAILED"></a>
1336+
1337+
### `ERR_FFI_CALL_FAILED`
1338+
1339+
A low-level FFI call failed.
1340+
1341+
<aid="ERR_FFI_INVALID_POINTER"></a>
1342+
1343+
### `ERR_FFI_INVALID_POINTER`
1344+
1345+
An invalid pointer was passed to an FFI operation.
1346+
1347+
<aid="ERR_FFI_LIBRARY_CLOSED"></a>
1348+
1349+
### `ERR_FFI_LIBRARY_CLOSED`
1350+
1351+
An operation was attempted on an FFI dynamic library after it was closed.
1352+
13351353
<aid="ERR_FS_CP_DIR_TO_NON_DIR"></a>
13361354

13371355
### `ERR_FS_CP_DIR_TO_NON_DIR`

‎doc/api/ffi.md‎

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,15 @@ const path = `libsqlite3.${suffix}`;
165165
added: REPLACEME
166166
-->
167167

168-
*`path` {string} Path to a dynamic library.
168+
*`path` {string|null} Path to a dynamic library, or `null` to resolve symbols
169+
from the current process image.
169170
*`definitions` {Object} Symbol definitions to resolve immediately.
170171
* Returns: {Object}
171172

172173
Loads a dynamic library and resolves the requested function definitions.
173174

175+
On Windows passing `null` is not supported.
176+
174177
When `definitions` is omitted, `functions` is returned as an empty object until
175178
symbols are resolved explicitly.
176179

@@ -237,10 +240,13 @@ Represents a loaded dynamic library.
237240

238241
### `new DynamicLibrary(path)`
239242

240-
*`path` {string} Path to a dynamic library.
243+
*`path` {string|null} Path to a dynamic library, or `null` to resolve symbols
244+
from the current process image.
241245

242246
Loads the dynamic library without resolving any functions eagerly.
243247

248+
On Windows passing `null` is not supported.
249+
244250
```cjs
245251
const { DynamicLibrary } =require('node:ffi');
246252

@@ -603,6 +609,55 @@ available storage. This function does not allocate memory on its own.
603609

604610
`buffer` must be a Node.js `Buffer`.
605611

612+
## `ffi.exportArrayBuffer(arrayBuffer, pointer, length)`
613+
614+
<!-- YAML
615+
added: REPLACEME
616+
-->
617+
618+
*`arrayBuffer` {ArrayBuffer}
619+
*`pointer` {bigint}
620+
*`length` {number}
621+
622+
Copies bytes from an `ArrayBuffer` into native memory.
623+
624+
`length` must be at least `arrayBuffer.byteLength`.
625+
626+
`pointer` must refer to writable native memory with at least `length` bytes of
627+
available storage. This function does not allocate memory on its own.
628+
629+
## `ffi.exportArrayBufferView(arrayBufferView, pointer, length)`
630+
631+
<!-- YAML
632+
added: REPLACEME
633+
-->
634+
635+
*`arrayBufferView` {ArrayBufferView}
636+
*`pointer` {bigint}
637+
*`length` {number}
638+
639+
Copies bytes from an `ArrayBufferView` into native memory.
640+
641+
`length` must be at least `arrayBufferView.byteLength`.
642+
643+
`pointer` must refer to writable native memory with at least `length` bytes of
644+
available storage. This function does not allocate memory on its own.
645+
646+
## `ffi.getRawPointer(source)`
647+
648+
<!-- YAML
649+
added: REPLACEME
650+
-->
651+
652+
*`source` {Buffer|ArrayBuffer|ArrayBufferView}
653+
* Returns: {bigint}
654+
655+
Returns the raw memory address of JavaScript-managed byte storage.
656+
657+
This is unsafe and dangerous. The returned pointer can become invalid if the
658+
underlying memory is detached, resized, transferred, or otherwise invalidated.
659+
Using stale pointers can cause memory corruption or process crashes.
660+
606661
## Safety notes
607662

608663
The `node:ffi` module does not track pointer validity, memory ownership, or

‎lib/ffi.js‎

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22

33
const{
44
ObjectFreeze,
5+
ObjectPrototypeToString,
56
}=primordials;
67
const{ Buffer }=require('buffer');
78
const{ emitExperimentalWarning }=require('internal/util');
9+
const{
10+
isArrayBufferView,
11+
}=require('internal/util/types');
812
const{
913
codes: {
1014
ERR_ACCESS_DENIED,
@@ -32,6 +36,8 @@ const {
3236
getUint64,
3337
getFloat32,
3438
getFloat64,
39+
exportBytes,
40+
getRawPointer,
3541
setInt8,
3642
setUint8,
3743
setInt16,
@@ -114,21 +120,52 @@ function exportString(str, data, len, encoding = 'utf8') {
114120
targetBuffer.fill(0,dataLength,dataLength+terminatorSize);
115121
}
116122

117-
functionexportBuffer(buffer,data,len){
123+
functionexportBuffer(source,data,len){
118124
checkFFIPermission();
119125

120-
if(!Buffer.isBuffer(buffer)){
121-
thrownewERR_INVALID_ARG_TYPE('buffer','Buffer',buffer);
126+
if(!Buffer.isBuffer(source)){
127+
thrownewERR_INVALID_ARG_TYPE('buffer','Buffer',source);
122128
}
123129

124130
validateInteger(len,'len',0);
125131

126-
if(len<buffer.length){
127-
thrownewERR_OUT_OF_RANGE('len',`>= ${buffer.length}`,len);
132+
if(len<source.length){
133+
thrownewERR_OUT_OF_RANGE('len',`>= ${source.length}`,len);
128134
}
129135

130-
consttargetBuffer=toBuffer(data,len,false);
131-
buffer.copy(targetBuffer,0,0,buffer.length);
136+
exportBytes(source,data,len);
137+
}
138+
139+
functionexportArrayBuffer(source,data,len){
140+
checkFFIPermission();
141+
142+
if(ObjectPrototypeToString(source)!=='[object ArrayBuffer]'){
143+
thrownewERR_INVALID_ARG_TYPE('arrayBuffer','ArrayBuffer',source);
144+
}
145+
146+
validateInteger(len,'len',0);
147+
148+
if(len<source.byteLength){
149+
thrownewERR_OUT_OF_RANGE('len',`>= ${source.byteLength}`,len);
150+
}
151+
152+
exportBytes(source,data,len);
153+
}
154+
155+
functionexportArrayBufferView(source,data,len){
156+
checkFFIPermission();
157+
158+
if(!isArrayBufferView(source)){
159+
thrownewERR_INVALID_ARG_TYPE('arrayBufferView','ArrayBufferView',source);
160+
}
161+
162+
validateInteger(len,'len',0);
163+
164+
if(len<source.byteLength){
165+
thrownewERR_OUT_OF_RANGE('len',`>= ${source.byteLength}`,len);
166+
}
167+
168+
exportBytes(source,data,len);
132169
}
133170

134171
constsuffix=process.platform==='win32' ? 'dll' : process.platform==='darwin' ? 'dylib' : 'so';
@@ -163,6 +200,8 @@ module.exports = {
163200
dlopen,
164201
dlclose,
165202
dlsym,
203+
exportArrayBuffer,
204+
exportArrayBufferView,
166205
exportString,
167206
exportBuffer,
168207
getInt8,
@@ -175,6 +214,7 @@ module.exports = {
175214
getUint64,
176215
getFloat32,
177216
getFloat64,
217+
getRawPointer,
178218
setInt8,
179219
setUint8,
180220
setInt16,

0 commit comments

Comments
 (0)