GeezerVison is showing github.com — lib: add new methods and error codes · nodejs/node@e850ee9
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboard +25Lines changed: 25 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +'use strict'; | | 2 | + | | 3 | +const common = require('../common.js'); | | 4 | +const ffi = require('node:ffi'); | | 5 | + | | 6 | +const bench = 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 | +const getpid = functions.uv_os_getpid; | | 17 | + | | 18 | +function main({ n }) { | | 19 | + bench.start(); | | 20 | + for (let i = 0; i < n; ++i) | | 21 | + getpid(); | | 22 | + bench.end(n); | | 23 | + | | 24 | + lib.close(); | | 25 | +} |
Collapse file Copy file name to clipboardExpand all lines: doc/api/errors.md +18Lines changed: 18 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -1332,6 +1332,24 @@ added: v14.0.0 | 1332 | 1332 | Used when a feature that is not available | 1333 | 1333 | to the current platform which is running Node.js is used. | 1334 | 1334 |
| | 1335 | +<a id="ERR_FFI_CALL_FAILED"></a> | | 1336 | + | | 1337 | +### `ERR_FFI_CALL_FAILED` | | 1338 | + | | 1339 | +A low-level FFI call failed. | | 1340 | + | | 1341 | +<a id="ERR_FFI_INVALID_POINTER"></a> | | 1342 | + | | 1343 | +### `ERR_FFI_INVALID_POINTER` | | 1344 | + | | 1345 | +An invalid pointer was passed to an FFI operation. | | 1346 | + | | 1347 | +<a id="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 | + | 1335 | 1353 | <a id="ERR_FS_CP_DIR_TO_NON_DIR"></a> | 1336 | 1354 |
| 1337 | 1355 | ### `ERR_FS_CP_DIR_TO_NON_DIR` | |
Collapse file Copy file name to clipboardExpand all lines: doc/api/ffi.md +57-2Lines changed: 57 additions & 2 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -165,12 +165,15 @@ const path = `libsqlite3.${suffix}`; | 165 | 165 | added: REPLACEME | 166 | 166 | --> | 167 | 167 |
| 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. | 169 | 170 | * `definitions` {Object} Symbol definitions to resolve immediately. | 170 | 171 | * Returns: {Object} | 171 | 172 |
| 172 | 173 | Loads a dynamic library and resolves the requested function definitions. | 173 | 174 |
| | 175 | +On Windows passing `null` is not supported. | | 176 | + | 174 | 177 | When `definitions` is omitted, `functions` is returned as an empty object until | 175 | 178 | symbols are resolved explicitly. | 176 | 179 |
| @@ -237,10 +240,13 @@ Represents a loaded dynamic library. | 237 | 240 |
| 238 | 241 | ### `new DynamicLibrary(path)` | 239 | 242 |
| 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. | 241 | 245 |
| 242 | 246 | Loads the dynamic library without resolving any functions eagerly. | 243 | 247 |
| | 248 | +On Windows passing `null` is not supported. | | 249 | + | 244 | 250 | ```cjs | 245 | 251 | const { DynamicLibrary } = require('node:ffi'); | 246 | 252 |
| @@ -603,6 +609,55 @@ available storage. This function does not allocate memory on its own. | 603 | 609 |
| 604 | 610 | `buffer` must be a Node.js `Buffer`. | 605 | 611 |
| | 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 | + | 606 | 661 | ## Safety notes | 607 | 662 |
| 608 | 663 | The `node:ffi` module does not track pointer validity, memory ownership, or | |
Collapse file Copy file name to clipboardExpand all lines: lib/ffi.js +47-7Lines changed: 47 additions & 7 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 2 | 2 |
| 3 | 3 | const { | 4 | 4 | ObjectFreeze, | | 5 | + ObjectPrototypeToString, | 5 | 6 | } = primordials; | 6 | 7 | const { Buffer } = require('buffer'); | 7 | 8 | const { emitExperimentalWarning } = require('internal/util'); | | 9 | +const { | | 10 | + isArrayBufferView, | | 11 | +} = require('internal/util/types'); | 8 | 12 | const { | 9 | 13 | codes: { | 10 | 14 | ERR_ACCESS_DENIED, | @@ -32,6 +36,8 @@ const { | 32 | 36 | getUint64, | 33 | 37 | getFloat32, | 34 | 38 | getFloat64, | | 39 | + exportBytes, | | 40 | + getRawPointer, | 35 | 41 | setInt8, | 36 | 42 | setUint8, | 37 | 43 | setInt16, | @@ -114,21 +120,52 @@ function exportString(str, data, len, encoding = 'utf8') { | 114 | 120 | targetBuffer.fill(0, dataLength, dataLength + terminatorSize); | 115 | 121 | } | 116 | 122 |
| 117 | | -function exportBuffer(buffer, data, len) { | | 123 | +function exportBuffer(source, data, len) { | 118 | 124 | checkFFIPermission(); | 119 | 125 |
| 120 | | - if (!Buffer.isBuffer(buffer)) { | 121 | | - throw new ERR_INVALID_ARG_TYPE('buffer', 'Buffer', buffer); | | 126 | + if (!Buffer.isBuffer(source)) { | | 127 | + throw new ERR_INVALID_ARG_TYPE('buffer', 'Buffer', source); | 122 | 128 | } | 123 | 129 |
| 124 | 130 | validateInteger(len, 'len', 0); | 125 | 131 |
| 126 | | - if (len < buffer.length) { | 127 | | - throw new ERR_OUT_OF_RANGE('len', `>= ${buffer.length}`, len); | | 132 | + if (len < source.length) { | | 133 | + throw new ERR_OUT_OF_RANGE('len', `>= ${source.length}`, len); | 128 | 134 | } | 129 | 135 |
| 130 | | - const targetBuffer = toBuffer(data, len, false); | 131 | | - buffer.copy(targetBuffer, 0, 0, buffer.length); | | 136 | + exportBytes(source, data, len); | | 137 | +} | | 138 | + | | 139 | +function exportArrayBuffer(source, data, len) { | | 140 | + checkFFIPermission(); | | 141 | + | | 142 | + if (ObjectPrototypeToString(source) !== '[object ArrayBuffer]') { | | 143 | + throw new ERR_INVALID_ARG_TYPE('arrayBuffer', 'ArrayBuffer', source); | | 144 | + } | | 145 | + | | 146 | + validateInteger(len, 'len', 0); | | 147 | + | | 148 | + if (len < source.byteLength) { | | 149 | + throw new ERR_OUT_OF_RANGE('len', `>= ${source.byteLength}`, len); | | 150 | + } | | 151 | + | | 152 | + exportBytes(source, data, len); | | 153 | +} | | 154 | + | | 155 | +function exportArrayBufferView(source, data, len) { | | 156 | + checkFFIPermission(); | | 157 | + | | 158 | + if (!isArrayBufferView(source)) { | | 159 | + throw new ERR_INVALID_ARG_TYPE('arrayBufferView', 'ArrayBufferView', source); | | 160 | + } | | 161 | + | | 162 | + validateInteger(len, 'len', 0); | | 163 | + | | 164 | + if (len < source.byteLength) { | | 165 | + throw new ERR_OUT_OF_RANGE('len', `>= ${source.byteLength}`, len); | | 166 | + } | | 167 | + | | 168 | + exportBytes(source, data, len); | 132 | 169 | } | 133 | 170 |
| 134 | 171 | const suffix = process.platform === 'win32' ? 'dll' : process.platform === 'darwin' ? 'dylib' : 'so'; | @@ -163,6 +200,8 @@ module.exports = { | 163 | 200 | dlopen, | 164 | 201 | dlclose, | 165 | 202 | dlsym, | | 203 | + exportArrayBuffer, | | 204 | + exportArrayBufferView, | 166 | 205 | exportString, | 167 | 206 | exportBuffer, | 168 | 207 | getInt8, | @@ -175,6 +214,7 @@ module.exports = { | 175 | 214 | getUint64, | 176 | 215 | getFloat32, | 177 | 216 | getFloat64, | | 217 | + getRawPointer, | 178 | 218 | setInt8, | 179 | 219 | setUint8, | 180 | 220 | setInt16, | |
|