85 lines
2.3 KiB
JavaScript
Raw Permalink Normal View History

2025-04-28 12:25:20 +08:00
'use strict';
/**
* Module dependencies.
*/
const ref = require('ref-napi');
const CIF = require('./cif');
const assert = require('assert');
const debug = require('debug')('ffi:Callback');
const _Callback = require('./bindings').Callback;
// Function used to report errors to the current process event loop,
// When user callback function gets gced.
function errorReportCallback (err) {
if (err) {
process.nextTick(function () {
if (typeof err === 'string') {
throw new Error(err);
} else {
throw err;
}
})
}
}
/**
* Turns a JavaScript function into a C function pointer.
* The function pointer may be used in other C functions that
* accept C callback functions.
*/
function Callback (retType, argTypes, abi, func) {
debug('creating new Callback');
if (typeof abi === 'function') {
func = abi;
abi = undefined;
}
// check args
assert(!!retType, 'expected a return "type" object as the first argument');
assert(Array.isArray(argTypes), 'expected Array of arg "type" objects as the second argument');
assert.equal(typeof func, 'function', 'expected a function as the third argument');
// normalize the "types" (they could be strings, so turn into real type
// instances)
retType = ref.coerceType(retType);
argTypes = argTypes.map(ref.coerceType);
// create the `ffi_cif *` instance
const cif = CIF(retType, argTypes, abi);
const argc = argTypes.length;
const callback = _Callback(cif, retType.size, argc, errorReportCallback, (retval, params) => {
debug('Callback function being invoked')
try {
const args = [];
for (var i = 0; i < argc; i++) {
const type = argTypes[i];
const argPtr = params.readPointer(i * ref.sizeof.pointer, type.size);
argPtr.type = type;
args.push(argPtr.deref());
}
// Invoke the user-given function
const result = func.apply(null, args);
try {
ref.set(retval, 0, result, retType);
} catch (e) {
e.message = 'error setting return value - ' + e.message;
throw e;
}
} catch (e) {
return e;
}
});
// store reference to the CIF Buffer so that it doesn't get
// garbage collected before the callback Buffer does
callback._cif = cif;
return callback;
}
module.exports = Callback;