Version
main
Platform
Subsystem
ffi
What steps will reproduce the bug?
repro.c
#include <stdint.h>
int8_t i8(int8_t x) {
return x;
}
uint8_t u8(uint8_t x) {
return x;
}
int16_t i16(int16_t x) {
return x;
}
uint16_t u16(uint16_t x) {
return x;
}
int64_t i64(int64_t x) {
return x;
}
Compile it by running
$ clang -dynamiclib repro.c -o librepro.dylib
repro.js
import { dlopen } from 'node:ffi';
const { functions } = dlopen(`./librepro.dylib`, {
i8: { arguments: ['i8'], return: 'i8' },
u8: { arguments: ['u8'], return: 'u8' },
i16: { arguments: ['i16'], return: 'i16' },
u16: { arguments: ['u16'], return: 'u16' },
i64: { arguments: ['i64'], return: 'i64' },
});
function callI8(x) { return functions.i8(x); }
function callU8(x) { return functions.u8(x); }
function callI16(x) { return functions.i16(x); }
function callU16(x) { return functions.u16(x); }
function callI64(x) { return functions.i64(x); }
function optimize(fn, value) {
eval('%PrepareFunctionForOptimization(fn)');
fn(value);
fn(value);
eval('%OptimizeFunctionOnNextCall(fn)');
fn(value);
}
for (const [fn, value] of [
[callI8, 0],
[callU8, 0],
[callI16, 0],
[callU16, 0],
[callI64, 0n],
]) {
optimize(fn, value);
}
console.log('i8:', callI8(128));
console.log('u8:', callU8(256));
console.log('i16:', callI16(32768));
console.log('u16:', callU16(65536));
console.log('i64:', callI64(2n ** 63n));
Run it
$ node --no-warnings --experimental-ffi --allow-natives-syntax repro.js
How often does it reproduce? Is there a required condition?
Always
What is the expected behavior? Why is that the expected behavior?
Each call should throw ERR_INVALID_ARG_VALUE, as the generic FFI path does.
What do you see instead?
i8: -128
u8: 0
i16: -32768
u16: 0
i64: -9223372036854775808n
optimized FFI calls silently truncate or wrap every out-of-range argument
Additional information
No response
Version
main
Platform
Subsystem
ffi
What steps will reproduce the bug?
repro.cCompile it by running
repro.jsRun it
How often does it reproduce? Is there a required condition?
Always
What is the expected behavior? Why is that the expected behavior?
Each call should throw
ERR_INVALID_ARG_VALUE, as the generic FFI path does.What do you see instead?
optimized FFI calls silently truncate or wrap every out-of-range argument
Additional information
No response