import | compile a csharp file into a DLL | display interpreted results in markdown | Search

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.

Run example

npm run import -- "compile a dylib from source cell"

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

What the code could have been:

/**
 * 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

Overview

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.

Imported Modules

Variables and Functions

Key Functions

Notes