The code is part of a larger project that generates dynamic libraries (dylibs) from code, using Node.js modules and the ffi-rs
library for interoping with Rust code. It defines several functions, including generateCallback
and makeDylib
, which are used to interact with and create dylibs.
npm run import -- "compile a dylib from source cell"
const path = require('path')
const { Module } = require('module')
const BUILD_DIRECTORY = path.join(__dirname, '../.build')
let typeToDataType
function generateCallback(libName, i, types, params) {
const { importNotebook } = require('../Core')
const { load } = require('ffi-rs')
if(!typeToDataType)
typeToDataType = importNotebook("c type to data type")
return function internalCallback(returnLength, ...paramValues) {
//console.log(paramValues, types)
let returnType = typeToDataType(returnLength, types[0])
return load({
library: libName, // path to the dynamic library file
funcName: params[i][0], // the name of the function to call
retType: returnType, // the return value type
paramsType: types.slice(1).map((t, i) => typeToDataType(typeof paramValues[i] == 'undefined' ? 0 : typeof paramValues == 'object' && paramValues[i].constructor.name == 'Array' ? paramValues[i].length : 1, t)), // the parameter types
paramsValue: paramValues.length < 2 && types.length && types.length <= 2 && types[1] == 'void' && typeof paramValues[0] == 'undefined' ? [void 0] : paramValues // the actual parameter values
// freeResultMemory: true, // whether or not need to free the result of return value memory automatically, default is false
})
}
}
async function makeDylib(code, pathToCode, ctx) {
const {
importNotebook, interpret
} = require('../Core')
const { open } = require('ffi-rs')
if (Module._cache[pathToCode]) {
return Module._cache[pathToCode].exports
}
const buildDylib = importNotebook("build dylib")
// TODO: await module loaded
buildDylib(code, pathToCode, ctx)
const codeCell = interpret(pathToCode)
// TODO: compare file times for dylib module recompile
const { safeurl } = importNotebook("domain cache tools")
let libName = safeurl(codeCell.questions[0])
open({
library: libName, // key
path: path.join(BUILD_DIRECTORY, libName + '.dylib') // path
})
// TODO: turn this into a sort of prepFunction(...types) that takes a types argument instead
//const selectCode = await importNotebook("select antlr tree")
//console.log(selectCode)
const selectCode = await importNotebook("select antlr tree")
const rootNode = (await selectCode(['//*'], code.toString(), 'cpp'))[0]
//console.log('###########################', rootNode.tagName)
const getParameters = await importNotebook("get c parameters")
const params = await getParameters(rootNode)
console.log(params)
const getTypes = await importNotebook("get c types")
// TODO: make functions out of all exports
let resources = {}
for (let i = 0; i < params.length; i++) {
if(typeof params[i] == 'string') {
params[i] = [params[i]]
}
const types = await getTypes(rootNode, params[i][0])
//console.log(types)
// TODO: make a version of this function that can load all data types from parameters
let newCallback = generateCallback(libName, i, types, params)
resources[params[i][0]] = ((i, types) => function (...paramValues) {
return resources[params[i][0] + 'Ret'](1, ...paramValues)
})(i, types)
resources[params[i][0] + 'Ret'] = newCallback
resources[params[i][0]].name = params[0]
resources[params[i][0] + 'Ret'].name = params[0] + 'Ret'
resources[params[i][0]].params = params[i].slice(1)
resources[params[i][0] + 'Ret'].params = ['ret'].concat(params[i].slice(1))
resources[params[i][0]].types = types
resources[params[i][0] + 'Ret'].types = types
}
Module._cache[pathToCode] = {}
Module._cache[pathToCode].exports = resources
Module._cache[pathToCode].loaded = true
//close(libName)
return resources
}
module.exports.makeDylib = makeDylib
/**
* Generates a dynamic library from the given code.
*
* @param {string} code - The code to be compiled.
* @param {string} pathToCode - The path to the code file.
* @param {*} ctx - The context for the code execution.
*
* @returns {object} The dynamic library exports.
*/
async function makeDylib(code, pathToCode, ctx) {
const { importNotebook, interpret } = require('../Core');
const { open } = require('ffi-rs');
// Check if the module is already cached.
if (Module._cache[pathToCode]) {
return Module._cache[pathToCode].exports;
}
// Import required notebooks.
const { buildDylib, selectCode, getParameters, getTypes, safeurl } = await importNotebooks(pathToCode);
// Build the dynamic library.
await buildDylib(code, pathToCode, ctx);
// Get the code cell and interpret it.
const codeCell = interpret(pathToCode);
const libName = safeurl(codeCell.questions[0]);
// Open the dynamic library.
open({
library: libName,
path: path.join(BUILD_DIRECTORY, libName + '.dylib'),
});
// Select the ANTLR tree.
const rootNode = (await selectCode(['//*'], code.toString(), 'cpp'))[0];
// Get the parameters and types.
const params = await getParameters(rootNode);
const types = await getTypes(rootNode);
// Generate the callback functions.
const resources = {};
for (let i = 0; i < params.length; i++) {
const param = params[i];
if (typeof param ==='string') {
param = [param];
}
const paramTypes = types[param[0]];
const returnType = paramTypes[0];
const paramTypes = paramTypes.slice(1);
// Generate the callback function.
const callback = generateCallback(libName, i, paramTypes, param);
resources[param[0]] = ((i, paramTypes, param) => function (...paramValues) {
return resources[param[0] + 'Ret'](1,...paramValues);
})(i, paramTypes, param);
// Set the properties of the callback functions.
resources[param[0]].name = param[0];
resources[param[0] + 'Ret'].name = param[0] + 'Ret';
resources[param[0]].params = param.slice(1);
resources[param[0] + 'Ret'].params = ['ret'].concat(param.slice(1));
resources[param[0]].types = paramTypes;
resources[param[0] + 'Ret'].types = paramTypes;
}
// Cache the exports.
Module._cache[pathToCode] = {
exports: resources,
loaded: true,
};
return resources;
}
// Helper function to import multiple notebooks.
async function importNotebooks(pathToCode) {
const notebooks = {};
const notebookNames = ['build dylib','select antlr tree', 'get c parameters', 'get c types', 'c type to data type'];
for (const notebookName of notebookNames) {
notebooks[notebookName] = await importNotebook(notebookName);
}
return notebooks;
}
// Export the makeDylib function.
module.exports.makeDylib = makeDylib;
Breakdown of the Code
The code appears to be a part of a larger project that generates dynamic libraries (dylibs) from code. It involves importing external libraries, loading modules, and creating callback functions to interact with the dylibs.
path
: a Node.js module for working with file paths.Module
: a Node.js module for working with dynamic modules.ffi-rs
: a library for interoping with Rust code.../Core
: a local module containing notebook functions for importing and loading modules.BUILD_DIRECTORY
: the directory where build files are stored.typeToDataType
: a function that converts C types to data types.generateCallback(libName, i, types, params)
: a function that generates a callback function to interact with a dylib. It takes the library name, function index, types, and parameters as arguments.makeDylib(code, pathToCode, ctx)
: an asynchronous function that creates a dylib from code. It takes the code, path to the code, and context as arguments.generateCallback
: uses the typeToDataType
function to convert C types to data types, then returns a callback function that loads a dylib using ffi-rs
.makeDylib
: imports notebooks from ../Core
, creates a dylib using buildDylib
, and loads the dylib using open
from ffi-rs
.makeDylib
function does not await the result of buildDylib
.generateCallback
function uses a closure to capture variables from its scope.../Core
module exports functions importNotebook
and interpret
.path.join
to construct file paths, but does not handle potential errors.require
to import modules, which is not recommended for performance and security reasons.