blob: e45d7df29377a0ac3fdb9cbed734c26d6f697ba5 [file] [log] [blame] [edit]
'use strict';
const { resolve: resolvePath } = require('path');
const { writeFile } = require('fs/promises');
const { runClang } = require('../lib/clang-utils');
/** @typedef {{ js_native_api_symbols: string[]; node_api_symbols: string[]; }} SymbolInfo */
/**
* @param {number} [version]
* @returns {Promise<SymbolInfo>}
*/
async function getSymbolsForVersion(version) {
try {
const { stdout } = await runClang([ '-ast-dump=json', '-fsyntax-only', '-fno-diagnostics-color', `-DNAPI_VERSION=${version}`, resolvePath(__dirname, '..', 'include', 'node_api.h')])
const ast = JSON.parse(stdout);
/** @type {SymbolInfo} */
const symbols = { js_native_api_symbols: [], node_api_symbols: [] };
for (const statement of ast.inner) {
if (statement.kind !== 'FunctionDecl') {
continue;
}
const name = statement.name;
const file = statement.loc.includedFrom?.file;
if (file) {
symbols.js_native_api_symbols.push(name);
} else {
symbols.node_api_symbols.push(name);
}
}
symbols.js_native_api_symbols.sort();
symbols.node_api_symbols.sort();
return symbols;
} catch (err) {
if (err.code === 'ENOENT') {
throw new Error('This tool requires clang to be installed.');
}
throw err;
}
}
/** @returns {Promise<{maxVersion: number, symbols: {[x: string]: SymbolInfo}}>} */
async function getAllSymbols() {
/** @type {{[x: string]: SymbolInfo}} */
const allSymbols = {};
let version = 1;
console.log('Processing symbols from clang:')
while (true) {
const symbols = await getSymbolsForVersion(version);
if (version > 1) {
const previousSymbols = allSymbols[`v${version - 1}`];
if (previousSymbols.js_native_api_symbols.length == symbols.js_native_api_symbols.length && previousSymbols.node_api_symbols.length === symbols.node_api_symbols.length) {
--version;
break;
}
}
allSymbols[`v${version}`] = symbols;
console.log(` v${version}: ${symbols.js_native_api_symbols.length} js_native_api_symbols, ${symbols.node_api_symbols.length} node_api_symbols`);
++version;
}
return {
maxVersion: version,
symbols: allSymbols
};
}
/**
* @param {SymbolInfo} previousSymbols
* @param {SymbolInfo} currentSymbols
* @returns {SymbolInfo}
*/
function getUniqueSymbols(previousSymbols, currentSymbols) {
/** @type {SymbolInfo} */
const symbols = { js_native_api_symbols: [], node_api_symbols: [] };
for (const symbol of currentSymbols.js_native_api_symbols) {
if (!previousSymbols.js_native_api_symbols.includes(symbol)) {
symbols.js_native_api_symbols.push(symbol);
}
}
for (const symbol of currentSymbols.node_api_symbols) {
if (!previousSymbols.node_api_symbols.includes(symbol)) {
symbols.node_api_symbols.push(symbol);
}
}
return symbols;
}
/**
* @param {string[]} strings
*/
function joinStrings(strings, prependNewLine = false) {
if (strings.length === 0) return '';
return `${prependNewLine ? ',\n ' : ''}'${strings.join("',\n '")}'`;
}
async function getSymbolData() {
const { maxVersion, symbols } = await getAllSymbols();
let data = `'use strict'
const v1 = {
js_native_api_symbols: [
${joinStrings(symbols.v1.js_native_api_symbols)}
],
node_api_symbols: [
${joinStrings(symbols.v1.node_api_symbols)}
]
}
`;
for (let version = 2; version <= maxVersion; ++version) {
const newSymbols = getUniqueSymbols(symbols[`v${version - 1}`], symbols[`v${version}`]);
data += `
const v${version} = {
js_native_api_symbols: [
...v${version - 1}.js_native_api_symbols${joinStrings(newSymbols.js_native_api_symbols, true)}
],
node_api_symbols: [
...v${version - 1}.node_api_symbols${joinStrings(newSymbols.node_api_symbols, true)}
]
}
`;
}
data += `
module.exports = {
${new Array(maxVersion).fill(undefined).map((_, i) => `v${i + 1}`).join(',\n ')}
}
`
return data;
}
async function main() {
const path = resolvePath(__dirname, '../symbols.js');
const data = await getSymbolData();
console.log(`Writing symbols to ${path}`)
return writeFile(path, data);
}
main().catch(e => {
console.error(e);
process.exitCode = 1;
});