bootstrap: clean up inspector console methods during serialization

Some console methods are created by the V8 inspector after
an inspector client is created, reset them to undefined during
sereialization to avoid holding on to invalid references in
the snapshot. V8 will take care of the re-initialization when
another inspector client is created during deserialization.

PR-URL: https://github.com/nodejs/node/pull/44279
Fixes: https://github.com/nodejs/node-v8/issues/237
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
This commit is contained in:
Joyee Cheung 2022-09-07 19:20:40 +08:00 committed by GitHub
parent bae03c4e30
commit f0cf1005a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 98 additions and 0 deletions

View File

@ -28,6 +28,7 @@ const {
SafeArrayIterator,
SafeMap,
SafeWeakMap,
SafeSet,
StringPrototypeIncludes,
StringPrototypePadStart,
StringPrototypeRepeat,
@ -687,6 +688,32 @@ Console.prototype.groupCollapsed = Console.prototype.group;
function initializeGlobalConsole(globalConsole) {
globalConsole[kBindStreamsLazy](process);
globalConsole[kBindProperties](true, 'auto');
const {
addSerializeCallback,
isBuildingSnapshot,
} = require('v8').startupSnapshot;
if (!internalBinding('config').hasInspector || !isBuildingSnapshot()) {
return;
}
const { console: consoleFromVM } = internalBinding('inspector');
const nodeConsoleKeys = ObjectKeys(Console.prototype);
const vmConsoleKeys = ObjectKeys(consoleFromVM);
const originalKeys = new SafeSet(vmConsoleKeys.concat(nodeConsoleKeys));
const inspectorConsoleKeys = new SafeSet();
for (const key of ObjectKeys(globalConsole)) {
if (!originalKeys.has(key)) {
inspectorConsoleKeys.add(key);
}
}
// During deserialization these should be reinstalled to console by
// V8 when the inspector client is created.
addSerializeCallback(() => {
for (const key of inspectorConsoleKeys) {
globalConsole[key] = undefined;
}
});
}
module.exports = {

9
test/fixtures/snapshot/console.js vendored Normal file
View File

@ -0,0 +1,9 @@
const {
setDeserializeMainFunction,
} = require('v8').startupSnapshot;
console.log(JSON.stringify(Object.keys(console), null, 2));
setDeserializeMainFunction(() => {
console.log(JSON.stringify(Object.keys(console), null, 2));
});

View File

@ -0,0 +1,62 @@
'use strict';
// TODO(joyeecheung): remove the flag when it is turned on by default in V8.
// Flags: --experimental-async-stack-tagging-api
// This tests the console works in the deserialized snapshot.
const common = require('../common');
common.skipIfInspectorDisabled();
const assert = require('assert');
const { spawnSync } = require('child_process');
const tmpdir = require('../common/tmpdir');
const fixtures = require('../common/fixtures');
const path = require('path');
const fs = require('fs');
tmpdir.refresh();
const blobPath = path.join(tmpdir.path, 'snapshot.blob');
const entry = fixtures.path('snapshot', 'console.js');
{
const child = spawnSync(process.execPath, [
'--experimental-async-stack-tagging-api',
'--snapshot-blob',
blobPath,
'--build-snapshot',
entry,
], {
cwd: tmpdir.path
});
const stdout = child.stdout.toString();
if (child.status !== 0) {
console.log(stdout);
console.log(child.stderr.toString());
assert.strictEqual(child.status, 0);
}
assert.deepStrictEqual(Object.keys(console), JSON.parse(stdout));
const stats = fs.statSync(path.join(tmpdir.path, 'snapshot.blob'));
assert(stats.isFile());
}
{
const child = spawnSync(process.execPath, [
'--experimental-async-stack-tagging-api',
'--snapshot-blob',
blobPath,
], {
cwd: tmpdir.path,
env: {
...process.env,
}
});
const stdout = child.stdout.toString();
if (child.status !== 0) {
console.log(stdout);
console.log(child.stderr.toString());
assert.strictEqual(child.status, 0);
}
assert.deepStrictEqual(Object.keys(console), JSON.parse(stdout));
assert.strictEqual(child.status, 0);
}