Add fast API trampolines for AArch64 and x86_64. IsJitMemorySupported() maps an RW page, writes a ret instruction (0xD65F03C0 AArch64 / 0xC3 x86_64), and mprotects it to RX. A successful RX transition is treated as the support signal. The page is deliberately not executed: this check may run during normal operation (when an FFI function is first created), and executing freshly written code from a capability probe could SIGSEGV/SIGKILL the process on systems that block executable memory. The real trampoline emitter performs the same mprotect at creation time and falls back to libffi when it is rejected. The result is computed once via std::call_once and cached for the lifetime of the process, so concurrent callers never observe a provisional value. Wired into CreateFastFFIMetadata() for early nullptr bail-out when JIT memory is unavailable. Windows stub returns false (no trampolines yet on this branch). IsFastCallEligible() validates at parse time whether a signature can use the fast-call path, covering: - Return and argument type eligibility (numeric, pointer; no structs) - Argument count cap (8, matching V8 fast-call limit) - Per-ABI register pressure limits that mirror the trampoline emitters (AArch64 and x86_64 SysV). Platforms without an emitter, including Win64, are reported ineligible. - Buffer and float args cannot coexist, and buffer args additionally consume an extra GP register slot on both supported ABIs. The arg/arg-name lengths are checked before the per-arg loop so a malformed signature cannot index out of bounds. Returns nullptr from CreateFastFFIMetadata() for ineligible signatures, falling back to libffi. Co-authored-by: Bryan English <bryan@bryanenglish.com> Signed-off-by: Paolo Insogna <paolo@cowtech.it> Signed-off-by: Bryan English <bryan@bryanenglish.com> Assisted-By: OpenAI:GPT-5.5 <openai/gpt-5.5> PR-URL: https://github.com/nodejs/node/pull/63068 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Bryan English <bryan@bryanenglish.com>
738 lines
21 KiB
Markdown
738 lines
21 KiB
Markdown
# FFI
|
|
|
|
<!--introduced_in=v26.1.0-->
|
|
|
|
<!-- YAML
|
|
added: v26.1.0
|
|
-->
|
|
|
|
> Stability: 1 - Experimental
|
|
|
|
<!-- source_link=lib/ffi.js -->
|
|
|
|
The `node:ffi` module provides an experimental foreign function interface for
|
|
loading dynamic libraries and calling native symbols from JavaScript.
|
|
|
|
This API is unsafe. Passing invalid pointers, using an incorrect symbol
|
|
signature, or accessing memory after it has been freed can crash the process
|
|
or corrupt memory.
|
|
|
|
To access it:
|
|
|
|
```mjs
|
|
import ffi from 'node:ffi';
|
|
```
|
|
|
|
```cjs
|
|
const ffi = require('node:ffi');
|
|
```
|
|
|
|
This module is only available under the `node:` scheme in builds with FFI
|
|
support and is gated by the `--experimental-ffi` flag.
|
|
|
|
Building Node.js with `node:ffi` support is available via the bundled `libffi` on
|
|
platforms where `libffi` provides a compatible static backend, or via a
|
|
shared `libffi` using the `--shared-ffi` configure flag.
|
|
The unofficial GN build does not support `node:ffi`.
|
|
|
|
The following targets are not supported by bundled libffi:
|
|
|
|
* `s390x`.
|
|
* `mips`, `mipsel`, and `mips64el` on targets other than FreeBSD, Linux, and
|
|
OpenBSD.
|
|
* `ppc64` on Android, CloudABI, iOS, OpenHarmony, OS/400, Solaris, and Windows.
|
|
|
|
When using the [Permission Model][], FFI APIs are
|
|
restricted unless the [`--allow-ffi`][] flag is provided.
|
|
|
|
## Overview
|
|
|
|
The `node:ffi` module exposes two groups of APIs:
|
|
|
|
* Dynamic library APIs for loading libraries, resolving symbols, and creating
|
|
callable JavaScript wrappers.
|
|
* Raw memory helpers for reading and writing primitive values through pointers,
|
|
converting pointers to JavaScript strings, `Buffer` instances, and
|
|
`ArrayBuffer` instances, and for copying data back into native memory.
|
|
|
|
## Type names
|
|
|
|
FFI signatures use string type names.
|
|
|
|
Supported type names:
|
|
|
|
* `void`
|
|
* `i8`, `int8`
|
|
* `u8`, `uint8`, `bool`, `char`
|
|
* `i16`, `int16`
|
|
* `u16`, `uint16`
|
|
* `i32`, `int32`
|
|
* `u32`, `uint32`
|
|
* `i64`, `int64`
|
|
* `u64`, `uint64`
|
|
* `f32`, `float`
|
|
* `f64`, `double`
|
|
* `pointer`, `ptr`
|
|
* `string`, `str`
|
|
* `buffer`
|
|
* `arraybuffer`
|
|
* `function`
|
|
|
|
These type names are also exposed as constants on `ffi.types`:
|
|
|
|
* `ffi.types.VOID` = `'void'`
|
|
* `ffi.types.POINTER` = `'pointer'`
|
|
* `ffi.types.BUFFER` = `'buffer'`
|
|
* `ffi.types.ARRAY_BUFFER` = `'arraybuffer'`
|
|
* `ffi.types.FUNCTION` = `'function'`
|
|
* `ffi.types.BOOL` = `'bool'`
|
|
* `ffi.types.CHAR` = `'char'`
|
|
* `ffi.types.STRING` = `'string'`
|
|
* `ffi.types.FLOAT` = `'float'`
|
|
* `ffi.types.DOUBLE` = `'double'`
|
|
* `ffi.types.INT_8` = `'int8'`
|
|
* `ffi.types.UINT_8` = `'uint8'`
|
|
* `ffi.types.INT_16` = `'int16'`
|
|
* `ffi.types.UINT_16` = `'uint16'`
|
|
* `ffi.types.INT_32` = `'int32'`
|
|
* `ffi.types.UINT_32` = `'uint32'`
|
|
* `ffi.types.INT_64` = `'int64'`
|
|
* `ffi.types.UINT_64` = `'uint64'`
|
|
* `ffi.types.FLOAT_32` = `'float32'`
|
|
* `ffi.types.FLOAT_64` = `'float64'`
|
|
|
|
Pointer-like types (`pointer`, `string`, `buffer`, `arraybuffer`, and
|
|
`function`) are all passed through the native layer as pointers.
|
|
|
|
When `Buffer`, `ArrayBuffer`, or typed array values are passed as pointer-like
|
|
arguments, Node.js borrows a raw pointer to their backing memory for the
|
|
duration of the native call. The caller must ensure that backing store remains
|
|
valid and stable for the entire call.
|
|
|
|
It is unsupported and dangerous to resize, transfer, detach, or otherwise
|
|
invalidate that backing store while the native call is active, including
|
|
through reentrant JavaScript such as FFI callbacks. Doing so may crash the
|
|
process, produce incorrect output, or corrupt memory.
|
|
|
|
The `char` type follows the platform C ABI. On platforms where plain C `char`
|
|
is signed it behaves like `i8`; otherwise it behaves like `u8`.
|
|
|
|
The `bool` type is marshaled as an 8-bit unsigned integer. Pass numeric values
|
|
such as `0` and `1`; JavaScript `true` and `false` are not accepted.
|
|
|
|
On optimized Fast FFI calls, `pointer`, `ptr`, and `function` parameters accept
|
|
raw pointer `bigint` values. For pointer-like parameters, `null`, `undefined`,
|
|
strings, `Buffer`, typed array, `DataView`, and `ArrayBuffer` values are
|
|
converted on the JavaScript side before calling the optimized native wrapper.
|
|
|
|
Optimized Fast FFI calls support at most 8 function arguments. Functions with
|
|
more than 7 arguments use the generic FFI call path instead.
|
|
|
|
## Signature objects
|
|
|
|
Functions and callbacks are described with signature objects.
|
|
|
|
Signature objects may contain the following properties, both of which are
|
|
optional:
|
|
|
|
* `return` {string} A [type name][type names] specifying the return type of the
|
|
function or callback. **Default:** `'void'`.
|
|
* `arguments` {string\[]} An array of [type names][] specifying the argument
|
|
type list of the function or callback. **Default:** `[]`.
|
|
|
|
```js
|
|
const signature = {
|
|
return: 'i32',
|
|
arguments: ['i32', 'i32'],
|
|
};
|
|
```
|
|
|
|
## `ffi.suffix`
|
|
|
|
<!-- YAML
|
|
added: v26.1.0
|
|
-->
|
|
|
|
* {string}
|
|
|
|
The native shared library suffix for the current platform:
|
|
|
|
* `'dylib'` on macOS
|
|
* `'so'` on Unix-like platforms
|
|
* `'dll'` on Windows
|
|
|
|
This can be used to build portable library paths:
|
|
|
|
```cjs
|
|
const { suffix } = require('node:ffi');
|
|
|
|
const path = `libsqlite3.${suffix}`;
|
|
```
|
|
|
|
## `ffi.dlopen(path[, definitions])`
|
|
|
|
<!-- YAML
|
|
added: v26.1.0
|
|
-->
|
|
|
|
* `path` {string|null} Path to a dynamic library, or `null` to resolve symbols
|
|
from the current process image.
|
|
* `definitions` {Object} Symbol definitions to resolve immediately.
|
|
* Returns: {Object}
|
|
|
|
Loads a dynamic library and resolves the requested function definitions.
|
|
|
|
On Windows passing `null` is not supported.
|
|
|
|
When `definitions` is omitted, `functions` is returned as an empty object until
|
|
symbols are resolved explicitly.
|
|
|
|
The returned object contains:
|
|
|
|
* `lib` {DynamicLibrary} The loaded library handle.
|
|
* `functions` {Object} Callable wrappers for the requested symbols.
|
|
|
|
The returned object also implements the explicit resource management protocol,
|
|
so it can be used with the [`using`][] declaration. Disposing the returned
|
|
object closes the library handle.
|
|
|
|
```mjs
|
|
import { dlopen } from 'node:ffi';
|
|
|
|
{
|
|
using handle = dlopen('./mylib.so', {
|
|
add_i32: { arguments: ['i32', 'i32'], return: 'i32' },
|
|
});
|
|
console.log(handle.functions.add_i32(20, 22));
|
|
} // handle.lib.close() is invoked automatically here.
|
|
```
|
|
|
|
```mjs
|
|
import { dlopen } from 'node:ffi';
|
|
|
|
const { lib, functions } = dlopen('./mylib.so', {
|
|
add_i32: { arguments: ['i32', 'i32'], return: 'i32' },
|
|
string_length: { arguments: ['pointer'], return: 'u64' },
|
|
});
|
|
|
|
console.log(functions.add_i32(20, 22));
|
|
```
|
|
|
|
```cjs
|
|
const { dlopen } = require('node:ffi');
|
|
|
|
const { lib, functions } = dlopen('./mylib.so', {
|
|
add_i32: { arguments: ['i32', 'i32'], return: 'i32' },
|
|
string_length: { arguments: ['pointer'], return: 'u64' },
|
|
});
|
|
|
|
console.log(functions.add_i32(20, 22));
|
|
```
|
|
|
|
## `ffi.dlclose(handle)`
|
|
|
|
<!-- YAML
|
|
added: v26.1.0
|
|
-->
|
|
|
|
* `handle` {DynamicLibrary}
|
|
|
|
Closes a dynamic library.
|
|
|
|
This is equivalent to calling `handle.close()`.
|
|
|
|
## `ffi.dlsym(handle, symbol)`
|
|
|
|
<!-- YAML
|
|
added: v26.1.0
|
|
-->
|
|
|
|
* `handle` {DynamicLibrary}
|
|
* `symbol` {string}
|
|
* Returns: {bigint}
|
|
|
|
Resolves a symbol address from a loaded library.
|
|
|
|
This is equivalent to calling `handle.getSymbol(symbol)`.
|
|
|
|
## Class: `DynamicLibrary`
|
|
|
|
<!-- YAML
|
|
added: v26.1.0
|
|
-->
|
|
|
|
Represents a loaded dynamic library.
|
|
|
|
### `new DynamicLibrary(path)`
|
|
|
|
* `path` {string|null} Path to a dynamic library, or `null` to resolve symbols
|
|
from the current process image.
|
|
|
|
Loads the dynamic library without resolving any functions eagerly.
|
|
|
|
On Windows passing `null` is not supported.
|
|
|
|
```cjs
|
|
const { DynamicLibrary } = require('node:ffi');
|
|
|
|
const lib = new DynamicLibrary('./mylib.so');
|
|
```
|
|
|
|
### `library.path`
|
|
|
|
* {string}
|
|
|
|
The path used to load the library.
|
|
|
|
### `library.functions`
|
|
|
|
* {Object}
|
|
|
|
An object containing previously resolved function wrappers.
|
|
|
|
### `library.symbols`
|
|
|
|
* {Object}
|
|
|
|
An object containing previously resolved symbol addresses as `bigint` values.
|
|
|
|
### `library.close()`
|
|
|
|
Closes the library handle.
|
|
|
|
`DynamicLibrary` implements the explicit resource management protocol, so a
|
|
library instance can be managed with the [`using`][] declaration. Leaving the
|
|
enclosing scope invokes `library.close()` automatically.
|
|
|
|
```mjs
|
|
import { DynamicLibrary } from 'node:ffi';
|
|
|
|
{
|
|
using lib = new DynamicLibrary('./mylib.so');
|
|
// Use `lib` here; `lib.close()` is called when the block exits.
|
|
}
|
|
```
|
|
|
|
Calling `library.close()` (or disposing the library) more than once is a no-op.
|
|
|
|
After a library has been closed:
|
|
|
|
* Resolved function wrappers become invalid.
|
|
* Further symbol and function resolution throws.
|
|
* Registered callbacks are invalidated.
|
|
|
|
Closing a library does not make previously exported callback pointers safe to
|
|
reuse. Node.js does not track or revoke callback pointers that have already
|
|
been handed to native code.
|
|
|
|
If native code still holds a callback pointer after `library.close()` or after
|
|
`library.unregisterCallback(pointer)`, invoking that pointer has undefined
|
|
behavior, is not allowed, and is dangerous: it can crash the process, produce
|
|
incorrect output, or corrupt memory. Native code must stop using callback
|
|
addresses before the library is closed or before the callback is unregistered.
|
|
|
|
Calling `library.close()` from one of the library's active callbacks is
|
|
unsupported and dangerous. The callback must return before the library is
|
|
closed.
|
|
|
|
### `library[Symbol.dispose]()`
|
|
|
|
<!-- YAML
|
|
added: v26.1.0
|
|
-->
|
|
|
|
Calls `library.close()`. This allows `DynamicLibrary` instances to be used with
|
|
the [`using`][] declaration for automatic cleanup when the enclosing scope
|
|
exits. It is a no-op on a library that has already been closed.
|
|
|
|
### `library.getFunction(name, signature)`
|
|
|
|
* `name` {string}
|
|
* `signature` {Object}
|
|
* Returns: {Function}
|
|
|
|
Resolves a symbol and returns a callable JavaScript wrapper.
|
|
|
|
The returned function has a `.pointer` property containing the native function
|
|
address as a `bigint`.
|
|
|
|
If the same symbol has already been resolved, requesting it again with a
|
|
different signature throws.
|
|
|
|
```cjs
|
|
const { DynamicLibrary } = require('node:ffi');
|
|
|
|
const lib = new DynamicLibrary('./mylib.so');
|
|
const add = lib.getFunction('add_i32', {
|
|
arguments: ['i32', 'i32'],
|
|
return: 'i32',
|
|
});
|
|
|
|
console.log(add(20, 22));
|
|
console.log(add.pointer);
|
|
```
|
|
|
|
### `library.getFunctions([definitions])`
|
|
|
|
* `definitions` {Object}
|
|
* Returns: {Object}
|
|
|
|
When `definitions` is provided, resolves each named symbol and returns an
|
|
object containing callable wrappers.
|
|
|
|
When `definitions` is omitted, returns wrappers for all functions that have
|
|
already been resolved on the library.
|
|
|
|
### `library.getSymbol(name)`
|
|
|
|
* `name` {string}
|
|
* Returns: {bigint}
|
|
|
|
Resolves a symbol and returns its native address as a `bigint`.
|
|
|
|
### `library.getSymbols()`
|
|
|
|
* Returns: {Object}
|
|
|
|
Returns an object containing all previously resolved symbol addresses.
|
|
|
|
### `library.registerCallback([signature,] callback)`
|
|
|
|
* `signature` {Object}
|
|
* `callback` {Function}
|
|
* Returns: {bigint}
|
|
|
|
Creates a native callback pointer backed by a JavaScript function.
|
|
|
|
When `signature` is omitted, the callback uses a default `void ()` signature.
|
|
|
|
The return value is the callback pointer address as a `bigint`. It can be
|
|
passed to native functions expecting a callback pointer.
|
|
|
|
```cjs
|
|
const { DynamicLibrary } = require('node:ffi');
|
|
|
|
const lib = new DynamicLibrary('./mylib.so');
|
|
|
|
const callback = lib.registerCallback(
|
|
{ arguments: ['i32'], return: 'i32' },
|
|
(value) => value * 2,
|
|
);
|
|
```
|
|
|
|
Callbacks are subject to the following restrictions:
|
|
|
|
* They must be invoked on the same system thread where they were created.
|
|
* They must not throw exceptions.
|
|
* They must not return promises.
|
|
* They must return a value compatible with the declared return type.
|
|
* They must not call `library.close()` on their owning library while running.
|
|
* They must not unregister themselves while running.
|
|
|
|
Closing the owning library or unregistering the currently executing callback
|
|
from inside the callback is unsupported and dangerous. Doing so may crash the
|
|
process, produce incorrect output, or corrupt memory.
|
|
|
|
### `library.unregisterCallback(pointer)`
|
|
|
|
* `pointer` {bigint}
|
|
|
|
Releases a callback previously created with `library.registerCallback()`.
|
|
|
|
Calling `library.unregisterCallback(pointer)` for a callback that is currently
|
|
executing is unsupported and dangerous. The callback must return before it is
|
|
unregistered.
|
|
|
|
After `library.unregisterCallback(pointer)` returns, invoking that callback
|
|
pointer from native code has undefined behavior, is not allowed, and is
|
|
dangerous: it can crash the process, produce incorrect output, or corrupt
|
|
memory.
|
|
|
|
### `library.refCallback(pointer)`
|
|
|
|
* `pointer` {bigint}
|
|
|
|
Keeps the callback strongly referenced by JavaScript.
|
|
|
|
### `library.unrefCallback(pointer)`
|
|
|
|
* `pointer` {bigint}
|
|
|
|
Allows the callback to become weakly referenced by JavaScript.
|
|
|
|
If the callback function is later garbage collected, subsequent native
|
|
invocations become a no-op. Non-void return values are zero-initialized before
|
|
returning to native code.
|
|
|
|
## Calling native functions
|
|
|
|
Argument conversion depends on the declared FFI type.
|
|
|
|
For 8-, 16-, and 32-bit integer types and for floating-point types, pass
|
|
JavaScript `number` values that match the declared type.
|
|
|
|
For 64-bit integer types (`i64` and `u64`), pass JavaScript `bigint` values.
|
|
|
|
For pointer-like arguments:
|
|
|
|
* `null` and `undefined` are passed as null pointers.
|
|
* `string` values are copied to temporary NUL-terminated UTF-8 strings for the
|
|
duration of the call.
|
|
* `Buffer`, typed arrays, and `DataView` instances pass a pointer to their
|
|
backing memory.
|
|
* `ArrayBuffer` passes a pointer to its backing memory.
|
|
* `bigint` values are passed as raw pointer addresses.
|
|
|
|
Pointer return values are exposed as `bigint` addresses.
|
|
|
|
## Primitive memory access helpers
|
|
|
|
The following helpers read and write primitive values at a native pointer,
|
|
optionally with a byte offset:
|
|
|
|
* `ffi.getInt8(pointer[, offset])`
|
|
* `ffi.getUint8(pointer[, offset])`
|
|
* `ffi.getInt16(pointer[, offset])`
|
|
* `ffi.getUint16(pointer[, offset])`
|
|
* `ffi.getInt32(pointer[, offset])`
|
|
* `ffi.getUint32(pointer[, offset])`
|
|
* `ffi.getInt64(pointer[, offset])`
|
|
* `ffi.getUint64(pointer[, offset])`
|
|
* `ffi.getFloat32(pointer[, offset])`
|
|
* `ffi.getFloat64(pointer[, offset])`
|
|
* `ffi.setInt8(pointer, offset, value)`
|
|
* `ffi.setUint8(pointer, offset, value)`
|
|
* `ffi.setInt16(pointer, offset, value)`
|
|
* `ffi.setUint16(pointer, offset, value)`
|
|
* `ffi.setInt32(pointer, offset, value)`
|
|
* `ffi.setUint32(pointer, offset, value)`
|
|
* `ffi.setInt64(pointer, offset, value)`
|
|
* `ffi.setUint64(pointer, offset, value)`
|
|
* `ffi.setFloat32(pointer, offset, value)`
|
|
* `ffi.setFloat64(pointer, offset, value)`
|
|
|
|
These helpers perform direct memory reads and writes. `pointer` must be a
|
|
`bigint` referring to valid readable or writable native memory. `offset`, when
|
|
provided, is interpreted as a byte offset from `pointer`.
|
|
|
|
The getter helpers return JavaScript `number` values for 8-, 16-, and 32-bit
|
|
integer types and for floating-point types. They return `bigint` values for
|
|
64-bit integer types.
|
|
|
|
The setter helpers require an explicit byte offset and validate the supplied
|
|
JavaScript value against the target native type before writing it into memory.
|
|
For `setInt64()` and `setUint64()`, `bigint` values are accepted directly;
|
|
numeric inputs must be integers within JavaScript's safe integer range.
|
|
|
|
```cjs
|
|
const {
|
|
getInt32,
|
|
setInt32,
|
|
} = require('node:ffi');
|
|
|
|
setInt32(ptr, 0, 42);
|
|
console.log(getInt32(ptr, 0));
|
|
```
|
|
|
|
Like the other raw memory helpers in this module, these APIs do not track
|
|
ownership, bounds, or lifetime. Passing an invalid pointer, using the wrong
|
|
offset, or writing through a stale pointer can corrupt memory or crash the
|
|
process.
|
|
|
|
## `ffi.toString(pointer)`
|
|
|
|
<!-- YAML
|
|
added: v26.1.0
|
|
-->
|
|
|
|
* `pointer` {bigint}
|
|
* Returns: {string|null}
|
|
|
|
Reads a NUL-terminated UTF-8 string from native memory.
|
|
|
|
If `pointer` is `0n`, `null` is returned.
|
|
|
|
This function does not validate that `pointer` refers to readable memory or
|
|
that the pointed-to data is terminated with `\0`. Passing an invalid pointer,
|
|
a pointer to freed memory, or a pointer to bytes without a terminating NUL can
|
|
read unrelated memory, crash the process, or produce truncated or garbled
|
|
output.
|
|
|
|
```cjs
|
|
const { toString } = require('node:ffi');
|
|
|
|
const value = toString(ptr);
|
|
```
|
|
|
|
## `ffi.toBuffer(pointer, length[, copy])`
|
|
|
|
<!-- YAML
|
|
added: v26.1.0
|
|
-->
|
|
|
|
* `pointer` {bigint}
|
|
* `length` {number}
|
|
* `copy` {boolean} When `false`, creates a zero-copy view. **Default:** `true`.
|
|
* Returns: {Buffer}
|
|
|
|
Creates a `Buffer` from native memory.
|
|
|
|
When `copy` is `true`, the returned `Buffer` owns its own copied memory.
|
|
When `copy` is `false`, the returned `Buffer` references the original native
|
|
memory directly.
|
|
|
|
Using `copy: false` is a zero-copy escape hatch. The returned `Buffer` is a
|
|
writable view onto foreign memory, so writes in JavaScript update the original
|
|
native memory directly. The caller must guarantee that:
|
|
|
|
* `pointer` remains valid for the entire lifetime of the returned `Buffer`.
|
|
* `length` stays within the allocated native region.
|
|
* no native code frees or repurposes that memory while JavaScript still uses
|
|
the `Buffer`.
|
|
* Memory protection is observed. For example, read-only memory pages must not
|
|
be written to.
|
|
|
|
If these guarantees are not met, reading or writing the `Buffer` can corrupt
|
|
memory or crash the process.
|
|
|
|
## `ffi.toArrayBuffer(pointer, length[, copy])`
|
|
|
|
<!-- YAML
|
|
added: v26.1.0
|
|
-->
|
|
|
|
* `pointer` {bigint}
|
|
* `length` {number}
|
|
* `copy` {boolean} When `false`, creates a zero-copy view. **Default:** `true`.
|
|
* Returns: {ArrayBuffer}
|
|
|
|
Creates an `ArrayBuffer` from native memory.
|
|
|
|
When `copy` is `true`, the returned `ArrayBuffer` contains copied bytes.
|
|
When `copy` is `false`, the returned `ArrayBuffer` references the original
|
|
native memory directly.
|
|
|
|
The same lifetime and bounds requirements described for
|
|
[`ffi.toBuffer(pointer, length, copy)`][] apply
|
|
here. With `copy: false`, the
|
|
returned `ArrayBuffer` is a zero-copy view of foreign memory and is only safe
|
|
while that memory remains allocated, unchanged in layout, and valid for the
|
|
entire exposed range.
|
|
|
|
## `ffi.exportString(string, pointer, length[, encoding])`
|
|
|
|
<!-- YAML
|
|
added: v26.1.0
|
|
-->
|
|
|
|
* `string` {string}
|
|
* `pointer` {bigint}
|
|
* `length` {number}
|
|
* `encoding` {string} **Default:** `'utf8'`.
|
|
|
|
Copies a JavaScript string into native memory and appends a trailing NUL
|
|
terminator.
|
|
|
|
`length` must be large enough to hold the full encoded string plus the trailing
|
|
NUL terminator. For UTF-16 and UCS-2 encodings, the trailing terminator uses
|
|
two zero bytes.
|
|
|
|
`pointer` must refer to writable native memory with at least `length` bytes of
|
|
available storage. This function does not allocate memory on its own.
|
|
|
|
`string` must be a JavaScript string. `encoding` must be a string.
|
|
|
|
## `ffi.exportBuffer(buffer, pointer, length)`
|
|
|
|
<!-- YAML
|
|
added: v26.1.0
|
|
-->
|
|
|
|
* `buffer` {Buffer}
|
|
* `pointer` {bigint}
|
|
* `length` {number}
|
|
|
|
Copies bytes from a `Buffer` into native memory.
|
|
|
|
`length` must be at least `buffer.length`.
|
|
|
|
`pointer` must refer to writable native memory with at least `length` bytes of
|
|
available storage. This function does not allocate memory on its own.
|
|
|
|
`buffer` must be a Node.js `Buffer`.
|
|
|
|
## `ffi.exportArrayBuffer(arrayBuffer, pointer, length)`
|
|
|
|
<!-- YAML
|
|
added: v26.1.0
|
|
-->
|
|
|
|
* `arrayBuffer` {ArrayBuffer}
|
|
* `pointer` {bigint}
|
|
* `length` {number}
|
|
|
|
Copies bytes from an `ArrayBuffer` into native memory.
|
|
|
|
`length` must be at least `arrayBuffer.byteLength`.
|
|
|
|
`pointer` must refer to writable native memory with at least `length` bytes of
|
|
available storage. This function does not allocate memory on its own.
|
|
|
|
## `ffi.exportArrayBufferView(arrayBufferView, pointer, length)`
|
|
|
|
<!-- YAML
|
|
added: v26.1.0
|
|
-->
|
|
|
|
* `arrayBufferView` {ArrayBufferView}
|
|
* `pointer` {bigint}
|
|
* `length` {number}
|
|
|
|
Copies bytes from an `ArrayBufferView` into native memory.
|
|
|
|
`length` must be at least `arrayBufferView.byteLength`.
|
|
|
|
`pointer` must refer to writable native memory with at least `length` bytes of
|
|
available storage. This function does not allocate memory on its own.
|
|
|
|
## `ffi.getRawPointer(source)`
|
|
|
|
<!-- YAML
|
|
added: v26.1.0
|
|
-->
|
|
|
|
* `source` {Buffer|ArrayBuffer|ArrayBufferView}
|
|
* Returns: {bigint}
|
|
|
|
Returns the raw memory address of JavaScript-managed byte storage.
|
|
|
|
This is unsafe and dangerous. The returned pointer can become invalid if the
|
|
underlying memory is detached, resized, transferred, or otherwise invalidated.
|
|
Using stale pointers can cause memory corruption or process crashes.
|
|
|
|
## Safety notes
|
|
|
|
The `node:ffi` module does not track pointer validity, memory ownership, or
|
|
native object lifetimes.
|
|
|
|
In particular:
|
|
|
|
* Do not read from or write to freed memory.
|
|
* Do not use zero-copy views after the native memory has been released.
|
|
* Do not declare incorrect signatures for native symbols.
|
|
* Do not unregister callbacks while native code may still call them.
|
|
* Do not call callback pointers after `library.close()` or
|
|
`library.unregisterCallback(pointer)`.
|
|
* Assume undefined callback behavior can crash the process, produce incorrect
|
|
output, or corrupt memory.
|
|
* Do not assume pointer return values imply ownership; whether the caller must
|
|
free the returned address depends entirely on the native API.
|
|
|
|
As a general rule, prefer copied values unless zero-copy access is required,
|
|
and keep callback and pointer lifetimes explicit on the native side.
|
|
|
|
[Permission Model]: permissions.md#permission-model
|
|
[`--allow-ffi`]: cli.md#--allow-ffi
|
|
[`ffi.toBuffer(pointer, length, copy)`]: #ffitobufferpointer-length-copy
|
|
[`using`]: https://tc39.es/proposal-explicit-resource-management/#sec-using-declarations
|
|
[type names]: #type-names
|