'use strict'; // Flags: --expose-internals // This file generates the code cache for builtin modules and // writes them into static char arrays of a C++ file that can be // compiled into the binary using the `--code-cache-path` option // of `configure`. const { nativeModuleWrap, builtinSource, cannotUseCache } = require('internal/bootstrap/cache'); const vm = require('vm'); const fs = require('fs'); const resultPath = process.argv[2]; if (!resultPath) { console.error(`Usage: ${process.argv[0]} ${process.argv[1]}` + 'path/to/node_code_cache.cc'); process.exit(1); } /** * Format a number of a size in bytes into human-readable strings * @param {number} num * @return {string} */ function formatSize(num) { if (num < 1024) { return `${(num).toFixed(2)}B`; } else if (num < 1024 ** 2) { return `${(num / 1024).toFixed(2)}KB`; } else if (num < 1024 ** 3) { return `${(num / (1024 ** 2)).toFixed(2)}MB`; } else { return `${(num / (1024 ** 3)).toFixed(2)}GB`; } } /** * Generates the source code of definitions of the char arrays * that contains the code cache and the source code of the * initializers of the code cache. * * @param {string} key ID of the builtin module * @param {Buffer} cache Code cache of the builtin module * @return { definition: string, initializer: string } */ function getInitalizer(key, cache) { const defName = key.replace(/\//g, '_').replace(/-/g, '_'); const definition = `static uint8_t ${defName}_raw[] = {\n` + `${cache.join(',')}\n};`; const initializer = ` v8::Local ${defName}_ab = v8::ArrayBuffer::New(isolate, ${defName}_raw, ${cache.length}); v8::Local ${defName}_array = v8::Uint8Array::New(${defName}_ab, 0, ${cache.length}); target->Set(context, FIXED_ONE_BYTE_STRING(isolate, "${key}"), ${defName}_array).FromJust(); `; return { definition, initializer }; } const cacheDefinitions = []; const cacheInitializers = []; let totalCacheSize = 0; for (const key of Object.keys(builtinSource)) { if (cannotUseCache.includes(key)) continue; const code = nativeModuleWrap(builtinSource[key]); // Note that this must corresponds to the code in // NativeModule.prototype.compile const script = new vm.Script(code, { filename: `${key}.js`, produceCachedData: true }); if (!script.cachedData) { console.error(`Failed to generate code cache for '${key}'`); process.exit(1); } const length = script.cachedData.length; totalCacheSize += length; const { definition, initializer } = getInitalizer(key, script.cachedData); cacheDefinitions.push(definition); cacheInitializers.push(initializer); console.log(`Generated cache for '${key}', size = ${formatSize(length)}` + `, total = ${formatSize(totalCacheSize)}`); } const result = `#include "node.h" #include "node_code_cache.h" #include "v8.h" #include "env.h" #include "env-inl.h" // This file is generated by tools/generate_code_cache.js // and is used when configure is run with \`--code-cache-path\` namespace node { ${cacheDefinitions.join('\n\n')} // The target here will be returned as \`internalBinding('code_cache')\` void DefineCodeCache(Environment* env, v8::Local target) { v8::Isolate* isolate = env->isolate(); v8::Local context = env->context(); ${cacheInitializers.join('\n')} } } // namespace node `; fs.writeFileSync(resultPath, result); console.log(`Generated code cache C++ file to ${resultPath}`);