Skip to content

Commit

Permalink
build: create V8 code cache after script is run
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
joyeecheung committed Jul 23, 2018
1 parent 9466865 commit 7365d3f
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 35 deletions.
49 changes: 38 additions & 11 deletions lib/internal/bootstrap/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
10 changes: 8 additions & 2 deletions lib/internal/bootstrap/loaders.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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):
Expand Down
6 changes: 2 additions & 4 deletions test/code-cache/test-code-cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ const {
}
} = require('util');
const {
builtinSource,
cachableBuiltins,
codeCache,
cannotUseCache,
compiledWithCache,
compiledWithoutCache
} = require('internal/bootstrap/cache');
Expand All @@ -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`);
}
25 changes: 7 additions & 18 deletions tools/generate_code_cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -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)}` +
Expand Down

0 comments on commit 7365d3f

Please sign in to comment.