diff --git a/doc/api/ffi.md b/doc/api/ffi.md index 54de34da7a1..fcb39d223f0 100644 --- a/doc/api/ffi.md +++ b/doc/api/ffi.md @@ -63,22 +63,40 @@ Supported type names: * `void` * `char` -* `i8`, `int8` -* `u8`, `uint8`, `bool` -* `i16`, `int16` -* `u16`, `uint16` -* `i32`, `int32` -* `u32`, `uint32` -* `i64`, `int64` -* `u64`, `uint64` -* `f32`, `float`, `float32` -* `f64`, `double`, `float64` -* `pointer`, `ptr` -* `string`, `str` +* `int8` +* `uint8` +* `int16` +* `uint16` +* `int32` +* `uint32` +* `int64` +* `uint64` +* `float32` +* `float64` +* `pointer` +* `string` * `buffer` * `arraybuffer` * `function` +
+Alternative spellings + +* `i8` for `int8` +* `u8` and `bool` for `uint8` +* `i16` for `int16` +* `u16` for `uint16` +* `i32` for `int32` +* `u32` for `uint32` +* `i64` for `int64` +* `u64` for `uint64` +* `f32` and `float` for `float32` +* `f64` and `double` for `float64` +* `ptr` for `pointer` +* `str` for `string` + +
+ These type names are also exposed as constants on `ffi.types`: * `ffi.types.VOID` = `'void'` @@ -116,15 +134,15 @@ 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`. +is signed it behaves like `int8`; otherwise it behaves like `uint8`. 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. +On optimized Fast FFI calls, `pointer` 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 fall back to the generic FFI call path when a function's arguments or return type do not fit the platform-specific fast @@ -161,8 +179,8 @@ optional: ```js const signature = { - return: 'i32', - arguments: ['i32', 'i32'], + return: 'int32', + arguments: ['int32', 'int32'], }; ``` @@ -220,7 +238,7 @@ import { dlopen, suffix } from 'node:ffi'; { using handle = dlopen(`./mylib.${suffix}`, { - add_i32: { arguments: ['i32', 'i32'], return: 'i32' }, + add_i32: { arguments: ['int32', 'int32'], return: 'int32' }, }); console.log(handle.functions.add_i32(20, 22)); } // handle.lib.close() is invoked automatically here. @@ -230,8 +248,8 @@ import { dlopen, suffix } from 'node:ffi'; import { dlopen, suffix } from 'node:ffi'; const { lib, functions } = dlopen(`./mylib.${suffix}`, { - add_i32: { arguments: ['i32', 'i32'], return: 'i32' }, - string_length: { arguments: ['pointer'], return: 'u64' }, + add_i32: { arguments: ['int32', 'int32'], return: 'int32' }, + string_length: { arguments: ['pointer'], return: 'uint64' }, }); console.log(functions.add_i32(20, 22)); @@ -241,8 +259,8 @@ console.log(functions.add_i32(20, 22)); const { dlopen, suffix } = require('node:ffi'); const { lib, functions } = dlopen(`./mylib.${suffix}`, { - add_i32: { arguments: ['i32', 'i32'], return: 'i32' }, - string_length: { arguments: ['pointer'], return: 'u64' }, + add_i32: { arguments: ['int32', 'int32'], return: 'int32' }, + string_length: { arguments: ['pointer'], return: 'uint64' }, }); console.log(functions.add_i32(20, 22)); @@ -384,8 +402,8 @@ const { DynamicLibrary, suffix } = require('node:ffi'); const lib = new DynamicLibrary(`./mylib.${suffix}`); const add = lib.getFunction('add_i32', { - arguments: ['i32', 'i32'], - return: 'i32', + arguments: ['int32', 'int32'], + return: 'int32', }); console.log(add(20, 22)); @@ -435,7 +453,7 @@ const { DynamicLibrary, suffix } = require('node:ffi'); const lib = new DynamicLibrary(`./mylib.${suffix}`); const callback = lib.registerCallback( - { arguments: ['i32'], return: 'i32' }, + { arguments: ['int32'], return: 'int32' }, (value) => value * 2, ); ``` @@ -498,7 +516,8 @@ 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 64-bit integer types (`int64` and `uint64`), pass JavaScript `bigint` +values. For pointer-like arguments: diff --git a/lib/internal/ffi-shared-buffer.js b/lib/internal/ffi-shared-buffer.js index 94764bdf0db..13002193cd9 100644 --- a/lib/internal/ffi-shared-buffer.js +++ b/lib/internal/ffi-shared-buffer.js @@ -64,38 +64,40 @@ const gF64 = DataViewPrototypeGetFloat64; const sbTypeInfo = { __proto__: null, - i8: { set: sI8, get: gI8, kind: 'int', min: -128, max: 127, label: 'an int8' }, - int8: { set: sI8, get: gI8, kind: 'int', min: -128, max: 127, label: 'an int8' }, char: charIsSigned ? { set: sI8, get: gI8, kind: 'int', min: -128, max: 127, label: 'an int8' } : { set: sU8, get: gU8, kind: 'int', min: 0, max: 255, label: 'a uint8' }, - u8: { set: sU8, get: gU8, kind: 'int', min: 0, max: 255, label: 'a uint8' }, + int8: { set: sI8, get: gI8, kind: 'int', min: -128, max: 127, label: 'an int8' }, uint8: { set: sU8, get: gU8, kind: 'int', min: 0, max: 255, label: 'a uint8' }, + int16: { set: sI16, get: gI16, kind: 'int', min: -32768, max: 32767, label: 'an int16' }, + uint16: { set: sU16, get: gU16, kind: 'int', min: 0, max: 65535, label: 'a uint16' }, + int32: { set: sI32, get: gI32, kind: 'int', min: -2147483648, max: 2147483647, label: 'an int32' }, + uint32: { set: sU32, get: gU32, kind: 'int', min: 0, max: 4294967295, label: 'a uint32' }, + int64: { set: sI64, get: gI64, kind: 'i64', label: 'an int64' }, + uint64: { set: sU64, get: gU64, kind: 'u64', label: 'a uint64' }, + float32: { set: sF32, get: gF32, kind: 'float', label: 'a float' }, + float64: { set: sF64, get: gF64, kind: 'float', label: 'a double' }, + pointer: { set: sU64, get: gU64, kind: 'pointer' }, + string: { set: sU64, get: gU64, kind: 'pointer' }, + buffer: { set: sU64, get: gU64, kind: 'pointer' }, + arraybuffer: { set: sU64, get: gU64, kind: 'pointer' }, + function: { set: sU64, get: gU64, kind: 'pointer' }, + + // Alternative spellings. + i8: { set: sI8, get: gI8, kind: 'int', min: -128, max: 127, label: 'an int8' }, + u8: { set: sU8, get: gU8, kind: 'int', min: 0, max: 255, label: 'a uint8' }, bool: { set: sU8, get: gU8, kind: 'int', min: 0, max: 255, label: 'a uint8' }, i16: { set: sI16, get: gI16, kind: 'int', min: -32768, max: 32767, label: 'an int16' }, - int16: { set: sI16, get: gI16, kind: 'int', min: -32768, max: 32767, label: 'an int16' }, u16: { set: sU16, get: gU16, kind: 'int', min: 0, max: 65535, label: 'a uint16' }, - uint16: { set: sU16, get: gU16, kind: 'int', min: 0, max: 65535, label: 'a uint16' }, i32: { set: sI32, get: gI32, kind: 'int', min: -2147483648, max: 2147483647, label: 'an int32' }, - int32: { set: sI32, get: gI32, kind: 'int', min: -2147483648, max: 2147483647, label: 'an int32' }, u32: { set: sU32, get: gU32, kind: 'int', min: 0, max: 4294967295, label: 'a uint32' }, - uint32: { set: sU32, get: gU32, kind: 'int', min: 0, max: 4294967295, label: 'a uint32' }, i64: { set: sI64, get: gI64, kind: 'i64', label: 'an int64' }, - int64: { set: sI64, get: gI64, kind: 'i64', label: 'an int64' }, u64: { set: sU64, get: gU64, kind: 'u64', label: 'a uint64' }, - uint64: { set: sU64, get: gU64, kind: 'u64', label: 'a uint64' }, f32: { set: sF32, get: gF32, kind: 'float', label: 'a float' }, float: { set: sF32, get: gF32, kind: 'float', label: 'a float' }, - float32: { set: sF32, get: gF32, kind: 'float', label: 'a float' }, f64: { set: sF64, get: gF64, kind: 'float', label: 'a double' }, double: { set: sF64, get: gF64, kind: 'float', label: 'a double' }, - float64: { set: sF64, get: gF64, kind: 'float', label: 'a double' }, - pointer: { set: sU64, get: gU64, kind: 'pointer' }, ptr: { set: sU64, get: gU64, kind: 'pointer' }, - function: { set: sU64, get: gU64, kind: 'pointer' }, - buffer: { set: sU64, get: gU64, kind: 'pointer' }, - arraybuffer: { set: sU64, get: gU64, kind: 'pointer' }, - string: { set: sU64, get: gU64, kind: 'pointer' }, str: { set: sU64, get: gU64, kind: 'pointer' }, }; diff --git a/lib/internal/ffi/fast-api.js b/lib/internal/ffi/fast-api.js index 4b531dfe039..38f4f4e3437 100644 --- a/lib/internal/ffi/fast-api.js +++ b/lib/internal/ffi/fast-api.js @@ -47,26 +47,28 @@ const fastLibraryStates = new SafeWeakMap(); // conversions, so the public FFI ranges must be checked before the raw call. const fastIntegerTypeInfo = { __proto__: null, - i8: { kind: 'number', min: -128, max: 127, label: 'an int8' }, - int8: { kind: 'number', min: -128, max: 127, label: 'an int8' }, char: charIsSigned ? { kind: 'number', min: -128, max: 127, label: 'an int8' } : { kind: 'number', min: 0, max: 255, label: 'a uint8' }, - u8: { kind: 'number', min: 0, max: 255, label: 'a uint8' }, + int8: { kind: 'number', min: -128, max: 127, label: 'an int8' }, uint8: { kind: 'number', min: 0, max: 255, label: 'a uint8' }, + int16: { kind: 'number', min: -32768, max: 32767, label: 'an int16' }, + uint16: { kind: 'number', min: 0, max: 65535, label: 'a uint16' }, + int32: { kind: 'number', min: -2147483648, max: 2147483647, label: 'an int32' }, + uint32: { kind: 'number', min: 0, max: 4294967295, label: 'a uint32' }, + int64: { kind: 'bigint', min: I64_MIN, max: I64_MAX, label: 'an int64' }, + uint64: { kind: 'bigint', min: 0n, max: U64_MAX, label: 'a uint64' }, + + // Alternative spellings. + i8: { kind: 'number', min: -128, max: 127, label: 'an int8' }, + u8: { kind: 'number', min: 0, max: 255, label: 'a uint8' }, bool: { kind: 'number', min: 0, max: 255, label: 'a uint8' }, i16: { kind: 'number', min: -32768, max: 32767, label: 'an int16' }, - int16: { kind: 'number', min: -32768, max: 32767, label: 'an int16' }, u16: { kind: 'number', min: 0, max: 65535, label: 'a uint16' }, - uint16: { kind: 'number', min: 0, max: 65535, label: 'a uint16' }, i32: { kind: 'number', min: -2147483648, max: 2147483647, label: 'an int32' }, - int32: { kind: 'number', min: -2147483648, max: 2147483647, label: 'an int32' }, u32: { kind: 'number', min: 0, max: 4294967295, label: 'a uint32' }, - uint32: { kind: 'number', min: 0, max: 4294967295, label: 'a uint32' }, i64: { kind: 'bigint', min: I64_MIN, max: I64_MAX, label: 'an int64' }, - int64: { kind: 'bigint', min: I64_MIN, max: I64_MAX, label: 'an int64' }, u64: { kind: 'bigint', min: 0n, max: U64_MAX, label: 'a uint64' }, - uint64: { kind: 'bigint', min: 0n, max: U64_MAX, label: 'a uint64' }, }; function throwFFIArgError(msg) { @@ -97,8 +99,8 @@ function needsRawPointerConversion(type) { } function needsPointerLikeConversion(type) { - return type === 'pointer' || type === 'ptr' || type === 'function' || - type === 'buffer' || type === 'arraybuffer'; + return type === 'pointer' || type === 'function' || type === 'buffer' || + type === 'arraybuffer' || type === 'ptr'; } function needsStringPointerConversion(type) { diff --git a/src/ffi/fast.cc b/src/ffi/fast.cc index 8e92c56aebb..24809e744a2 100644 --- a/src/ffi/fast.cc +++ b/src/ffi/fast.cc @@ -36,34 +36,32 @@ bool FastScalarTypeFromName(std::string_view type, FastFFIType* out) { // JavaScript wrappers handle strings and object-to-pointer conversions. if (type == "void") { *out = FastFFIType::kVoid; - } else if (type == "bool") { - *out = FastFFIType::kUint8; - } else if (IsTypeName(type, {"i8", "int8"})) { - *out = FastFFIType::kInt8; - } else if (IsTypeName(type, {"u8", "uint8"})) { - *out = FastFFIType::kUint8; } else if (type == "char") { *out = CHAR_MIN < 0 ? FastFFIType::kInt8 : FastFFIType::kUint8; - } else if (IsTypeName(type, {"i16", "int16"})) { + } else if (IsTypeName(type, {"int8", "i8"})) { + *out = FastFFIType::kInt8; + } else if (IsTypeName(type, {"uint8", "u8", "bool"})) { + *out = FastFFIType::kUint8; + } else if (IsTypeName(type, {"int16", "i16"})) { *out = FastFFIType::kInt16; - } else if (IsTypeName(type, {"u16", "uint16"})) { + } else if (IsTypeName(type, {"uint16", "u16"})) { *out = FastFFIType::kUint16; - } else if (IsTypeName(type, {"i32", "int32"})) { + } else if (IsTypeName(type, {"int32", "i32"})) { *out = FastFFIType::kInt32; - } else if (IsTypeName(type, {"u32", "uint32"})) { + } else if (IsTypeName(type, {"uint32", "u32"})) { *out = FastFFIType::kUint32; - } else if (IsTypeName(type, {"i64", "int64"})) { + } else if (IsTypeName(type, {"int64", "i64"})) { *out = FastFFIType::kInt64; - } else if (IsTypeName(type, {"u64", "uint64"})) { + } else if (IsTypeName(type, {"uint64", "u64"})) { *out = FastFFIType::kUint64; - } else if (IsTypeName(type, {"f32", "float", "float32"})) { + } else if (IsTypeName(type, {"float32", "f32", "float"})) { *out = FastFFIType::kFloat32; - } else if (IsTypeName(type, {"f64", "double", "float64"})) { + } else if (IsTypeName(type, {"float64", "f64", "double"})) { *out = FastFFIType::kFloat64; } else if (IsTypeName(type, {"buffer", "arraybuffer"})) { *out = FastFFIType::kPointer; } else if (IsTypeName(type, - {"pointer", "ptr", "string", "str", "function"})) { + {"pointer", "string", "function", "ptr", "str"})) { *out = FastFFIType::kPointer; } else { return false; @@ -162,11 +160,12 @@ bool SignatureNeedsFastIntegerValidation(const FFIFunction& fn) { // V8 widens narrow integers to 32 bits and truncates BigInts to 64 bits for // Fast API calls. These types need a JS range check before the trampoline. for (const std::string& name : fn.arg_type_names) { - if (name == "bool" || name == "char" || name == "i8" || name == "int8" || - name == "u8" || name == "uint8" || name == "i16" || name == "int16" || - name == "u16" || name == "uint16" || name == "i32" || name == "int32" || - name == "u32" || name == "uint32" || name == "i64" || name == "int64" || - name == "u64" || name == "uint64") { + if (name == "char" || name == "int8" || name == "uint8" || + name == "int16" || name == "uint16" || name == "int32" || + name == "uint32" || name == "int64" || name == "uint64" || + name == "i8" || name == "u8" || name == "bool" || name == "i16" || + name == "u16" || name == "i32" || name == "u32" || name == "i64" || + name == "u64") { return true; } } @@ -176,7 +175,7 @@ bool SignatureNeedsFastIntegerValidation(const FFIFunction& fn) { bool IsPointerTypeName(const std::string& name) { // `pointer`, `ptr`, and `function` all use the same uintptr ABI slot; only // the public type spelling differs. - return name == "pointer" || name == "ptr" || name == "function"; + return name == "pointer" || name == "function" || name == "ptr"; } bool IsBufferTypeName(const std::string& name) { diff --git a/src/ffi/types.cc b/src/ffi/types.cc index db0c913c547..83be0f48ecc 100644 --- a/src/ffi/types.cc +++ b/src/ffi/types.cc @@ -552,34 +552,33 @@ void WriteFFIReturnToBuffer(ffi_type* type, v8::Maybe ToFFIType(Environment* env, std::string_view type_str) { if (type_str == "void") { return Just(&ffi_type_void); - } else if (type_str == "i8" || type_str == "int8") { - return Just(&ffi_type_sint8); - } else if (type_str == "u8" || type_str == "uint8" || type_str == "bool") { - return Just(&ffi_type_uint8); } else if (type_str == "char") { return Just(CHAR_MIN < 0 ? &ffi_type_sint8 : &ffi_type_uint8); - } else if (type_str == "i16" || type_str == "int16") { + } else if (type_str == "int8" || type_str == "i8") { + return Just(&ffi_type_sint8); + } else if (type_str == "uint8" || type_str == "u8" || type_str == "bool") { + return Just(&ffi_type_uint8); + } else if (type_str == "int16" || type_str == "i16") { return Just(&ffi_type_sint16); - } else if (type_str == "u16" || type_str == "uint16") { + } else if (type_str == "uint16" || type_str == "u16") { return Just(&ffi_type_uint16); - } else if (type_str == "i32" || type_str == "int32") { + } else if (type_str == "int32" || type_str == "i32") { return Just(&ffi_type_sint32); - } else if (type_str == "u32" || type_str == "uint32") { + } else if (type_str == "uint32" || type_str == "u32") { return Just(&ffi_type_uint32); - } else if (type_str == "i64" || type_str == "int64") { + } else if (type_str == "int64" || type_str == "i64") { return Just(&ffi_type_sint64); - } else if (type_str == "u64" || type_str == "uint64") { + } else if (type_str == "uint64" || type_str == "u64") { return Just(&ffi_type_uint64); - } else if (type_str == "f32" || type_str == "float" || - type_str == "float32") { + } else if (type_str == "float32" || type_str == "f32" || + type_str == "float") { return Just(&ffi_type_float); - } else if (type_str == "f64" || type_str == "double" || - type_str == "float64") { + } else if (type_str == "float64" || type_str == "f64" || + type_str == "double") { return Just(&ffi_type_double); - } else if (type_str == "buffer" || type_str == "arraybuffer" || - type_str == "string" || type_str == "str" || - type_str == "pointer" || type_str == "ptr" || - type_str == "function") { + } else if (type_str == "pointer" || type_str == "string" || + type_str == "buffer" || type_str == "arraybuffer" || + type_str == "function" || type_str == "ptr" || type_str == "str") { return Just(&ffi_type_pointer); } else { THROW_ERR_INVALID_ARG_VALUE(env, "Unsupported FFI type: %s", type_str);