diff --git a/doc/api/ffi.md b/doc/api/ffi.md index 37ccf2a719bc1a..01abdc0b6bbdb1 100644 --- a/doc/api/ffi.md +++ b/doc/api/ffi.md @@ -124,18 +124,18 @@ such as `0` and `1`; JavaScript `true` and `false` are not accepted. Functions and callbacks are described with signature objects. -Supported fields: +Signature objects may contain the following properties, both of which are +optional: -* `result`, `return`, or `returns` for the return type. -* `parameters` or `arguments` for the parameter type list. +* `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:** `[]`. -Only one return-type field and one parameter-list field may be present in a -single signature object. - -```cjs +```js const signature = { - result: 'i32', - parameters: ['i32', 'i32'], + return: 'i32', + arguments: ['i32', 'i32'], }; ``` @@ -193,7 +193,7 @@ import { dlopen } from 'node:ffi'; { using handle = dlopen('./mylib.so', { - add_i32: { parameters: ['i32', 'i32'], result: 'i32' }, + add_i32: { arguments: ['i32', 'i32'], return: 'i32' }, }); console.log(handle.functions.add_i32(20, 22)); } // handle.lib.close() is invoked automatically here. @@ -203,8 +203,8 @@ import { dlopen } from 'node:ffi'; import { dlopen } from 'node:ffi'; const { lib, functions } = dlopen('./mylib.so', { - add_i32: { parameters: ['i32', 'i32'], result: 'i32' }, - string_length: { parameters: ['pointer'], result: 'u64' }, + add_i32: { arguments: ['i32', 'i32'], return: 'i32' }, + string_length: { arguments: ['pointer'], return: 'u64' }, }); console.log(functions.add_i32(20, 22)); @@ -214,8 +214,8 @@ console.log(functions.add_i32(20, 22)); const { dlopen } = require('node:ffi'); const { lib, functions } = dlopen('./mylib.so', { - add_i32: { parameters: ['i32', 'i32'], result: 'i32' }, - string_length: { parameters: ['pointer'], result: 'u64' }, + add_i32: { arguments: ['i32', 'i32'], return: 'i32' }, + string_length: { arguments: ['pointer'], return: 'u64' }, }); console.log(functions.add_i32(20, 22)); @@ -356,8 +356,8 @@ const { DynamicLibrary } = require('node:ffi'); const lib = new DynamicLibrary('./mylib.so'); const add = lib.getFunction('add_i32', { - parameters: ['i32', 'i32'], - result: 'i32', + arguments: ['i32', 'i32'], + return: 'i32', }); console.log(add(20, 22)); @@ -407,7 +407,7 @@ const { DynamicLibrary } = require('node:ffi'); const lib = new DynamicLibrary('./mylib.so'); const callback = lib.registerCallback( - { parameters: ['i32'], result: 'i32' }, + { arguments: ['i32'], return: 'i32' }, (value) => value * 2, ); ``` @@ -417,7 +417,7 @@ 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 result type. +* 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. @@ -465,7 +465,7 @@ JavaScript `number` values that match the declared type. For 64-bit integer types (`i64` and `u64`), pass JavaScript `bigint` values. -For pointer-like parameters: +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 @@ -727,3 +727,4 @@ and keep callback and pointer lifetimes explicit on the native side. [`--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 diff --git a/lib/internal/ffi-shared-buffer.js b/lib/internal/ffi-shared-buffer.js index bce51fd79959dd..c5a769c394f11a 100644 --- a/lib/internal/ffi-shared-buffer.js +++ b/lib/internal/ffi-shared-buffer.js @@ -31,18 +31,14 @@ const { TypeError, } = primordials; -const { - codes: { - ERR_INTERNAL_ASSERTION, - }, -} = require('internal/errors'); +const assert = require('internal/assert'); const { DynamicLibrary, charIsSigned, + kSbArguments, kSbInvokeSlow, - kSbParams, - kSbResult, + kSbReturn, kSbSharedBuffer, uintptrMax, } = internalBinding('ffi'); @@ -159,11 +155,9 @@ function writeNumericArg(view, info, offset, arg, index) { return; } - /* c8 ignore start */ // Unreachable: caller filters out non-numeric kinds. - throw new ERR_INTERNAL_ASSERTION( - `FFI: writeNumericArg reached with unexpected kind="${kind}"`); - /* c8 ignore stop */ + /* c8 ignore next */ + assert.fail(`FFI: writeNumericArg reached with unexpected kind="${kind}"`); } // Returns true on fast-path success, false when the caller must fall back @@ -208,51 +202,46 @@ function inheritMetadata(wrapper, rawFn, nargs) { // arguments out of it into invocation-local storage before `ffi_call` and // reads the return value back only after, so nested/reentrant calls into // the same function are safe. -function wrapWithSharedBuffer(rawFn, parameters, resultType) { - if (rawFn === undefined || rawFn === null) return rawFn; +function wrapWithSharedBuffer(rawFn, signature) { + if (rawFn == null) return rawFn; const buffer = rawFn[kSbSharedBuffer]; if (buffer === undefined) return rawFn; // Callers without explicit signature info (the `functions` accessor - // patch below) rely on the `kSbParams` / `kSbResult` metadata attached + // patch below) rely on the `kSbArguments` / `kSbReturn` metadata attached // by the native `CreateFunction`. - if (parameters === undefined) parameters = rawFn[kSbParams]; - if (resultType === undefined) resultType = rawFn[kSbResult]; - // `CreateFunction` always attaches these for SB-eligible functions. - // Missing here means the native side and this wrapper are out of sync. - /* c8 ignore start */ - if (parameters === undefined || resultType === undefined) { - throw new ERR_INTERNAL_ASSERTION( - 'FFI: shared-buffer raw function is missing kSbParams or kSbResult'); + let argumentTypes, returnType; + if (signature === undefined) { + argumentTypes = rawFn[kSbArguments]; + returnType = rawFn[kSbReturn]; + + // `CreateFunction` always attaches these for SB-eligible functions. + // Missing here means the native side and this wrapper are out of sync. + assert(argumentTypes !== undefined && returnType !== undefined, + 'FFI: shared-buffer raw function is missing kSbArguments or kSbReturn'); + } else { + argumentTypes = signature.arguments ?? []; + returnType = signature.return ?? 'void'; } - /* c8 ignore stop */ const slowInvoke = rawFn[kSbInvokeSlow]; const view = new DataView(buffer); let retGetter = null; - if (resultType !== 'void') { - const retInfo = sbTypeInfo[resultType]; - /* c8 ignore start */ - if (retInfo === undefined) { - throw new ERR_INTERNAL_ASSERTION( - `FFI: shared-buffer type table missing entry for result type "${resultType}"`); - } - /* c8 ignore stop */ + if (returnType !== 'void') { + const retInfo = sbTypeInfo[returnType]; + assert(retInfo !== undefined, + `FFI: shared-buffer type table missing entry for return type "${returnType}"`); retGetter = retInfo.get; } - const nargs = parameters.length; + const nargs = argumentTypes.length; const argInfos = []; const argOffsets = []; let anyPointer = false; for (let i = 0; i < nargs; i++) { - const info = sbTypeInfo[parameters[i]]; - /* c8 ignore start */ - if (info === undefined) { - throw new ERR_INTERNAL_ASSERTION( - `FFI: shared-buffer type table missing entry for parameter type "${parameters[i]}"`); - } - /* c8 ignore stop */ + const info = sbTypeInfo[argumentTypes[i]]; + assert(info !== undefined, + `FFI: shared-buffer type table missing entry for argument type "${argumentTypes[i]}"`); // Push the `sbTypeInfo` entry directly (entries with the same `kind` // share a shape, keeping `writeNumericArg`'s call sites // low-polymorphism) and store offsets in a parallel array to avoid @@ -267,13 +256,8 @@ function wrapWithSharedBuffer(rawFn, parameters, resultType) { // Pointer signatures need a per-arg runtime type check and fall back // to the native slow-path invoker for non-BigInt pointer arguments, // so arity specialization wouldn't buy much here. - /* c8 ignore start */ - if (slowInvoke === undefined) { - throw new ERR_INTERNAL_ASSERTION( - 'FFI: shared-buffer raw function with pointer arguments is ' + - 'missing kSbInvokeSlow'); - } - /* c8 ignore stop */ + assert(slowInvoke !== undefined, + 'FFI: shared-buffer raw function with pointer arguments is missing kSbInvokeSlow'); wrapper = function(...args) { if (args.length !== nargs) { throwFFIArgCountError(nargs, args.length); @@ -542,19 +526,6 @@ function buildNumericWrapper( }; } -// Accept-set mirrors the native `ParseFunctionSignature` in -// `src/ffi/types.cc`. `ParseFunctionSignature` additionally throws when -// multiple aliases are set at once. The wrapper runs before the native -// call, so those conflicts still surface from the native side regardless -// of which alias we happen to read here. -function sigParams(sig) { - return sig.parameters ?? sig.arguments ?? []; -} - -function sigResult(sig) { - return sig.result ?? sig.return ?? sig.returns ?? 'void'; -} - // The native invoker for SB-eligible symbols is `InvokeFunctionSB`, which // reads arguments from the shared buffer populated by // `wrapWithSharedBuffer`. These patches make sure every path that surfaces @@ -563,11 +534,11 @@ function sigResult(sig) { const rawGetFunction = DynamicLibrary.prototype.getFunction; const rawGetFunctions = DynamicLibrary.prototype.getFunctions; -DynamicLibrary.prototype.getFunction = function getFunction(name, sig) { - // Native `DynamicLibrary::GetFunction` validates `sig`, so by the time - // we have `raw` we know `sig` is a valid object. - const raw = FunctionPrototypeCall(rawGetFunction, this, name, sig); - return wrapWithSharedBuffer(raw, sigParams(sig), sigResult(sig)); +DynamicLibrary.prototype.getFunction = function getFunction(name, signature) { + // Native `DynamicLibrary::GetFunction` validates `signature`, so by the time + // we have `raw` we know `signature` is a valid object. + const raw = FunctionPrototypeCall(rawGetFunction, this, name, signature); + return wrapWithSharedBuffer(raw, signature); }; DynamicLibrary.prototype.getFunctions = function getFunctions(definitions) { @@ -583,13 +554,12 @@ DynamicLibrary.prototype.getFunctions = function getFunctions(definitions) { for (let i = 0; i < keys.length; i++) { const name = keys[i]; // No `definitions`: native side returned every cached function, so we - // wrap using each function's own `kSbParams` / `kSbResult` metadata + // wrap using each function's own `kSbArguments` / `kSbReturn` metadata // (same fallback as the `functions` accessor). if (definitions === undefined) { out[name] = wrapWithSharedBuffer(raw[name]); } else { - const sig = definitions[name]; - out[name] = wrapWithSharedBuffer(raw[name], sigParams(sig), sigResult(sig)); + out[name] = wrapWithSharedBuffer(raw[name], definitions[name]); } } return out; @@ -602,16 +572,12 @@ DynamicLibrary.prototype.getFunctions = function getFunctions(definitions) { // uninitialized buffer. const functionsDescriptor = ObjectGetOwnPropertyDescriptor(DynamicLibrary.prototype, 'functions'); - /* c8 ignore start */ - if (functionsDescriptor === undefined || !functionsDescriptor.get) { - // Missing getter means the native and JS sides are out of sync; silently - // skipping the patch would expose the fast-path-against-uninitialized-buffer - // footgun this whole block exists to prevent. - throw new ERR_INTERNAL_ASSERTION( - 'FFI: DynamicLibrary.prototype.functions accessor not found or has no getter'); - } - /* c8 ignore stop */ - const origGetter = functionsDescriptor.get; + const origGetter = functionsDescriptor?.get; + // Missing getter means the native and JS sides are out of sync; silently + // skipping the patch would expose the fast-path-against-uninitialized-buffer + // footgun this whole block exists to prevent. + assert(origGetter !== undefined, + 'FFI: DynamicLibrary.prototype.functions accessor not found or has no getter'); ObjectDefineProperty(DynamicLibrary.prototype, 'functions', { __proto__: null, configurable: true, diff --git a/src/env_properties.h b/src/env_properties.h index 113cc066ab2c5d..896b664b64eea2 100644 --- a/src/env_properties.h +++ b/src/env_properties.h @@ -48,8 +48,8 @@ V(async_id_symbol, "async_id_symbol") \ V(ffi_sb_shared_buffer_symbol, "ffi_sb_shared_buffer_symbol") \ V(ffi_sb_invoke_slow_symbol, "ffi_sb_invoke_slow_symbol") \ - V(ffi_sb_params_symbol, "ffi_sb_params_symbol") \ - V(ffi_sb_result_symbol, "ffi_sb_result_symbol") \ + V(ffi_sb_arguments_symbol, "ffi_sb_arguments_symbol") \ + V(ffi_sb_return_symbol, "ffi_sb_return_symbol") \ V(constructor_key_symbol, "constructor_key_symbol") \ V(handle_onclose_symbol, "handle_onclose") \ V(no_message_symbol, "no_message_symbol") \ @@ -293,7 +293,6 @@ V(password_string, "password") \ V(path_string, "path") \ V(pathname_string, "pathname") \ - V(parameters_string, "parameters") \ V(pending_handle_string, "pendingHandle") \ V(permission_string, "permission") \ V(phase_string, "phase") \ @@ -329,9 +328,8 @@ V(require_string, "require") \ V(resource_string, "resource") \ V(result_string, "result") \ - V(return_string, "return") \ - V(returns_string, "returns") \ V(return_arrays_string, "returnArrays") \ + V(return_string, "return") \ V(salt_length_string, "saltLength") \ V(search_string, "search") \ V(servername_string, "servername") \ diff --git a/src/ffi/types.cc b/src/ffi/types.cc index e8469ebc0bbcc6..7f31845a06736c 100644 --- a/src/ffi/types.cc +++ b/src/ffi/types.cc @@ -83,63 +83,26 @@ Maybe ParseFunctionSignature(Environment* env, std::string_view name, Local signature) { Local context = env->context(); - Local returns_key = env->returns_string(); Local return_key = env->return_string(); - Local result_key = env->result_string(); - Local parameters_key = env->parameters_string(); Local arguments_key = env->arguments_string(); - bool has_returns; bool has_return; - bool has_result; - bool has_parameters; bool has_arguments; - if (!signature->Has(context, returns_key).To(&has_returns) || - !signature->Has(context, return_key).To(&has_return) || - !signature->Has(context, result_key).To(&has_result) || - !signature->Has(context, parameters_key).To(&has_parameters) || + if (!signature->Has(context, return_key).To(&has_return) || !signature->Has(context, arguments_key).To(&has_arguments)) { return {}; } - if (has_returns + has_return + has_result > 1) { - THROW_ERR_INVALID_ARG_VALUE( - env, - "Function signature of %s" - " must have either 'returns', 'return' or 'result' " - "property", - name); - return {}; - } - - if (has_arguments && has_parameters) { - THROW_ERR_INVALID_ARG_VALUE(env, - "Function signature of %s" - " must have either 'parameters' or 'arguments' " - "property", - name); - return {}; - } - ffi_type* return_type = &ffi_type_void; std::vector args; std::string return_type_name = "void"; std::vector arg_type_names; Isolate* isolate = env->isolate(); - if (has_returns || has_return || has_result) { - Local return_type_key; - if (has_returns) { - return_type_key = returns_key; - } else if (has_return) { - return_type_key = return_key; - } else { - return_type_key = result_key; - } - + if (has_return) { Local return_type_val; - if (!signature->Get(context, return_type_key).ToLocal(&return_type_val)) { + if (!signature->Get(context, return_key).ToLocal(&return_type_val)) { return {}; } @@ -162,10 +125,9 @@ Maybe ParseFunctionSignature(Environment* env, return_type_name = return_type_str.ToString(); } - if (has_arguments || has_parameters) { + if (has_arguments) { Local arguments_val; - if (!signature->Get(context, has_arguments ? arguments_key : parameters_key) - .ToLocal(&arguments_val)) { + if (!signature->Get(context, arguments_key).ToLocal(&arguments_val)) { return {}; } diff --git a/src/node_ffi.cc b/src/node_ffi.cc index 34f999b015123b..e0071baecb7e95 100644 --- a/src/node_ffi.cc +++ b/src/node_ffi.cc @@ -312,28 +312,28 @@ MaybeLocal DynamicLibrary::CreateFunction( // Attach the original signature type names so the JS wrapper can // rebuild the signature from a raw function when the caller did not - // pass parameters and result explicitly. The `lib.functions` accessor + // pass arguments and return explicitly. The `lib.functions` accessor // path relies on this. - Local params_arr; - if (!ToV8Value(context, fn->arg_type_names, isolate).ToLocal(¶ms_arr)) { + Local args_arr; + if (!ToV8Value(context, fn->arg_type_names, isolate).ToLocal(&args_arr)) { return MaybeLocal(); } if (!ret->DefineOwnProperty(context, - env->ffi_sb_params_symbol(), - params_arr, + env->ffi_sb_arguments_symbol(), + args_arr, internal_attrs) .FromMaybe(false)) { return MaybeLocal(); } - Local result_name; + Local return_name; if (!ToV8Value(context, fn->return_type_name, isolate) - .ToLocal(&result_name)) { + .ToLocal(&return_name)) { return MaybeLocal(); } if (!ret->DefineOwnProperty(context, - env->ffi_sb_result_symbol(), - result_name, + env->ffi_sb_return_symbol(), + return_name, internal_attrs) .FromMaybe(false)) { return MaybeLocal(); @@ -1196,13 +1196,13 @@ static void Initialize(Local target, .Check(); target ->Set(context, - FIXED_ONE_BYTE_STRING(isolate, "kSbParams"), - env->ffi_sb_params_symbol()) + FIXED_ONE_BYTE_STRING(isolate, "kSbArguments"), + env->ffi_sb_arguments_symbol()) .Check(); target ->Set(context, - FIXED_ONE_BYTE_STRING(isolate, "kSbResult"), - env->ffi_sb_result_symbol()) + FIXED_ONE_BYTE_STRING(isolate, "kSbReturn"), + env->ffi_sb_return_symbol()) .Check(); } diff --git a/test/ffi/ffi-test-common.js b/test/ffi/ffi-test-common.js index 7cc64eb00cda2e..86e56de8ec2163 100644 --- a/test/ffi/ffi-test-common.js +++ b/test/ffi/ffi-test-common.js @@ -31,56 +31,56 @@ function ensureFixtureLibrary() { ensureFixtureLibrary(); const fixtureSymbols = { - add_i8: { parameters: ['i8', 'i8'], result: 'i8' }, - add_u8: { parameters: ['u8', 'u8'], result: 'u8' }, - add_i16: { parameters: ['i16', 'i16'], result: 'i16' }, - add_u16: { parameters: ['u16', 'u16'], result: 'u16' }, - add_i32: { parameters: ['i32', 'i32'], result: 'i32' }, - add_u32: { parameters: ['u32', 'u32'], result: 'u32' }, - add_i64: { parameters: ['i64', 'i64'], result: 'i64' }, - add_u64: { parameters: ['u64', 'u64'], result: 'u64' }, - identity_char: { parameters: ['char'], result: 'char' }, - char_is_signed: { parameters: [], result: 'i32' }, - add_f32: { parameters: ['f32', 'f32'], result: 'f32' }, - multiply_f64: { parameters: ['f64', 'f64'], result: 'f64' }, - identity_pointer: { parameters: ['pointer'], result: 'pointer' }, - pointer_to_usize: { parameters: ['pointer'], result: 'u64' }, - usize_to_pointer: { parameters: ['u64'], result: 'pointer' }, - string_length: { parameters: ['pointer'], result: 'u64' }, - string_concat: { parameters: ['pointer', 'pointer'], result: 'pointer' }, - string_duplicate: { parameters: ['pointer'], result: 'pointer' }, - free_string: { parameters: ['pointer'], result: 'void' }, - fill_buffer: { parameters: ['pointer', 'u64', 'u32'], result: 'void' }, - sum_buffer: { parameters: ['pointer', 'u64'], result: 'u64' }, - reverse_buffer: { parameters: ['pointer', 'u64'], result: 'void' }, - logical_and: { parameters: ['i32', 'i32'], result: 'i32' }, - logical_or: { parameters: ['i32', 'i32'], result: 'i32' }, - logical_not: { parameters: ['i32'], result: 'i32' }, - increment_counter: { parameters: [], result: 'void' }, - get_counter: { parameters: [], result: 'i32' }, - reset_counter: { parameters: [], result: 'void' }, - call_int_callback: { parameters: ['pointer', 'i32'], result: 'i32' }, - call_int8_callback: { parameters: ['pointer', 'i8'], result: 'i8' }, - call_pointer_callback_is_null: { parameters: ['pointer'], result: 'i32' }, - call_void_callback: { parameters: ['pointer'], result: 'void' }, - call_string_callback: { parameters: ['function', 'pointer'], result: 'void' }, - call_binary_int_callback: { parameters: ['function', 'i32', 'i32'], result: 'i32' }, - call_callback_multiple_times: { parameters: ['pointer', 'i32'], result: 'void' }, - divide_i32: { parameters: ['i32', 'i32'], result: 'i32' }, - safe_strlen: { parameters: ['pointer'], result: 'i32' }, - sum_five_i32: { parameters: ['i32', 'i32', 'i32', 'i32', 'i32'], result: 'i32' }, - sum_five_f64: { parameters: ['f64', 'f64', 'f64', 'f64', 'f64'], result: 'f64' }, - mixed_operation: { parameters: ['i32', 'f32', 'f64', 'u32'], result: 'f64' }, - allocate_memory: { parameters: ['u64'], result: 'pointer' }, - deallocate_memory: { parameters: ['pointer'], result: 'void' }, - array_get_i32: { parameters: ['pointer', 'u64'], result: 'i32' }, - array_set_i32: { parameters: ['pointer', 'u64', 'i32'], result: 'void' }, - array_get_f64: { parameters: ['pointer', 'u64'], result: 'f64' }, - array_set_f64: { parameters: ['pointer', 'u64', 'f64'], result: 'void' }, + add_i8: { arguments: ['i8', 'i8'], return: 'i8' }, + add_u8: { arguments: ['u8', 'u8'], return: 'u8' }, + add_i16: { arguments: ['i16', 'i16'], return: 'i16' }, + add_u16: { arguments: ['u16', 'u16'], return: 'u16' }, + add_i32: { arguments: ['i32', 'i32'], return: 'i32' }, + add_u32: { arguments: ['u32', 'u32'], return: 'u32' }, + add_i64: { arguments: ['i64', 'i64'], return: 'i64' }, + add_u64: { arguments: ['u64', 'u64'], return: 'u64' }, + identity_char: { arguments: ['char'], return: 'char' }, + char_is_signed: { arguments: [], return: 'i32' }, + add_f32: { arguments: ['f32', 'f32'], return: 'f32' }, + multiply_f64: { arguments: ['f64', 'f64'], return: 'f64' }, + identity_pointer: { arguments: ['pointer'], return: 'pointer' }, + pointer_to_usize: { arguments: ['pointer'], return: 'u64' }, + usize_to_pointer: { arguments: ['u64'], return: 'pointer' }, + string_length: { arguments: ['pointer'], return: 'u64' }, + string_concat: { arguments: ['pointer', 'pointer'], return: 'pointer' }, + string_duplicate: { arguments: ['pointer'], return: 'pointer' }, + free_string: { arguments: ['pointer'], return: 'void' }, + fill_buffer: { arguments: ['pointer', 'u64', 'u32'], return: 'void' }, + sum_buffer: { arguments: ['pointer', 'u64'], return: 'u64' }, + reverse_buffer: { arguments: ['pointer', 'u64'], return: 'void' }, + logical_and: { arguments: ['i32', 'i32'], return: 'i32' }, + logical_or: { arguments: ['i32', 'i32'], return: 'i32' }, + logical_not: { arguments: ['i32'], return: 'i32' }, + increment_counter: { arguments: [], return: 'void' }, + get_counter: { arguments: [], return: 'i32' }, + reset_counter: { arguments: [], return: 'void' }, + call_int_callback: { arguments: ['pointer', 'i32'], return: 'i32' }, + call_int8_callback: { arguments: ['pointer', 'i8'], return: 'i8' }, + call_pointer_callback_is_null: { arguments: ['pointer'], return: 'i32' }, + call_void_callback: { arguments: ['pointer'], return: 'void' }, + call_string_callback: { arguments: ['function', 'pointer'], return: 'void' }, + call_binary_int_callback: { arguments: ['function', 'i32', 'i32'], return: 'i32' }, + call_callback_multiple_times: { arguments: ['pointer', 'i32'], return: 'void' }, + divide_i32: { arguments: ['i32', 'i32'], return: 'i32' }, + safe_strlen: { arguments: ['pointer'], return: 'i32' }, + sum_five_i32: { arguments: ['i32', 'i32', 'i32', 'i32', 'i32'], return: 'i32' }, + sum_five_f64: { arguments: ['f64', 'f64', 'f64', 'f64', 'f64'], return: 'f64' }, + mixed_operation: { arguments: ['i32', 'f32', 'f64', 'u32'], return: 'f64' }, + allocate_memory: { arguments: ['u64'], return: 'pointer' }, + deallocate_memory: { arguments: ['pointer'], return: 'void' }, + array_get_i32: { arguments: ['pointer', 'u64'], return: 'i32' }, + array_set_i32: { arguments: ['pointer', 'u64', 'i32'], return: 'void' }, + array_get_f64: { arguments: ['pointer', 'u64'], return: 'f64' }, + array_set_f64: { arguments: ['pointer', 'u64', 'f64'], return: 'void' }, }; if (!common.isWindows) { - fixtureSymbols.readonly_memory = { parameters: [], result: 'pointer' }; + fixtureSymbols.readonly_memory = { arguments: [], return: 'pointer' }; } function cString(value) { diff --git a/test/ffi/test-ffi-calls.js b/test/ffi/test-ffi-calls.js index ef43fb0a6f7274..14020c5a6c78ae 100644 --- a/test/ffi/test-ffi-calls.js +++ b/test/ffi/test-ffi-calls.js @@ -59,8 +59,8 @@ test('ffi bool signatures use uint8 values', () => { assert.strictEqual(symbols.logical_not(0), 1); const boolAdder = lib.getFunction('add_u8', { - parameters: ['bool', 'bool'], - result: 'bool', + arguments: ['bool', 'bool'], + return: 'bool', }); assert.strictEqual(boolAdder(1, 0), 1); assert.throws(() => boolAdder(true, false), /Argument 0 must be a uint8/); @@ -148,15 +148,15 @@ test('ffi callbacks can be registered and invoked', () => { const { lib, functions: symbols } = getLibrary(); const seen = []; const intCallback = lib.registerCallback( - { parameters: ['i32'], result: 'i32' }, + { arguments: ['i32'], return: 'i32' }, (value) => value * 2, ); const stringCallback = lib.registerCallback( - { parameters: ['pointer'], result: 'void' }, + { arguments: ['pointer'], return: 'void' }, (ptr) => seen.push(ffi.toString(ptr)), ); const binaryCallback = lib.registerCallback( - { arguments: ['i32', 'i32'], returns: 'i32' }, + { arguments: ['i32', 'i32'], return: 'i32' }, (a, b) => a + b, ); @@ -166,8 +166,8 @@ test('ffi callbacks can be registered and invoked', () => { assert.deepStrictEqual(seen, ['hello callback']); assert.strictEqual(symbols.call_binary_int_callback(binaryCallback, 19, 23), 42); - const nullPointerCallback = lib.registerCallback({ result: 'pointer' }, () => null); - const undefinedPointerCallback = lib.registerCallback({ result: 'pointer' }, () => undefined); + const nullPointerCallback = lib.registerCallback({ return: 'pointer' }, () => null); + const undefinedPointerCallback = lib.registerCallback({ return: 'pointer' }, () => undefined); try { assert.strictEqual(symbols.call_pointer_callback_is_null(nullPointerCallback), 1); assert.strictEqual(symbols.call_pointer_callback_is_null(undefinedPointerCallback), 1); @@ -191,7 +191,7 @@ test('ffi callback ref and unref APIs work', () => { called = true; }); const countingCallback = lib.registerCallback( - { parameters: ['i32'], result: 'i32' }, + { arguments: ['i32'], return: 'i32' }, (value) => { values.push(value); return 0; @@ -271,7 +271,7 @@ const ffi = require('node:ffi'); const { fixtureSymbols, libraryPath } = require(${JSON.stringify(require.resolve('./ffi-test-common'))}); const { lib, functions } = ffi.dlopen(libraryPath, fixtureSymbols); const callback = lib.registerCallback( - { parameters: ['i32'], result: 'i32' }, + { arguments: ['i32'], return: 'i32' }, () => (${returnExpression}), ); functions.call_int_callback(callback, 21);`, @@ -294,7 +294,7 @@ const ffi = require('node:ffi'); const { fixtureSymbols, libraryPath } = require(${JSON.stringify(require.resolve('./ffi-test-common'))}); const { lib, functions } = ffi.dlopen(libraryPath, fixtureSymbols); const callback = lib.registerCallback( - { parameters: ['i32'], result: 'i32' }, + { arguments: ['i32'], return: 'i32' }, () => { ${callbackBody} }, ); functions.call_int_callback(callback, 21);`, @@ -325,7 +325,7 @@ const ffi = require('node:ffi'); const { fixtureSymbols, libraryPath } = require(${JSON.stringify(require.resolve('./ffi-test-common'))}); const { lib } = ffi.dlopen(libraryPath, fixtureSymbols); const callback = lib.registerCallback( - { parameters: ['i32'], result: 'i32' }, + { arguments: ['i32'], return: 'i32' }, (value) => value * 2, ); new Worker(${JSON.stringify(workerSource)}, { eval: true, workerData: callback });`, @@ -359,7 +359,7 @@ test('ffi unrefCallback releases callback function', async () => { let callback = () => 1; const ref = new WeakRef(callback); const pointer = lib.registerCallback( - { parameters: ['i32'], result: 'i32' }, + { arguments: ['i32'], return: 'i32' }, callback, ); @@ -383,7 +383,7 @@ test('ffi unrefCallback zero-fills narrow callback return', async () => { let callback = () => 1; const ref = new WeakRef(callback); const pointer = lib.registerCallback( - { parameters: ['i8'], result: 'i8' }, + { arguments: ['i8'], return: 'i8' }, callback, ); @@ -406,7 +406,7 @@ test('ffi refCallback retains callback function', async () => { try { let callback = () => 1; const ref = new WeakRef(callback); - const pointer = lib.registerCallback({ result: 'i32' }, callback); + const pointer = lib.registerCallback({ return: 'i32' }, callback); lib.unrefCallback(pointer); lib.refCallback(pointer); diff --git a/test/ffi/test-ffi-dynamic-library.js b/test/ffi/test-ffi-dynamic-library.js index e3171b57124250..ba0f8a383ffd8b 100644 --- a/test/ffi/test-ffi-dynamic-library.js +++ b/test/ffi/test-ffi-dynamic-library.js @@ -27,7 +27,7 @@ test('dlopen resolves symbols from the current process with null path', { skip: common.isWindows, }, () => { const { lib, functions } = ffi.dlopen(null, { - uv_os_getpid: { result: 'i32', parameters: [] }, + uv_os_getpid: { return: 'i32', arguments: [] }, }); try { @@ -41,8 +41,8 @@ test('dlopen resolves symbols from the current process with null path', { test('dlopen resolves functions from definitions', () => { const { lib, functions } = ffi.dlopen(libraryPath, { add_i32: fixtureSymbols.add_i32, - add_f32: { returns: 'f32', arguments: ['f32', 'f32'] }, - add_u64: { return: 'u64', parameters: ['u64', 'u64'] }, + add_f32: { return: 'f32', arguments: ['f32', 'f32'] }, + add_u64: { return: 'u64', arguments: ['u64', 'u64'] }, }); try { @@ -73,7 +73,7 @@ test('DynamicLibrary exposes functions and symbols', () => { try { const addI32 = lib.getFunction('add_i32', fixtureSymbols.add_i32); const addU64 = lib.getFunction('add_u64', { - returns: 'u64', + return: 'u64', arguments: ['u64', 'u64'], }); const addI32Ptr = lib.getSymbol('add_i32'); @@ -84,7 +84,7 @@ test('DynamicLibrary exposes functions and symbols', () => { assert.strictEqual(addI32.pointer, addI32Ptr); const functions = lib.getFunctions({ - add_f32: { result: 'f32', parameters: ['f32', 'f32'] }, + add_f32: { return: 'f32', arguments: ['f32', 'f32'] }, add_i64: { return: 'i64', arguments: ['i64', 'i64'] }, }); @@ -115,7 +115,7 @@ test('getFunction caches signatures consistently', () => { ); assert.throws(() => { - lib.getFunction('add_i32', { parameters: ['u32', 'u32'], result: 'u32' }); + lib.getFunction('add_i32', { arguments: ['u32', 'u32'], return: 'u32' }); }, /already requested with a different signature/); } finally { lib.close(); @@ -226,7 +226,7 @@ test('dynamic library APIs validate failures and bad signatures', () => { assert.throws(() => { ffi.dlopen(libraryPath, { add_i32: fixtureSymbols.add_i32, - missing_symbol: { result: 'void', parameters: [] }, + missing_symbol: { return: 'void', arguments: [] }, }); }, /dlsym failed:/); @@ -245,7 +245,7 @@ test('dynamic library APIs validate failures and bad signatures', () => { try { assert.throws(() => { - lib.getFunction('missing_symbol', { result: 'void', parameters: [] }); + lib.getFunction('missing_symbol', { return: 'void', arguments: [] }); }, /dlsym failed:/); assert.throws(() => { @@ -259,21 +259,21 @@ test('dynamic library APIs validate failures and bad signatures', () => { assert.throws(() => { lib.getFunctions({ add_i32: fixtureSymbols.add_i32, - missing_symbol: { result: 'void', parameters: [] }, + missing_symbol: { return: 'void', arguments: [] }, }); }, /dlsym failed:/); assert.strictEqual(lib.getFunction('add_i32', { - result: 'pointer', - parameters: ['pointer'], + return: 'pointer', + arguments: ['pointer'], }).pointer, lib.getSymbol('add_i32')); assert.throws(() => { - lib.getFunction('add_i32', { result: 'i32\0bad', parameters: [] }); + lib.getFunction('add_i32', { return: 'i32\0bad', arguments: [] }); }, /Return value type of function add_i32 must not contain null bytes/); assert.throws(() => { - lib.getFunction('add_i32', { result: 'i32', parameters: ['i32\0bad'] }); + lib.getFunction('add_i32', { return: 'i32', arguments: ['i32\0bad'] }); }, /Argument 0 of function add_i32 must not contain null bytes/); assert.throws(() => { @@ -305,30 +305,14 @@ test('dynamic library APIs validate failures and bad signatures', () => { }); assert.throws(() => { - lib.getFunction('add_i32', { - result: 'i32', - return: 'i32', - parameters: ['i32', 'i32'], - }); - }, /must have either 'returns', 'return' or 'result' property/); - - assert.throws(() => { - lib.getFunction('add_i32', { - result: 'i32', - parameters: ['i32', 'i32'], - arguments: ['i32', 'i32'], - }); - }, /must have either 'parameters' or 'arguments' property/); - - assert.throws(() => { - lib.getFunction('add_i32', { result: 'bogus', parameters: [] }); + lib.getFunction('add_i32', { return: 'bogus', arguments: [] }); }, /Unsupported FFI type: bogus/); const hasTrapError = new Error('signature has trap'); assert.throws(() => { lib.getFunction('add_i32', new Proxy({}, { has(target, key) { - if (key === 'result') { + if (key === 'return') { throw hasTrapError; } return Reflect.has(target, key); @@ -339,8 +323,8 @@ test('dynamic library APIs validate failures and bad signatures', () => { const getterError = new Error('signature getter'); assert.throws(() => { lib.getFunction('add_i32', { - result: 'i32', - get parameters() { + return: 'i32', + get arguments() { throw getterError; }, }); diff --git a/test/ffi/test-ffi-shared-buffer.js b/test/ffi/test-ffi-shared-buffer.js index 944b4021abc47a..429faf6439faf0 100644 --- a/test/ffi/test-ffi-shared-buffer.js +++ b/test/ffi/test-ffi-shared-buffer.js @@ -19,8 +19,8 @@ const { internalBinding } = require('internal/test/binding'); const ffiBinding = internalBinding('ffi'); const { kSbInvokeSlow, - kSbParams, - kSbResult, + kSbArguments, + kSbReturn, kSbSharedBuffer, } = ffiBinding; const rawGetFunctionUnpatched = ffiBinding.DynamicLibrary.prototype.getFunction; @@ -30,7 +30,7 @@ const { libraryPath } = require('./ffi-test-common'); test('numeric-only i32 function uses SB path', () => { const { lib, functions } = ffi.dlopen(libraryPath, { - add_i32: { result: 'i32', parameters: ['i32', 'i32'] }, + add_i32: { return: 'i32', arguments: ['i32', 'i32'] }, }); try { assert.strictEqual(functions.add_i32(20, 22), 42); @@ -44,10 +44,10 @@ test('numeric-only i32 function uses SB path', () => { test('i8/u8/i16/u16 round-trip', () => { const { lib, functions } = ffi.dlopen(libraryPath, { - add_i8: { result: 'i8', parameters: ['i8', 'i8'] }, - add_u8: { result: 'u8', parameters: ['u8', 'u8'] }, - add_i16: { result: 'i16', parameters: ['i16', 'i16'] }, - add_u16: { result: 'u16', parameters: ['u16', 'u16'] }, + add_i8: { return: 'i8', arguments: ['i8', 'i8'] }, + add_u8: { return: 'u8', arguments: ['u8', 'u8'] }, + add_i16: { return: 'i16', arguments: ['i16', 'i16'] }, + add_u16: { return: 'u16', arguments: ['u16', 'u16'] }, }); try { assert.strictEqual(functions.add_i8(10, 20), 30); @@ -61,8 +61,8 @@ test('i8/u8/i16/u16 round-trip', () => { test('f32/f64 round-trip', () => { const { lib, functions } = ffi.dlopen(libraryPath, { - add_f32: { result: 'f32', parameters: ['f32', 'f32'] }, - add_f64: { result: 'f64', parameters: ['f64', 'f64'] }, + add_f32: { return: 'f32', arguments: ['f32', 'f32'] }, + add_f64: { return: 'f64', arguments: ['f64', 'f64'] }, }); try { // 1.25 and 2.75 are exactly representable in float32, so the sum is exact. @@ -75,8 +75,8 @@ test('f32/f64 round-trip', () => { test('i64/u64 BigInt round-trip', () => { const { lib, functions } = ffi.dlopen(libraryPath, { - add_i64: { result: 'i64', parameters: ['i64', 'i64'] }, - add_u64: { result: 'u64', parameters: ['u64', 'u64'] }, + add_i64: { return: 'i64', arguments: ['i64', 'i64'] }, + add_u64: { return: 'u64', arguments: ['u64', 'u64'] }, }); try { assert.strictEqual(functions.add_i64(10n, 20n), 30n); @@ -88,7 +88,7 @@ test('i64/u64 BigInt round-trip', () => { test('zero-arg function', () => { const { lib, functions } = ffi.dlopen(libraryPath, { - char_is_signed: { result: 'i32', parameters: [] }, + char_is_signed: { return: 'i32', arguments: [] }, }); try { const result = functions.char_is_signed(); @@ -101,7 +101,7 @@ test('zero-arg function', () => { test('6-arg numeric function', () => { const { lib, functions } = ffi.dlopen(libraryPath, { - sum_6_i32: { result: 'i32', parameters: ['i32', 'i32', 'i32', 'i32', 'i32', 'i32'] }, + sum_6_i32: { return: 'i32', arguments: ['i32', 'i32', 'i32', 'i32', 'i32', 'i32'] }, }); try { assert.strictEqual(functions.sum_6_i32(1, 2, 3, 4, 5, 6), 21); @@ -112,8 +112,8 @@ test('6-arg numeric function', () => { test('pointer args: fast path (BigInt/null) and slow-path fallback (Buffer/ArrayBuffer)', () => { const { lib, functions } = ffi.dlopen(libraryPath, { - identity_pointer: { result: 'pointer', parameters: ['pointer'] }, - pointer_to_usize: { result: 'u64', parameters: ['pointer'] }, + identity_pointer: { return: 'pointer', arguments: ['pointer'] }, + pointer_to_usize: { return: 'u64', arguments: ['pointer'] }, }); try { assert.strictEqual(functions.identity_pointer(0n), 0n); @@ -137,7 +137,7 @@ test('pointer args: fast path (BigInt/null) and slow-path fallback (Buffer/Array test('string pointer uses slow-path fallback', () => { const { lib, functions } = ffi.dlopen(libraryPath, { - string_length: { result: 'u64', parameters: ['pointer'] }, + string_length: { return: 'u64', arguments: ['pointer'] }, }); try { assert.strictEqual(functions.string_length('hello'), 5n); @@ -150,8 +150,8 @@ test('string pointer uses slow-path fallback', () => { test('non-SB-eligible signature falls back to raw function', () => { const { lib, functions } = ffi.dlopen(libraryPath, { - string_duplicate: { result: 'pointer', parameters: ['pointer'] }, - free_string: { result: 'void', parameters: ['pointer'] }, + string_duplicate: { return: 'pointer', arguments: ['pointer'] }, + free_string: { return: 'void', arguments: ['pointer'] }, }); try { const dup = functions.string_duplicate('round-trip'); @@ -167,14 +167,14 @@ test('reentrancy across two FFI symbols', () => { // A JS callback invoked by one FFI function reenters a different FFI // function. Each has its own ArrayBuffer; neither may clobber the other. const { lib, functions } = ffi.dlopen(libraryPath, { - call_int_callback: { result: 'i32', parameters: ['pointer', 'i32'] }, - add_i32: { result: 'i32', parameters: ['i32', 'i32'] }, + call_int_callback: { return: 'i32', arguments: ['pointer', 'i32'] }, + add_i32: { return: 'i32', arguments: ['i32', 'i32'] }, }); let callDepth = 0; let innerResult = -1; const callback = lib.registerCallback( - { result: 'i32', parameters: ['i32'] }, + { return: 'i32', arguments: ['i32'] }, (x) => { callDepth++; if (callDepth === 1) innerResult = functions.add_i32(x, 100); @@ -194,7 +194,7 @@ test('reentrancy across two FFI symbols', () => { test('arity mismatch throws ERR_INVALID_ARG_VALUE', () => { const { lib, functions } = ffi.dlopen(libraryPath, { - add_i32: { result: 'i32', parameters: ['i32', 'i32'] }, + add_i32: { return: 'i32', arguments: ['i32', 'i32'] }, }); try { assert.throws(() => functions.add_i32(1), { @@ -213,8 +213,8 @@ test('arity mismatch throws ERR_INVALID_ARG_VALUE', () => { test('arity 7+ uses the generic rest-params branch', () => { const { lib, functions } = ffi.dlopen(libraryPath, { sum_7_i32: { - result: 'i32', - parameters: ['i32', 'i32', 'i32', 'i32', 'i32', 'i32', 'i32'], + return: 'i32', + arguments: ['i32', 'i32', 'i32', 'i32', 'i32', 'i32', 'i32'], }, }); try { @@ -230,8 +230,8 @@ test('arity 7+ uses the generic rest-params branch', () => { test('wrappers preserve name/length/pointer and the functions accessor returns wrappers', () => { const { lib, functions } = ffi.dlopen(libraryPath, { - add_i32: { result: 'i32', parameters: ['i32', 'i32'] }, - identity_pointer: { result: 'pointer', parameters: ['pointer'] }, + add_i32: { return: 'i32', arguments: ['i32', 'i32'] }, + identity_pointer: { return: 'pointer', arguments: ['pointer'] }, }); try { assert.strictEqual(functions.add_i32.name, 'add_i32'); @@ -253,12 +253,12 @@ test('wrappers preserve name/length/pointer and the functions accessor returns w test('integer boundaries for i8/u8/i16/u16/i32/u32', () => { const { lib, functions } = ffi.dlopen(libraryPath, { - add_i8: { result: 'i8', parameters: ['i8', 'i8'] }, - add_u8: { result: 'u8', parameters: ['u8', 'u8'] }, - add_i16: { result: 'i16', parameters: ['i16', 'i16'] }, - add_u16: { result: 'u16', parameters: ['u16', 'u16'] }, - add_i32: { result: 'i32', parameters: ['i32', 'i32'] }, - add_u32: { result: 'u32', parameters: ['u32', 'u32'] }, + add_i8: { return: 'i8', arguments: ['i8', 'i8'] }, + add_u8: { return: 'u8', arguments: ['u8', 'u8'] }, + add_i16: { return: 'i16', arguments: ['i16', 'i16'] }, + add_u16: { return: 'u16', arguments: ['u16', 'u16'] }, + add_i32: { return: 'i32', arguments: ['i32', 'i32'] }, + add_u32: { return: 'u32', arguments: ['u32', 'u32'] }, }); try { @@ -299,8 +299,8 @@ test('integer boundaries for i8/u8/i16/u16/i32/u32', () => { test('i64/u64 BigInt boundaries and Number/BigInt type mismatches', () => { const { lib, functions } = ffi.dlopen(libraryPath, { - add_i64: { result: 'i64', parameters: ['i64', 'i64'] }, - add_u64: { result: 'u64', parameters: ['u64', 'u64'] }, + add_i64: { return: 'i64', arguments: ['i64', 'i64'] }, + add_u64: { return: 'u64', arguments: ['u64', 'u64'] }, }); try { @@ -328,8 +328,8 @@ test('i64/u64 BigInt boundaries and Number/BigInt type mismatches', () => { test('char type picks signed/unsigned range based on host ABI', () => { const { lib, functions } = ffi.dlopen(libraryPath, { - char_is_signed: { result: 'i32', parameters: [] }, - identity_char: { result: 'char', parameters: ['char'] }, + char_is_signed: { return: 'i32', arguments: [] }, + identity_char: { return: 'char', arguments: ['char'] }, }); try { @@ -358,13 +358,13 @@ test('SB metadata is Symbol-keyed, attribute-hardened, and not leaked onto the w const rawLib = new ffiBinding.DynamicLibrary(libraryPath); try { const rawFn = rawGetFunctionUnpatched.call( - rawLib, 'add_i32', { result: 'i32', parameters: ['i32', 'i32'] }); + rawLib, 'add_i32', { return: 'i32', arguments: ['i32', 'i32'] }); for (const [name, sym] of [ ['kSbSharedBuffer', kSbSharedBuffer], ['kSbInvokeSlow', kSbInvokeSlow], - ['kSbParams', kSbParams], - ['kSbResult', kSbResult], + ['kSbArguments', kSbArguments], + ['kSbReturn', kSbReturn], ]) { assert.strictEqual(typeof sym, 'symbol', `${name} must be a Symbol`); } @@ -372,8 +372,8 @@ test('SB metadata is Symbol-keyed, attribute-hardened, and not leaked onto the w // Numeric-only signature: kSbInvokeSlow absent; the rest present and hardened. for (const [name, sym] of [ ['kSbSharedBuffer', kSbSharedBuffer], - ['kSbParams', kSbParams], - ['kSbResult', kSbResult], + ['kSbArguments', kSbArguments], + ['kSbReturn', kSbReturn], ]) { const desc = Object.getOwnPropertyDescriptor(rawFn, sym); assert.ok(desc !== undefined, `${name} missing on pure-numeric SB function`); @@ -386,7 +386,7 @@ test('SB metadata is Symbol-keyed, attribute-hardened, and not leaked onto the w // Pointer signature: kSbInvokeSlow must exist (and be hardened). const rawPtrFn = rawGetFunctionUnpatched.call( - rawLib, 'identity_pointer', { result: 'pointer', parameters: ['pointer'] }); + rawLib, 'identity_pointer', { return: 'pointer', arguments: ['pointer'] }); const slowDesc = Object.getOwnPropertyDescriptor(rawPtrFn, kSbInvokeSlow); assert.ok(slowDesc !== undefined); assert.strictEqual(slowDesc.enumerable, false); @@ -396,18 +396,18 @@ test('SB metadata is Symbol-keyed, attribute-hardened, and not leaked onto the w assert.deepStrictEqual(Object.keys(rawFn), ['pointer']); const ownSyms = Object.getOwnPropertySymbols(rawFn); assert.ok(ownSyms.includes(kSbSharedBuffer)); - assert.ok(ownSyms.includes(kSbParams)); - assert.ok(ownSyms.includes(kSbResult)); + assert.ok(ownSyms.includes(kSbArguments)); + assert.ok(ownSyms.includes(kSbReturn)); // Internals must not be forwarded by `inheritMetadata`. const { lib, functions } = ffi.dlopen(libraryPath, { - add_i32: { result: 'i32', parameters: ['i32', 'i32'] }, + add_i32: { return: 'i32', arguments: ['i32', 'i32'] }, }); try { assert.strictEqual(functions.add_i32[kSbSharedBuffer], undefined); assert.strictEqual(functions.add_i32[kSbInvokeSlow], undefined); - assert.strictEqual(functions.add_i32[kSbParams], undefined); - assert.strictEqual(functions.add_i32[kSbResult], undefined); + assert.strictEqual(functions.add_i32[kSbArguments], undefined); + assert.strictEqual(functions.add_i32[kSbReturn], undefined); } finally { lib.close(); } @@ -418,7 +418,7 @@ test('SB metadata is Symbol-keyed, attribute-hardened, and not leaked onto the w test('pointer fast-path range check: [0, 2^64 - 1]', () => { const { lib, functions } = ffi.dlopen(libraryPath, { - identity_pointer: { result: 'pointer', parameters: ['pointer'] }, + identity_pointer: { return: 'pointer', arguments: ['pointer'] }, }); try { assert.strictEqual(functions.identity_pointer(0n), 0n); @@ -438,15 +438,15 @@ test('self-recursive reentrancy: a single function\'s ArrayBuffer survives a nes // call can reuse the same buffer without clobbering the outer frame. const { lib, functions } = ffi.dlopen(libraryPath, { call_binary_int_callback: { - result: 'i32', - parameters: ['function', 'i32', 'i32'], + return: 'i32', + arguments: ['function', 'i32', 'i32'], }, }); try { let depth = 0; const callback = lib.registerCallback( - { result: 'i32', parameters: ['i32', 'i32'] }, + { return: 'i32', arguments: ['i32', 'i32'] }, common.mustCall((a, b) => { depth++; if (depth === 1) { @@ -469,9 +469,9 @@ test('self-recursive reentrancy: a single function\'s ArrayBuffer survives a nes test('void-return 0-arg wrapper branch', () => { const { lib, functions } = ffi.dlopen(libraryPath, { - reset_counter: { result: 'void', parameters: [] }, - increment_counter: { result: 'void', parameters: [] }, - get_counter: { result: 'i32', parameters: [] }, + reset_counter: { return: 'void', arguments: [] }, + increment_counter: { return: 'void', arguments: [] }, + get_counter: { return: 'i32', arguments: [] }, }); try { assert.strictEqual(functions.reset_counter(), undefined); @@ -496,26 +496,26 @@ test('void-return wrapper at every specialized arity observes side effects', () // at every arity the ladder specializes (1..6) plus the 7+ rest-params // fallback. const { lib, functions } = ffi.dlopen(libraryPath, { - store_i32: { result: 'void', parameters: ['i32'] }, - store_sum_2_i32: { result: 'void', parameters: ['i32', 'i32'] }, - store_sum_3_i32: { result: 'void', parameters: ['i32', 'i32', 'i32'] }, + store_i32: { return: 'void', arguments: ['i32'] }, + store_sum_2_i32: { return: 'void', arguments: ['i32', 'i32'] }, + store_sum_3_i32: { return: 'void', arguments: ['i32', 'i32', 'i32'] }, store_sum_4_i32: { - result: 'void', - parameters: ['i32', 'i32', 'i32', 'i32'], + return: 'void', + arguments: ['i32', 'i32', 'i32', 'i32'], }, store_sum_5_i32: { - result: 'void', - parameters: ['i32', 'i32', 'i32', 'i32', 'i32'], + return: 'void', + arguments: ['i32', 'i32', 'i32', 'i32', 'i32'], }, store_sum_6_i32: { - result: 'void', - parameters: ['i32', 'i32', 'i32', 'i32', 'i32', 'i32'], + return: 'void', + arguments: ['i32', 'i32', 'i32', 'i32', 'i32', 'i32'], }, store_sum_8_i32: { - result: 'void', - parameters: ['i32', 'i32', 'i32', 'i32', 'i32', 'i32', 'i32', 'i32'], + return: 'void', + arguments: ['i32', 'i32', 'i32', 'i32', 'i32', 'i32', 'i32', 'i32'], }, - get_scratch: { result: 'i32', parameters: [] }, + get_scratch: { return: 'i32', arguments: [] }, }); try { // Powers-of-two summands detect a dropped or duplicated slot at each @@ -598,17 +598,17 @@ test('value-return wrapper arity mismatch hits every specialized branch', () => // value-return closures for arities 1..6 so each specialization's // argument-count guard runs at least once. const { lib, functions } = ffi.dlopen(libraryPath, { - logical_not: { result: 'i32', parameters: ['i32'] }, - add_i32: { result: 'i32', parameters: ['i32', 'i32'] }, - sum_3_i32: { result: 'i32', parameters: ['i32', 'i32', 'i32'] }, - sum_4_i32: { result: 'i32', parameters: ['i32', 'i32', 'i32', 'i32'] }, + logical_not: { return: 'i32', arguments: ['i32'] }, + add_i32: { return: 'i32', arguments: ['i32', 'i32'] }, + sum_3_i32: { return: 'i32', arguments: ['i32', 'i32', 'i32'] }, + sum_4_i32: { return: 'i32', arguments: ['i32', 'i32', 'i32', 'i32'] }, sum_five_i32: { - result: 'i32', - parameters: ['i32', 'i32', 'i32', 'i32', 'i32'], + return: 'i32', + arguments: ['i32', 'i32', 'i32', 'i32', 'i32'], }, sum_6_i32: { - result: 'i32', - parameters: ['i32', 'i32', 'i32', 'i32', 'i32', 'i32'], + return: 'i32', + arguments: ['i32', 'i32', 'i32', 'i32', 'i32', 'i32'], }, }); try { @@ -647,7 +647,7 @@ test('pointer-dispatch wrapper rejects wrong-arity calls', () => { // per-arity ladder, but it still has its own `throwFFIArgCountError` // branch that needs to be exercised. const { lib, functions } = ffi.dlopen(libraryPath, { - identity_pointer: { result: 'pointer', parameters: ['pointer'] }, + identity_pointer: { return: 'pointer', arguments: ['pointer'] }, }); try { assert.throws( @@ -669,10 +669,10 @@ test('pointer-dispatch wrapper rejects wrong-arity calls', () => { test('mid-arity wrappers (1, 3, 4, 5)', () => { const { lib, functions } = ffi.dlopen(libraryPath, { - logical_not: { result: 'i32', parameters: ['i32'] }, - sum_3_i32: { result: 'i32', parameters: ['i32', 'i32', 'i32'] }, - sum_4_i32: { result: 'i32', parameters: ['i32', 'i32', 'i32', 'i32'] }, - sum_five_i32: { result: 'i32', parameters: ['i32', 'i32', 'i32', 'i32', 'i32'] }, + logical_not: { return: 'i32', arguments: ['i32'] }, + sum_3_i32: { return: 'i32', arguments: ['i32', 'i32', 'i32'] }, + sum_4_i32: { return: 'i32', arguments: ['i32', 'i32', 'i32', 'i32'] }, + sum_five_i32: { return: 'i32', arguments: ['i32', 'i32', 'i32', 'i32', 'i32'] }, }); try { assert.strictEqual(functions.logical_not(0), 1); @@ -689,8 +689,8 @@ test('mid-arity wrappers (1, 3, 4, 5)', () => { test('float specials: NaN, ±Infinity, -0 round-trip bit-exact', () => { const { lib, functions } = ffi.dlopen(libraryPath, { - add_f64: { result: 'f64', parameters: ['f64', 'f64'] }, - multiply_f64: { result: 'f64', parameters: ['f64', 'f64'] }, + add_f64: { return: 'f64', arguments: ['f64', 'f64'] }, + multiply_f64: { return: 'f64', arguments: ['f64', 'f64'] }, }); try { assert.ok(Number.isNaN(functions.add_f64(NaN, 1.0))); @@ -704,7 +704,7 @@ test('float specials: NaN, ±Infinity, -0 round-trip bit-exact', () => { test('arity-7+ branch still runs per-arg validation', () => { const { lib, functions } = ffi.dlopen(libraryPath, { - sum_7_i32: { result: 'i32', parameters: ['i32', 'i32', 'i32', 'i32', 'i32', 'i32', 'i32'] }, + sum_7_i32: { return: 'i32', arguments: ['i32', 'i32', 'i32', 'i32', 'i32', 'i32', 'i32'] }, }); try { assert.throws( @@ -720,7 +720,7 @@ test('mixed-kind signature (i32, f32, f64, u32) dispatches the right writer per // Four distinct `sbTypeInfo.kind` values (int, float, float, int) — a // wiring bug that reused one writer across slots would surface here. const { lib, functions } = ffi.dlopen(libraryPath, { - mixed_operation: { parameters: ['i32', 'f32', 'f64', 'u32'], result: 'f64' }, + mixed_operation: { arguments: ['i32', 'f32', 'f64', 'u32'], return: 'f64' }, }); try { @@ -748,11 +748,11 @@ test('lib.getFunctions() with no arguments wraps every cached function', () => { // the early-return path in `wrapWithSharedBuffer` alongside the wrapped // branch. const { lib } = ffi.dlopen(libraryPath, { - add_i32: { result: 'i32', parameters: ['i32', 'i32'] }, - add_f64: { result: 'f64', parameters: ['f64', 'f64'] }, - mixed_operation: { parameters: ['i32', 'f32', 'f64', 'u32'], result: 'f64' }, - identity_pointer: { result: 'pointer', parameters: ['pointer'] }, - string_length: { result: 'u64', parameters: ['string'] }, + add_i32: { return: 'i32', arguments: ['i32', 'i32'] }, + add_f64: { return: 'f64', arguments: ['f64', 'f64'] }, + mixed_operation: { arguments: ['i32', 'f32', 'f64', 'u32'], return: 'f64' }, + identity_pointer: { return: 'pointer', arguments: ['pointer'] }, + string_length: { return: 'u64', arguments: ['string'] }, }); try { @@ -789,12 +789,12 @@ test('lib.getFunctions() with no arguments wraps every cached function', () => { test('mixed pointer + numeric signature uses the pointer-dispatch wrapper', () => { const { lib, functions } = ffi.dlopen(libraryPath, { - call_int_callback: { result: 'i32', parameters: ['pointer', 'i32'] }, + call_int_callback: { return: 'i32', arguments: ['pointer', 'i32'] }, }); try { const cb = lib.registerCallback( - { result: 'i32', parameters: ['i32'] }, + { return: 'i32', arguments: ['i32'] }, (x) => x * 2, ); try { diff --git a/test/ffi/test-ffi-weakref-calls.js b/test/ffi/test-ffi-weakref-calls.js index d29ca1051fa450..1a2a2eaeab87cd 100644 --- a/test/ffi/test-ffi-weakref-calls.js +++ b/test/ffi/test-ffi-weakref-calls.js @@ -15,7 +15,7 @@ test('ffi unrefCallback releases callback function', async (t) => { let callback = () => 1; const ref = new WeakRef(callback); const pointer = lib.registerCallback( - { parameters: ['i32'], result: 'i32' }, + { arguments: ['i32'], return: 'i32' }, callback, ); @@ -37,7 +37,7 @@ test('ffi refCallback retains callback function', async (t) => { let callback = () => 1; const ref = new WeakRef(callback); - const pointer = lib.registerCallback({ result: 'i32' }, callback); + const pointer = lib.registerCallback({ return: 'i32' }, callback); lib.unrefCallback(pointer); lib.refCallback(pointer);