Skip to content

Commit 972cd22

Browse files
Renegade334aduh95
authored andcommitted
ffi: remove function signature property aliases
Signed-off-by: Renegade334 <contact.9a5d6388@renegade334.me.uk> PR-URL: #63482 Refs: https://github.com/nodejs/node/pull/62072/changes#r3067834658 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Paolo Insogna <paolo@cowtech.it>
1 parent fdc4b5a commit 972cd22

10 files changed

Lines changed: 254 additions & 343 deletions

‎doc/api/ffi.md‎

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -124,18 +124,18 @@ such as `0` and `1`; JavaScript `true` and `false` are not accepted.
124124

125125
Functions and callbacks are described with signature objects.
126126

127-
Supported fields:
127+
Signature objects may contain the following properties, both of which are
128+
optional:
128129

129-
*`result`, `return`, or `returns` for the return type.
130-
*`parameters` or `arguments` for the parameter type list.
130+
*`return` {string} A [type name][type names] specifying the return type of the
131+
function or callback. **Default:**`'void'`.
132+
*`arguments` {string\[]} An array of [type names][] specifying the argument
133+
type list of the function or callback. **Default:**`[]`.
131134

132-
Only one return-type field and one parameter-list field may be present in a
133-
single signature object.
134-
135-
```cjs
135+
```js
136136
constsignature= {
137-
result:'i32',
138-
parameters: ['i32', 'i32'],
137+
return:'i32',
138+
arguments: ['i32', 'i32'],
139139
};
140140
```
141141

@@ -193,7 +193,7 @@ import { dlopen } from 'node:ffi';
193193

194194
{
195195
using handle =dlopen('./mylib.so', {
196-
add_i32: { parameters: ['i32', 'i32'], result:'i32' },
196+
add_i32: { arguments: ['i32', 'i32'], return:'i32' },
197197
});
198198
console.log(handle.functions.add_i32(20, 22));
199199
} // handle.lib.close() is invoked automatically here.
@@ -203,8 +203,8 @@ import { dlopen } from 'node:ffi';
203203
import { dlopen } from'node:ffi';
204204

205205
const { lib, functions } =dlopen('./mylib.so', {
206-
add_i32: { parameters: ['i32', 'i32'], result:'i32' },
207-
string_length: { parameters: ['pointer'], result:'u64' },
206+
add_i32: { arguments: ['i32', 'i32'], return:'i32' },
207+
string_length: { arguments: ['pointer'], return:'u64' },
208208
});
209209

210210
console.log(functions.add_i32(20, 22));
@@ -214,8 +214,8 @@ console.log(functions.add_i32(20, 22));
214214
const { dlopen } =require('node:ffi');
215215

216216
const { lib, functions } =dlopen('./mylib.so', {
217-
add_i32: { parameters: ['i32', 'i32'], result:'i32' },
218-
string_length: { parameters: ['pointer'], result:'u64' },
217+
add_i32: { arguments: ['i32', 'i32'], return:'i32' },
218+
string_length: { arguments: ['pointer'], return:'u64' },
219219
});
220220

221221
console.log(functions.add_i32(20, 22));
@@ -356,8 +356,8 @@ const { DynamicLibrary } = require('node:ffi');
356356

357357
constlib=newDynamicLibrary('./mylib.so');
358358
constadd=lib.getFunction('add_i32', {
359-
parameters: ['i32', 'i32'],
360-
result:'i32',
359+
arguments: ['i32', 'i32'],
360+
return:'i32',
361361
});
362362

363363
console.log(add(20, 22));
@@ -407,7 +407,7 @@ const { DynamicLibrary } = require('node:ffi');
407407
constlib=newDynamicLibrary('./mylib.so');
408408

409409
constcallback=lib.registerCallback(
410-
{ parameters: ['i32'], result:'i32' },
410+
{ arguments: ['i32'], return:'i32' },
411411
(value) => value *2,
412412
);
413413
```
@@ -417,7 +417,7 @@ Callbacks are subject to the following restrictions:
417417
* They must be invoked on the same system thread where they were created.
418418
* They must not throw exceptions.
419419
* They must not return promises.
420-
* They must return a value compatible with the declared result type.
420+
* They must return a value compatible with the declared return type.
421421
* They must not call `library.close()` on their owning library while running.
422422
* They must not unregister themselves while running.
423423

