From 62024b651e628c07c1910bfee1070bc10cadba1b Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Wed, 27 Jun 2018 22:31:01 +0800 Subject: [PATCH] build: create V8 code cache after script is run This patch makes it possible to generate the code cache for the builtins directly from the original script object (instead of compiling a new one) and after the script has been run (via `NativeModule.require`). Before this patch only the top level functions (the wrapped ones) are included in the cache, after this patch the inner functions in those modules will be included as well. Also blacklists modules from dependencies like V8 and node-inspect since we cannot guarantee that they are suitable to be executed directly. PR-URL: https://github.com/nodejs/node/pull/21567 Refs: https://github.com/nodejs/node/issues/21563 Reviewed-By: James M Snell Reviewed-By: Tiancheng "Timothy" Gu Reviewed-By: Gus Caplan Reviewed-By: John-David Dalton Reviewed-By: Colin Ihrig --- lib/internal/bootstrap/cache.js | 49 +++++++++++++++++++++++------- lib/internal/bootstrap/loaders.js | 10 ++++-- test/code-cache/test-code-cache.js | 6 ++-- tools/generate_code_cache.js | 25 +++++---------- 4 files changed, 55 insertions(+), 35 deletions(-) diff --git a/lib/internal/bootstrap/cache.js b/lib/internal/bootstrap/cache.js index 17c14e816ff668..e7e46bdf5174d5 100644 --- a/lib/internal/bootstrap/cache.js +++ b/lib/internal/bootstrap/cache.js @@ -9,23 +9,50 @@ const { NativeModule, internalBinding } = require('internal/bootstrap/loaders'); +function getCodeCache(id) { + const cached = NativeModule.getCached(id); + if (cached && (cached.loaded || cached.loading)) { + return cached.script.createCachedData(); + } + + // The script has not been compiled and run + NativeModule.require(id); + return getCodeCache(id); +} + +const depsModule = Object.keys(NativeModule._source).filter( + (key) => NativeModule.isDepsModule(key) || key.startsWith('internal/deps') +); + +// Modules with source code compiled in js2c that +// cannot be compiled with the code cache +const cannotUseCache = [ + 'config', + 'sys', // deprecated + 'internal/v8_prof_polyfill', + 'internal/v8_prof_processor', + + 'internal/per_context', + + 'internal/test/binding', + // TODO(joyeecheung): update the C++ side so that + // the code cache is also used when compiling these + // two files. + 'internal/bootstrap/loaders', + 'internal/bootstrap/node' +].concat(depsModule); + module.exports = { + cachableBuiltins: Object.keys(NativeModule._source).filter( + (key) => !cannotUseCache.includes(key) + ), builtinSource: Object.assign({}, NativeModule._source), + getCodeCache, codeCache: internalBinding('code_cache'), compiledWithoutCache: NativeModule.compiledWithoutCache, compiledWithCache: NativeModule.compiledWithCache, nativeModuleWrap(script) { return NativeModule.wrap(script); }, - // Modules with source code compiled in js2c that - // cannot be compiled with the code cache - cannotUseCache: [ - 'config', - // TODO(joyeecheung): update the C++ side so that - // the code cache is also used when compiling these - // two files. - 'internal/bootstrap/loaders', - 'internal/bootstrap/node', - 'internal/per_context', - ] + cannotUseCache }; diff --git a/lib/internal/bootstrap/loaders.js b/lib/internal/bootstrap/loaders.js index c141c9adcff9b2..e85d5de9b79a49 100644 --- a/lib/internal/bootstrap/loaders.js +++ b/lib/internal/bootstrap/loaders.js @@ -118,6 +118,7 @@ this.exportKeys = undefined; this.loaded = false; this.loading = false; + this.script = null; // The ContextifyScript of the module } NativeModule._source = getBinding('natives'); @@ -165,11 +166,14 @@ return nativeModule.exports; }; + NativeModule.isDepsModule = function(id) { + return id.startsWith('node-inspect/') || id.startsWith('v8/'); + }; + NativeModule.requireForDeps = function(id) { if (!NativeModule.exists(id) || // TODO(TimothyGu): remove when DEP0084 reaches end of life. - id.startsWith('node-inspect/') || - id.startsWith('v8/')) { + NativeModule.isDepsModule(id)) { id = `internal/deps/${id}`; } return NativeModule.require(id); @@ -241,6 +245,8 @@ codeCache[this.id], false, undefined ); + this.script = script; + // One of these conditions may be false when any of the inputs // of the `node_js2c` target in node.gyp is modified. // FIXME(joyeecheung): diff --git a/test/code-cache/test-code-cache.js b/test/code-cache/test-code-cache.js index a4378343010ee6..b05e764e8ad290 100644 --- a/test/code-cache/test-code-cache.js +++ b/test/code-cache/test-code-cache.js @@ -12,9 +12,8 @@ const { } } = require('util'); const { - builtinSource, + cachableBuiltins, codeCache, - cannotUseCache, compiledWithCache, compiledWithoutCache } = require('internal/bootstrap/cache'); @@ -35,8 +34,7 @@ for (const key of loadedModules) { `"${key}" should've been compiled with code cache`); } -for (const key of Object.keys(builtinSource)) { - if (cannotUseCache.includes(key)) continue; +for (const key of cachableBuiltins) { assert(isUint8Array(codeCache[key]) && codeCache[key].length > 0, `Code cache for "${key}" should've been generated`); } diff --git a/tools/generate_code_cache.js b/tools/generate_code_cache.js index 8aab6bc286d288..740cbd718aa11e 100644 --- a/tools/generate_code_cache.js +++ b/tools/generate_code_cache.js @@ -8,12 +8,10 @@ // of `configure`. const { - nativeModuleWrap, - builtinSource, - cannotUseCache + getCodeCache, + cachableBuiltins } = require('internal/bootstrap/cache'); -const vm = require('vm'); const fs = require('fs'); const resultPath = process.argv[2]; @@ -72,25 +70,16 @@ 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) { +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 = script.cachedData.length; + const length = cachedData.length; totalCacheSize += length; - const { definition, initializer } = getInitalizer(key, script.cachedData); + const { definition, initializer } = getInitalizer(key, cachedData); cacheDefinitions.push(definition); cacheInitializers.push(initializer); console.log(`Generated cache for '${key}', size = ${formatSize(length)}` +