| '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; |
| }); |