@@ -465,7 +465,7 @@ JavaScript `number` values that match the declared type.
465465

466466
For 64-bit integer types (`i64` and `u64`), pass JavaScript `bigint` values.
467467

468-
For pointer-like parameters:
468+
For pointer-like arguments:
469469

470470
*`null` and `undefined` are passed as null pointers.
471471
*`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.
727727
[`--allow-ffi`]: cli.md#--allow-ffi
728728
[`ffi.toBuffer(pointer, length, copy)`]: #ffitobufferpointer-length-copy
729729
[`using`]: https://tc39.es/proposal-explicit-resource-management/#sec-using-declarations
730+
[type names]: #type-names

‎lib/internal/ffi-shared-buffer.js‎

Lines changed: 43 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,14 @@ const {
3131
TypeError,
3232
}=primordials;
3333

34-
const{
35-
codes: {
36-
ERR_INTERNAL_ASSERTION,
37-
},
38-
}=require('internal/errors');
34+
constassert=require('internal/assert');
3935

4036
const{
4137
DynamicLibrary,
4238
charIsSigned,
39+
kSbArguments,
4340
kSbInvokeSlow,
44-
kSbParams,
45-
kSbResult,
41+
kSbReturn,
4642
kSbSharedBuffer,
4743
uintptrMax,
4844
}=internalBinding('ffi');
@@ -159,11 +155,9 @@ function writeNumericArg(view, info, offset, arg, index) {
159155
return;
160156
}
161157

162-
/* c8 ignore start */
163158
// Unreachable: caller filters out non-numeric kinds.
164-
thrownewERR_INTERNAL_ASSERTION(
165-
`FFI: writeNumericArg reached with unexpected kind="${kind}"`);
166-
/* c8 ignore stop */
159+
/* c8 ignore next */
160+
assert.fail(`FFI: writeNumericArg reached with unexpected kind="${kind}"`);
167161
}
168162

169163
// Returns true on fast-path success, false when the caller must fall back
@@ -208,51 +202,46 @@ function inheritMetadata(wrapper, rawFn, nargs) {
208202
// arguments out of it into invocation-local storage before `ffi_call` and
209203
// reads the return value back only after, so nested/reentrant calls into
210204
// the same function are safe.
211-
functionwrapWithSharedBuffer(rawFn,parameters,resultType){
212-
if(rawFn===undefined||rawFn===null)returnrawFn;
205+
functionwrapWithSharedBuffer(rawFn,signature){
206+
if(rawFn==null)returnrawFn;
213207
constbuffer=rawFn[kSbSharedBuffer];
214208
if(buffer===undefined)returnrawFn;
215209

216210
// Callers without explicit signature info (the `functions` accessor
217-
// patch below) rely on the `kSbParams` / `kSbResult` metadata attached
211+
// patch below) rely on the `kSbArguments` / `kSbReturn` metadata attached
218212
// by the native `CreateFunction`.
219-
if(parameters===undefined)parameters=rawFn[kSbParams];
220-
if(resultType===undefined)resultType=rawFn[kSbResult];
221-
// `CreateFunction` always attaches these for SB-eligible functions.
222-
// Missing here means the native side and this wrapper are out of sync.
223-
/* c8 ignore start */
224-
if(parameters===undefined||resultType===undefined){
225-
thrownewERR_INTERNAL_ASSERTION(
226-
'FFI: shared-buffer raw function is missing kSbParams or kSbResult');
213+
letargumentTypes,returnType;
214+
if(signature===undefined){
215+
argumentTypes=rawFn[kSbArguments];
216+
returnType=rawFn[kSbReturn];
217+
218+
// `CreateFunction` always attaches these for SB-eligible functions.
219+
// Missing here means the native side and this wrapper are out of sync.
220+
assert(argumentTypes!==undefined&&returnType!==undefined,
221+
'FFI: shared-buffer raw function is missing kSbArguments or kSbReturn');
222+
}else{
223+
argumentTypes=signature.arguments??[];
224+
returnType=signature.return??'void';
227225
}
228-
/* c8 ignore stop */
229226

230227
constslowInvoke=rawFn[kSbInvokeSlow];
231228
constview=newDataView(buffer);
232229
letretGetter=null;
233-
if(resultType!=='void'){
234-
constretInfo=sbTypeInfo[resultType];
235-
/* c8 ignore start */
236-
if(retInfo===undefined){
237-
thrownewERR_INTERNAL_ASSERTION(
238-
`FFI: shared-buffer type table missing entry for result type "${resultType}"`);
239-
}
240-
/* c8 ignore stop */
230+
if(returnType!=='void'){
231+
constretInfo=sbTypeInfo[returnType];
232+
assert(retInfo!==undefined,
233+
`FFI: shared-buffer type table missing entry for return type "${returnType}"`);
241234
retGetter=retInfo.get;
242235
}
243236

244-
constnargs=parameters.length;
237+
constnargs=argumentTypes.length;
245238
constargInfos=[];
246239
constargOffsets=[];
247240
letanyPointer=false;
248241
for(leti=0;i<nargs;i++){
249-
constinfo=sbTypeInfo[parameters[i]];
250-
/* c8 ignore start */
251-
if(info===undefined){
252-
thrownewERR_INTERNAL_ASSERTION(
253-
`FFI: shared-buffer type table missing entry for parameter type "${parameters[i]}"`);
254-
}
255-
/* c8 ignore stop */
242+
constinfo=sbTypeInfo[argumentTypes[i]];
243+
assert(info!==undefined,
244+
`FFI: shared-buffer type table missing entry for argument type "${argumentTypes[i]}"`);
256245
// Push the `sbTypeInfo` entry directly (entries with the same `kind`
257246
// share a shape, keeping `writeNumericArg`'s call sites
258247
// low-polymorphism) and store offsets in a parallel array to avoid
@@ -267,13 +256,8 @@ function wrapWithSharedBuffer(rawFn, parameters, resultType) {
267256
// Pointer signatures need a per-arg runtime type check and fall back
268257
// to the native slow-path invoker for non-BigInt pointer arguments,
269258
// so arity specialization wouldn't buy much here.
270-
/* c8 ignore start */
271-
if(slowInvoke===undefined){
272-
thrownewERR_INTERNAL_ASSERTION(
273-
'FFI: shared-buffer raw function with pointer arguments is '+
274-
'missing kSbInvokeSlow');
275-
}
276-
/* c8 ignore stop */
259+
assert(slowInvoke!==undefined,
260+
'FFI: shared-buffer raw function with pointer arguments is missing kSbInvokeSlow');
277261
wrapper=function(...args){
278262
if(args.length!==nargs){
279263
throwFFIArgCountError(nargs,args.length);
@@ -542,19 +526,6 @@ function buildNumericWrapper(
542526
};
543527
}
544528

545-
// Accept-set mirrors the native `ParseFunctionSignature` in
546-
// `src/ffi/types.cc`. `ParseFunctionSignature` additionally throws when
547-
// multiple aliases are set at once. The wrapper runs before the native
548-
// call, so those conflicts still surface from the native side regardless
549-
// of which alias we happen to read here.
550-
functionsigParams(sig){
551-
returnsig.parameters??sig.arguments??[];
552-
}
553-
554-
functionsigResult(sig){
555-
returnsig.result??sig.return??sig.returns??'void';
556-
}
557-
558529
// The native invoker for SB-eligible symbols is `InvokeFunctionSB`, which
559530
// reads arguments from the shared buffer populated by
560531
// `wrapWithSharedBuffer`. These patches make sure every path that surfaces
@@ -563,11 +534,11 @@ function sigResult(sig) {
563534
constrawGetFunction=DynamicLibrary.prototype.getFunction;
564535
constrawGetFunctions=DynamicLibrary.prototype.getFunctions;
565536

566-
DynamicLibrary.prototype.getFunction=functiongetFunction(name,sig){
567-
// Native `DynamicLibrary::GetFunction` validates `sig`, so by the time
568-
// we have `raw` we know `sig` is a valid object.
569-
constraw=FunctionPrototypeCall(rawGetFunction,this,name,sig);
570-
returnwrapWithSharedBuffer(raw,sigParams(sig),sigResult(sig));
537+
DynamicLibrary.prototype.getFunction=functiongetFunction(name,signature){
538+
// Native `DynamicLibrary::GetFunction` validates `signature`, so by the time
539+
// we have `raw` we know `signature` is a valid object.
540+
constraw=FunctionPrototypeCall(rawGetFunction,this,name,signature);
541+
returnwrapWithSharedBuffer(raw,signature);
571542
};
572543

573544
DynamicLibrary.prototype.getFunctions=functiongetFunctions(definitions){
@@ -583,13 +554,12 @@ DynamicLibrary.prototype.getFunctions = function getFunctions(definitions) {
583554
for(leti=0;i<keys.length;i++){
584555
constname=keys[i];
585556
// No `definitions`: native side returned every cached function, so we
586-
// wrap using each function's own `kSbParams` / `kSbResult` metadata
557+
// wrap using each function's own `kSbArguments` / `kSbReturn` metadata
587558
// (same fallback as the `functions` accessor).
588559
if(definitions===undefined){
589560
out[name]=wrapWithSharedBuffer(raw[name]);
590561
}else{
591-
constsig=definitions[name];
592-
out[name]=wrapWithSharedBuffer(raw[name],sigParams(sig),sigResult(sig));
562+
out[name]=wrapWithSharedBuffer(raw[name],definitions[name]);
593563
}
594564
}
595565
returnout;
@@ -602,16 +572,12 @@ DynamicLibrary.prototype.getFunctions = function getFunctions(definitions) {
602572
// uninitialized buffer.
603573
constfunctionsDescriptor=
604574
ObjectGetOwnPropertyDescriptor(DynamicLibrary.prototype,'functions');
605-
/* c8 ignore start */
606-
if(functionsDescriptor===undefined||!functionsDescriptor.get){
607-
// Missing getter means the native and JS sides are out of sync; silently
608-
// skipping the patch would expose the fast-path-against-uninitialized-buffer
609-
// footgun this whole block exists to prevent.
610-
thrownewERR_INTERNAL_ASSERTION(
611-
'FFI: DynamicLibrary.prototype.functions accessor not found or has no getter');
612-
}
613-
/* c8 ignore stop */
614-
constorigGetter=functionsDescriptor.get;
575+
constorigGetter=functionsDescriptor?.get;
576+
// Missing getter means the native and JS sides are out of sync; silently
577+
// skipping the patch would expose the fast-path-against-uninitialized-buffer
578+
// footgun this whole block exists to prevent.
579+
assert(origGetter!==undefined,
580+
'FFI: DynamicLibrary.prototype.functions accessor not found or has no getter');
615581
ObjectDefineProperty(DynamicLibrary.prototype,'functions',{
616582
__proto__: null,
617583
configurable: true,

‎src/env_properties.h‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@
4848
V(async_id_symbol, "async_id_symbol") \
4949
V(ffi_sb_shared_buffer_symbol, "ffi_sb_shared_buffer_symbol") \
5050
V(ffi_sb_invoke_slow_symbol, "ffi_sb_invoke_slow_symbol") \
51-
V(ffi_sb_params_symbol, "ffi_sb_params_symbol") \
52-
V(ffi_sb_result_symbol, "ffi_sb_result_symbol") \
51+
V(ffi_sb_arguments_symbol, "ffi_sb_arguments_symbol") \
52+
V(ffi_sb_return_symbol, "ffi_sb_return_symbol") \
5353
V(constructor_key_symbol, "constructor_key_symbol") \
5454
V(handle_onclose_symbol, "handle_onclose") \
5555
V(no_message_symbol, "no_message_symbol") \
@@ -293,7 +293,6 @@
293293
V(password_string, "password") \
294294
V(path_string, "path") \
295295
V(pathname_string, "pathname") \
296-
V(parameters_string, "parameters") \
297296
V(pending_handle_string, "pendingHandle") \
298297
V(permission_string, "permission") \
299298
V(phase_string, "phase") \
@@ -329,9 +328,8 @@
329328
V(require_string, "require") \
330329
V(resource_string, "resource") \
331330
V(result_string, "result") \
332-
V(return_string, "return") \
333-
V(returns_string, "returns") \
334331
V(return_arrays_string, "returnArrays") \
332+
V(return_string, "return") \
335333
V(salt_length_string, "saltLength") \
336334
V(search_string, "search") \
337335
V(servername_string, "servername") \

0 commit comments

Comments
 (0)