mirror of
https://github.com/nodejs/node.git
synced 2026-11-09 15:11:11 -05:00
ffi-test-common.js duplicated the platform branch already exposed as ffi.suffix, risking drift if lib/ffi.js changes. Reuse it, and require it after skipIfFFIMissing() so a build without FFI skips instead of throwing. Also assert ffi.suffix resolves to the correct value per platform, since test-ffi-module.js only checked that the key exists. Signed-off-by: leah-1ee <dltjddms1028@gmail.com> PR-URL: https://github.com/nodejs/node/pull/64840 Reviewed-By: Paolo Insogna <paolo@cowtech.it> Reviewed-By: Aviv Keller <me@aviv.sh>
224 lines
6.8 KiB
JavaScript
224 lines
6.8 KiB
JavaScript
// Flags: --experimental-ffi
|
|
'use strict';
|
|
const common = require('../common');
|
|
const { spawnSyncAndAssert } = require('../common/child_process');
|
|
const assert = require('node:assert');
|
|
const { spawnSync } = require('node:child_process');
|
|
const { test } = require('node:test');
|
|
const { Worker } = require('node:worker_threads');
|
|
|
|
common.skipIfFFIMissing();
|
|
|
|
test('ffi cannot be loaded without node: prefix', () => {
|
|
assert.throws(() => {
|
|
require('ffi');
|
|
}, {
|
|
code: 'MODULE_NOT_FOUND',
|
|
message: /Cannot find module 'ffi'/,
|
|
});
|
|
});
|
|
|
|
test('ffi builtin is unavailable when disabled', () => {
|
|
const { stdout, stderr, status, signal } = spawnSync(process.execPath, [
|
|
'--no-experimental-ffi',
|
|
'-e',
|
|
'require("node:ffi")',
|
|
], {
|
|
encoding: 'utf8',
|
|
});
|
|
|
|
assert.strictEqual(stdout, '');
|
|
assert.match(stderr, /No such built-in module: node:ffi/);
|
|
assert.notStrictEqual(status, 0);
|
|
assert.strictEqual(signal, null);
|
|
});
|
|
|
|
test('ffi builtin is listed', () => {
|
|
for (const [flag, stdout] of Object.entries({
|
|
'--experimental-ffi': 'true\n',
|
|
'--no-experimental-ffi': 'false\n',
|
|
})) {
|
|
spawnSyncAndAssert(process.execPath, [
|
|
flag,
|
|
'-p',
|
|
'require("node:module").builtinModules.includes("node:ffi")',
|
|
], { stdout });
|
|
}
|
|
});
|
|
|
|
test('ffi can be imported from ESM', () => {
|
|
const { stdout, stderr, status, signal } = spawnSync(process.execPath, [
|
|
'--experimental-ffi',
|
|
'--input-type=module',
|
|
'-e',
|
|
'import * as ffi from "node:ffi"; console.log(typeof ffi.dlopen);',
|
|
], {
|
|
encoding: 'utf8',
|
|
});
|
|
|
|
assert.strictEqual(stdout.trim(), 'function');
|
|
assert.match(stderr, /ExperimentalWarning: FFI is an experimental feature/);
|
|
assert.strictEqual(status, 0);
|
|
assert.strictEqual(signal, null);
|
|
});
|
|
|
|
test('DynamicLibrary requires new', () => {
|
|
const { stdout, stderr, status, signal } = spawnSync(process.execPath, [
|
|
'--experimental-ffi',
|
|
'-e',
|
|
'const ffi = require("node:ffi"); ffi.DynamicLibrary("missing");',
|
|
], {
|
|
encoding: 'utf8',
|
|
});
|
|
|
|
assert.strictEqual(stdout, '');
|
|
assert.match(stderr, /Class constructor DynamicLibrary cannot be invoked without 'new'/);
|
|
assert.notStrictEqual(status, 0);
|
|
assert.strictEqual(signal, null);
|
|
});
|
|
|
|
test('ffi exports expected API surface', () => {
|
|
const ffi = require('node:ffi');
|
|
const expected = [
|
|
'DynamicLibrary',
|
|
'dlclose',
|
|
'dlopen',
|
|
'dlsym',
|
|
'exportArrayBuffer',
|
|
'exportArrayBufferView',
|
|
'exportBuffer',
|
|
'exportString',
|
|
'getCurrentEventLoop',
|
|
'getFloat32',
|
|
'getFloat64',
|
|
'getInt16',
|
|
'getInt32',
|
|
'getInt64',
|
|
'getInt8',
|
|
'getRawPointer',
|
|
'getUint16',
|
|
'getUint32',
|
|
'getUint64',
|
|
'getUint8',
|
|
'setFloat32',
|
|
'setFloat64',
|
|
'setInt16',
|
|
'setInt32',
|
|
'setInt64',
|
|
'setInt8',
|
|
'setUint16',
|
|
'setUint32',
|
|
'setUint64',
|
|
'setUint8',
|
|
'suffix',
|
|
'toArrayBuffer',
|
|
'toBuffer',
|
|
'toString',
|
|
'types',
|
|
];
|
|
|
|
assert.deepStrictEqual(Object.keys(ffi).sort(), expected);
|
|
assert.strictEqual(typeof ffi.DynamicLibrary, 'function');
|
|
assert.strictEqual(typeof ffi.dlopen, 'function');
|
|
assert.strictEqual(typeof ffi.dlclose, 'function');
|
|
assert.strictEqual(typeof ffi.dlsym, 'function');
|
|
assert.strictEqual(typeof ffi.exportArrayBuffer, 'function');
|
|
assert.strictEqual(typeof ffi.exportArrayBufferView, 'function');
|
|
assert.strictEqual(typeof ffi.exportString, 'function');
|
|
assert.strictEqual(typeof ffi.exportBuffer, 'function');
|
|
assert.strictEqual(typeof ffi.getRawPointer, 'function');
|
|
assert.strictEqual(typeof ffi.getInt8, 'function');
|
|
assert.strictEqual(typeof ffi.getUint8, 'function');
|
|
assert.strictEqual(typeof ffi.getInt16, 'function');
|
|
assert.strictEqual(typeof ffi.getUint16, 'function');
|
|
assert.strictEqual(typeof ffi.getInt32, 'function');
|
|
assert.strictEqual(typeof ffi.getUint32, 'function');
|
|
assert.strictEqual(typeof ffi.getInt64, 'function');
|
|
assert.strictEqual(typeof ffi.getUint64, 'function');
|
|
assert.strictEqual(typeof ffi.getFloat32, 'function');
|
|
assert.strictEqual(typeof ffi.getFloat64, 'function');
|
|
assert.strictEqual(typeof ffi.getCurrentEventLoop, 'function');
|
|
assert.strictEqual(typeof ffi.setInt8, 'function');
|
|
assert.strictEqual(typeof ffi.setUint8, 'function');
|
|
assert.strictEqual(typeof ffi.setInt16, 'function');
|
|
assert.strictEqual(typeof ffi.setUint16, 'function');
|
|
assert.strictEqual(typeof ffi.setInt32, 'function');
|
|
assert.strictEqual(typeof ffi.setUint32, 'function');
|
|
assert.strictEqual(typeof ffi.setInt64, 'function');
|
|
assert.strictEqual(typeof ffi.setUint64, 'function');
|
|
assert.strictEqual(typeof ffi.setFloat32, 'function');
|
|
assert.strictEqual(typeof ffi.setFloat64, 'function');
|
|
assert.strictEqual(typeof ffi.toString, 'function');
|
|
assert.strictEqual(typeof ffi.toBuffer, 'function');
|
|
assert.strictEqual(typeof ffi.toArrayBuffer, 'function');
|
|
assert.strictEqual(typeof ffi.types, 'object');
|
|
});
|
|
|
|
test('ffi.suffix matches the current platform', () => {
|
|
const ffi = require('node:ffi');
|
|
const expected = process.platform === 'win32' ? 'dll' :
|
|
process.platform === 'darwin' ? 'dylib' : 'so';
|
|
|
|
assert.strictEqual(ffi.suffix, expected);
|
|
});
|
|
|
|
test('ffi.types exports canonical type constants', () => {
|
|
const ffi = require('node:ffi');
|
|
const expected = {
|
|
__proto__: null,
|
|
VOID: 'void',
|
|
POINTER: 'pointer',
|
|
BUFFER: 'buffer',
|
|
ARRAY_BUFFER: 'arraybuffer',
|
|
FUNCTION: 'function',
|
|
BOOL: 'bool',
|
|
CHAR: 'char',
|
|
STRING: 'string',
|
|
FLOAT: 'float',
|
|
DOUBLE: 'double',
|
|
INT_8: 'int8',
|
|
UINT_8: 'uint8',
|
|
INT_16: 'int16',
|
|
UINT_16: 'uint16',
|
|
INT_32: 'int32',
|
|
UINT_32: 'uint32',
|
|
INT_64: 'int64',
|
|
UINT_64: 'uint64',
|
|
FLOAT_32: 'float32',
|
|
FLOAT_64: 'float64',
|
|
};
|
|
|
|
assert.deepStrictEqual(ffi.types, expected);
|
|
assert.strictEqual(Object.isFrozen(ffi.types), true);
|
|
});
|
|
|
|
test('ffi.getCurrentEventLoop returns the current thread event loop address', async () => {
|
|
const ffi = require('node:ffi');
|
|
const mainLoop = ffi.getCurrentEventLoop();
|
|
|
|
assert.strictEqual(typeof mainLoop, 'bigint');
|
|
assert.ok(mainLoop > 0n);
|
|
assert.strictEqual(ffi.getCurrentEventLoop(), mainLoop);
|
|
|
|
const workerLoop = await new Promise((resolve, reject) => {
|
|
const worker = new Worker(`
|
|
const { parentPort } = require('node:worker_threads');
|
|
const ffi = require('node:ffi');
|
|
const loop = ffi.getCurrentEventLoop();
|
|
|
|
parentPort.postMessage([loop, ffi.getCurrentEventLoop()]);
|
|
`, { eval: true });
|
|
|
|
worker.on('message', resolve);
|
|
worker.on('error', reject);
|
|
worker.on('exit', (code) => {
|
|
if (code !== 0) reject(new Error(`Worker stopped with exit code ${code}`));
|
|
});
|
|
});
|
|
|
|
assert.strictEqual(typeof workerLoop[0], 'bigint');
|
|
assert.ok(workerLoop[0] > 0n);
|
|
assert.strictEqual(workerLoop[0], workerLoop[1]);
|
|
assert.notStrictEqual(workerLoop[0], mainLoop);
|
|
});
|