'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 { getCodeCache, getSource, cachableBuiltins } = require('internal/bootstrap/cache'); function hash(str) { if (process.versions.openssl) { return require('crypto').createHash('sha256').update(str).digest('hex'); } return ''; } 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 source = getSource(key); const sourceHash = hash(source); 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(); `; const hashIntializer = ` target->Set(context, FIXED_ONE_BYTE_STRING(isolate, "${key}"), OneByteString(isolate, "${sourceHash}")).FromJust(); `; return { definition, initializer, hashIntializer, sourceHash }; } const cacheDefinitions = []; const cacheInitializers = []; const cacheHashInitializers = []; let totalCacheSize = 0; for (const key of cachableBuiltins) { const cachedData = getCodeCache(key); if (!cachedData.length) { console.error(`Failed to generate code cache for '${key}'`); process.exit(1); } const length = cachedData.length; totalCacheSize += length; const { definition, initializer, hashIntializer, sourceHash } = getInitalizer(key, cachedData); cacheDefinitions.push(definition); cacheInitializers.push(initializer); cacheHashInitializers.push(hashIntializer); console.log(`Generated cache for '${key}', size = ${formatSize(length)}` + `, hash = ${sourceHash}, 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')} } // The target here will be returned as \`internalBinding('code_cache_hash')\` void DefineCodeCacheHash(Environment* env, v8::Local target) { v8::Isolate* isolate = env->isolate(); v8::Local context = env->context(); ${cacheHashInitializers.join('\n')} } } // namespace node `; fs.writeFileSync(resultPath, result); console.log(`Generated code cache C++ file to ${resultPath}`);