What is the problem this feature will solve?
node:ffi can make a callable function only from a symbol name (dlopen(), library.getFunction(name, signature)). Many native APIs give you a function pointer instead of an exported symbol, and there is no way to call it:
- COM interfaces on Windows: methods are vtable slots (
IUnknown::Release, etc.).
- Loader functions:
vkGetInstanceProcAddr, eglGetProcAddress, and wglGetProcAddress return pointers to functions that are not exported.
- Structs of function pointers: plugin APIs,
JNIEnv, etc.
- Objective-C
IMP from method_getImplementation().
node:ffi already gives the address as a bigint in all these cases (getUint64(), library.getSymbol(), fn.pointer), and registerCallback() goes from a JavaScript function to a pointer. Only the other direction, pointer to callable, is missing. library.getFunction() rejects a bigint with ERR_INVALID_ARG_TYPE: Function name must be a string (Node.js 26.9.0).
For COM, the only workaround is to call DispCallFunc from oleaut32.dll and let it call the vtable slot. For the other cases, you must compile a C shim, which is what node:ffi is meant to avoid.
What is the feature you are proposing to solve the problem?
A function that takes an address and a signature, and returnss the same kind of wrapper as getFunction():
import {getUint64, toFunction} from 'node:ffi';
// `object` is a COM interface pointer, for example from `CoCreateInstance`.
const vtable = getUint64(object);
const release = toFunction(getUint64(vtable, 2 * 8), {
arguments: ['pointer'],
return: 'uint32',
});
release(object);
The name follows toString(), toBuffer(), and toArrayBuffer(), which also turn a pointer into a JavaScript value. Like them, it does not track lifetime. The caller must make sure the address stays valid.
The internals seem to support this already: PrepareFunction() resolves the name with uv_dlsym() and stores the ptr in the FFIFunction that CreateFunction() uses. A pointer entry point would supply ptr directly, and libffi's ffi_call() accepts any address.
Other FFIs have this:
- Python ctypes:
CFUNCTYPE(restype, *argtypes)(address)
- Deno:
new Deno.UnsafeFnPointer(pointer, definition)
- Bun:
new CFunction({ptr, args, returns})
- ffi-napi:
ffi.ForeignFunction(pointer, returnType, argumentTypes)
The earlier node:ffi proposal (#57761) also had UnsafeFnPointer.
What alternatives have you considered?
- Accept a
bigint in library.getFunction(). But the function does not belong to the library, and getFunction() caches by symbol name.
DispCallFunc from oleaut32.dll. It works only on Windows and only for COM-style calls.
- A small compiled C shim that calls the pointer. This needs a native build step, which is what
node:ffi removes.
What is the problem this feature will solve?
node:ffican make a callable function only from a symbol name (dlopen(),library.getFunction(name, signature)). Many native APIs give you a function pointer instead of an exported symbol, and there is no way to call it:IUnknown::Release, etc.).vkGetInstanceProcAddr,eglGetProcAddress, andwglGetProcAddressreturn pointers to functions that are not exported.JNIEnv, etc.IMPfrommethod_getImplementation().node:ffialready gives the address as abigintin all these cases (getUint64(),library.getSymbol(),fn.pointer), andregisterCallback()goes from a JavaScript function to a pointer. Only the other direction, pointer to callable, is missing.library.getFunction()rejects abigintwithERR_INVALID_ARG_TYPE: Function name must be a string(Node.js 26.9.0).For COM, the only workaround is to call
DispCallFuncfromoleaut32.dlland let it call the vtable slot. For the other cases, you must compile a C shim, which is whatnode:ffiis meant to avoid.What is the feature you are proposing to solve the problem?
A function that takes an address and a signature, and returnss the same kind of wrapper as
getFunction():The name follows
toString(),toBuffer(), andtoArrayBuffer(), which also turn a pointer into a JavaScript value. Like them, it does not track lifetime. The caller must make sure the address stays valid.The internals seem to support this already:
PrepareFunction()resolves the name withuv_dlsym()and stores theptrin theFFIFunctionthatCreateFunction()uses. A pointer entry point would supplyptrdirectly, and libffi'sffi_call()accepts any address.Other FFIs have this:
CFUNCTYPE(restype, *argtypes)(address)new Deno.UnsafeFnPointer(pointer, definition)new CFunction({ptr, args, returns})ffi.ForeignFunction(pointer, returnType, argumentTypes)The earlier
node:ffiproposal (#57761) also hadUnsafeFnPointer.What alternatives have you considered?
bigintinlibrary.getFunction(). But the function does not belong to the library, andgetFunction()caches by symbol name.DispCallFuncfromoleaut32.dll. It works only on Windows and only for COM-style calls.node:ffiremoves.