repl: lazy-load acorn and defer vm context creation · nodejs/node@00dea28 · GitHub
Skip to content

Commit 00dea28

Browse files
watilderichardlau
authored andcommitted
repl: lazy-load acorn and defer vm context creation
Merely loading the repl builtin used to eagerly require acorn and acorn-walk (~250KB of JS) and create an entire V8 context via vm.runInNewContext() just to enumerate global property names, even though both are only needed once REPL input is actually parsed or tab-completion is used. Require acorn and acorn-walk at their function-level use sites instead, and wrap the global builtins set in getLazy(). The cost moves to the first preview/completion/recoverable-error check, where the one-time ~1ms is imperceptible. Benchmark results (Linux x64, misc/startup-core.js, 20 runs): require-builtins.js: +3.12% ops/s (t=3.57, p<0.01) import-builtins.mjs: +2.20% ops/s (t=3.00, p<0.01) In isolation, require('repl') drops from 4.64ms to 2.84ms (-39%) and interactive `node -i` startup improves by ~13%. Signed-off-by: Daijiro Wachi <daijiro.wachi@gmail.com> PR-URL: #63879 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
1 parent 8ef643d commit 00dea28

3 files changed

Lines changed: 22 additions & 22 deletions

File tree

lib/internal/repl/completion.js

Lines changed: 8 additions & 10 deletions

lib/internal/repl/utils.js

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,8 @@ const {
2020
Symbol,
2121
} = primordials;
2222

23-
const { tokTypes: tt, Parser: AcornParser } =
24-
require('internal/deps/acorn/acorn/dist/acorn');
25-
2623
const { sendInspectorCommand } = require('internal/util/inspector');
24+
const { getLazy } = require('internal/util');
2725

2826
const {
2927
ERR_INSPECTOR_NOT_AVAILABLE,
@@ -80,6 +78,9 @@ function isRecoverableError(e, code) {
8078
isRecoverableError(e, `(${code}`))
8179
return true;
8280

81+
const { tokTypes: tt, Parser: AcornParser } =
82+
require('internal/deps/acorn/acorn/dist/acorn');
83+
8384
let recoverable = false;
8485

8586
// Determine if the point of any error raised is at the end of the input.
@@ -756,6 +757,8 @@ function setupReverseSearch(repl) {
756757
const startsWithBraceRegExp = /^\s*{/;
757758
const endsWithSemicolonRegExp = /;\s*$/;
758759
function isValidSyntax(input) {
760+
const { Parser: AcornParser } =
761+
require('internal/deps/acorn/acorn/dist/acorn');
759762
try {
760763
AcornParser.parse(input, {
761764
ecmaVersion: 'latest',
@@ -815,8 +818,9 @@ function getREPLResourceName() {
815818
return `REPL${nextREPLResourceNumber++}`;
816819
}
817820

818-
const globalBuiltins =
819-
new SafeSet(vm.runInNewContext('Object.getOwnPropertyNames(globalThis)'));
821+
// Creating a new context is expensive, so only do it on first use.
822+
const getGlobalBuiltins = getLazy(() =>
823+
new SafeSet(vm.runInNewContext('Object.getOwnPropertyNames(globalThis)')));
820824

821825
let _builtinLibs = ArrayPrototypeFilter(
822826
CJSModule.builtinModules,
@@ -848,7 +852,7 @@ module.exports = {
848852
isValidSyntax,
849853
kContextId,
850854
getREPLResourceName,
851-
globalBuiltins,
855+
getGlobalBuiltins,
852856
getReplBuiltinLibs,
853857
setReplBuiltinLibs,
854858
fixReplRequire,

lib/repl.js

Lines changed: 4 additions & 6 deletions

0 commit comments

Comments
 (0)