koffi
Version:
Fast and simple C FFI (foreign function interface) for Node.js
158 lines (140 loc) • 4.26 kB
JavaScript
const assert = require('assert');
const asyncHook = require('async_hooks');
module.exports = require('./common').runTest(async (binding) => {
await test(binding.functionreference);
});
function installAsyncHook () {
let id;
let destroyed;
let hook;
const events = [];
return new Promise((resolve, reject) => {
const interval = setInterval(() => {
if (destroyed) {
hook.disable();
clearInterval(interval);
resolve(events);
}
}, 10);
hook = asyncHook
.createHook({
init (asyncId, type, triggerAsyncId, resource) {
if (id === undefined && type === 'func_ref_resources') {
id = asyncId;
events.push({ eventName: 'init', type, triggerAsyncId, resource });
}
},
before (asyncId) {
if (asyncId === id) {
events.push({ eventName: 'before' });
}
},
after (asyncId) {
if (asyncId === id) {
events.push({ eventName: 'after' });
}
},
destroy (asyncId) {
if (asyncId === id) {
events.push({ eventName: 'destroy' });
destroyed = true;
}
}
})
.enable();
});
}
function canConstructRefFromExistingRef (binding) {
const testFunc = () => 240;
assert(binding.ConstructWithMove(testFunc) === 240);
}
function canCallFunctionWithDifferentOverloads (binding) {
let outsideRef = {};
const testFunc = (a, b) => a * a - b * b;
const testFuncB = (a, b, c) => a + b - c * c;
const testFuncC = (a, b, c) => {
outsideRef.a = a;
outsideRef.b = b;
outsideRef.c = c;
};
const testFuncD = (a, b, c, d) => {
outsideRef.result = a + b * c - d;
return outsideRef.result;
};
assert(binding.CallWithVec(testFunc, 5, 4) === testFunc(5, 4));
assert(binding.CallWithInitList(testFuncB, 2, 4, 5) === testFuncB(2, 4, 5));
binding.CallWithRecvVector(testFuncC, outsideRef, 1, 2, 4);
assert(outsideRef.a === 1 && outsideRef.b === 2 && outsideRef.c === 4);
outsideRef = {};
binding.CallWithRecvInitList(testFuncC, outsideRef, 1, 2, 4);
assert(outsideRef.a === 1 && outsideRef.b === 2 && outsideRef.c === 4);
outsideRef = {};
binding.CallWithRecvArgc(testFuncD, outsideRef, 2, 4, 5, 6);
assert(outsideRef.result === testFuncD(2, 4, 5, 6));
}
async function canCallAsyncFunctionWithDifferentOverloads (binding) {
const testFunc = () => 2100;
const testFuncB = (a, b, c, d) => a + b + c + d;
let hook = installAsyncHook();
binding.AsyncCallWithInitList(testFunc);
let triggerAsyncId = asyncHook.executionAsyncId();
let res = await hook;
assert.deepStrictEqual(res, [
{
eventName: 'init',
type: 'func_ref_resources',
triggerAsyncId: triggerAsyncId,
resource: {}
},
{ eventName: 'before' },
{ eventName: 'after' },
{ eventName: 'destroy' }
]);
hook = installAsyncHook();
triggerAsyncId = asyncHook.executionAsyncId();
assert(
binding.AsyncCallWithVector(testFuncB, 2, 4, 5, 6) === testFuncB(2, 4, 5, 6)
);
res = await hook;
assert.deepStrictEqual(res, [
{
eventName: 'init',
type: 'func_ref_resources',
triggerAsyncId: triggerAsyncId,
resource: {}
},
{ eventName: 'before' },
{ eventName: 'after' },
{ eventName: 'destroy' }
]);
hook = installAsyncHook();
triggerAsyncId = asyncHook.executionAsyncId();
assert(
binding.AsyncCallWithArgv(testFuncB, 2, 4, 5, 6) === testFuncB(2, 4, 5, 6)
);
}
async function test (binding) {
const e = new Error('foobar');
const functionMayThrow = () => {
throw e;
};
const classMayThrow = class {
constructor () {
throw e;
}
};
const newRef = binding.CreateFuncRefWithNew(120);
assert(newRef.getValue() === 120);
const newRefWithVecArg = binding.CreateFuncRefWithNewVec(80);
assert(newRefWithVecArg.getValue() === 80);
assert.throws(() => {
binding.call(functionMayThrow);
}, /foobar/);
assert.throws(() => {
binding.construct(classMayThrow);
}, /foobar/);
canConstructRefFromExistingRef(binding);
canCallFunctionWithDifferentOverloads(binding);
await canCallAsyncFunctionWithDifferentOverloads(binding);
}
;