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: https://github.com/nodejs/node/pull/63879
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Whether or not an exception should be handled when it
was thrown in the REPL's context but the REPL is already
closed is something that will depend on the details of
the specific REPL instance's use case.
Adding this option lets the REPL's creator control the
details of this behavior.
This resolves a TODO recently added in bd3cba5633.
PR-URL: https://github.com/nodejs/node/pull/62188
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Chemi Atlow <chemi@atlow.co.il>
a9da9ffc04 recently restructured async context handling
in the REPL source so that it no longer uses the domain module,
and instead performs its own async context tracking.
That change was not intended to be breaking, but it affected
behavior for uncaught exceptions thrown after the REPL was closed.
Those would now be treated as uncaught exceptions on the process
level, which is probably not intentional in most situations.
While it's understandably not great that we handle execptions
after closing the REPL instance, I am confident that it's
best to restore the previous behavior for now and add more
granular handling options separately and intentionally
in a (potentially semver-major) follow-up change.
Refs: https://github.com/nodejs/node/pull/61227
PR-URL: https://github.com/nodejs/node/pull/62165
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Replace the domain-based error handling with AsyncLocalStorage and
setUncaughtExceptionCaptureCallback. This removes the REPL's dependency
on the deprecated domain module while preserving all existing behavior:
- Synchronous errors during eval are caught and displayed
- Async errors (setTimeout, promises, etc.) are caught via the
uncaught exception capture callback
- Top-level await errors are caught and displayed
- The REPL continues operating after errors
- Multiple REPL instances can coexist with errors routed correctly
Changes:
- Use AsyncLocalStorage to track which REPL instance owns an async
context, replacing domain's automatic async tracking
- Add setupExceptionCapture() to install
setUncaughtExceptionCaptureCallback for catching async errors and
routing them to the correct REPL
- Extract error handling logic into REPLServer.prototype._handleError()
- Wrap eval execution in replContext.run() for async context tracking
- Update newListener protection to check AsyncLocalStorage context
- Throw ERR_INVALID_ARG_VALUE if options.domain is passed
PR-URL: https://github.com/nodejs/node/pull/61227
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
When investigating a memory leak in one of our applications,
we discovered that this listener holds on to a `REPLServer`
instance and all heap objects transitively kept alive by it
by capturing as part of its closure.
It's cleaner to declare the listener outside of the `REPLServer`
class and to actually clean it up properly when it is no longer
required or meaningful, which is easily achieved through
keeping a reference count.
PR-URL: https://github.com/nodejs/node/pull/61895
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
The test was flaky because the FileHandle for the history file could
be garbage collected before being explicitly closed, causing
ERR_INVALID_STATE errors when the handle was already closed by the
GC finalizer.
This fix adds an explicit closeHandle() method to ReplHistory and
ensures that REPLServer.close() waits for the history file handle
to be properly closed before completing the close operation.
Refs: https://github.com/nodejs/reliability/issues/1450
PR-URL: https://github.com/nodejs/node/pull/61614
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
https://github.com/nodejs/node/pull/57909 introduced the disabling
of REPL tab completion on object containing proxies and getters
(since such completion triggers code evaluation which can be
unexpected/disruptive for the user)
the solution in 57909 did not address all possible such cases,
the changes here improve on such solution by using acorn and
AST analysis to cover most if not all possible cases
PR-URL: https://github.com/nodejs/node/pull/58891
Reviewed-By: James M Snell <jasnell@gmail.com>
improve the tab completion capabilities around computed properties
by replacing the use of brittle and error prone Regex checks with
more robust AST based analysis
PR-URL: https://github.com/nodejs/node/pull/58775
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
this commit reintroduces the REPL custom eval tests that have
been introduced in https://github.com/nodejs/node/pull/57691
but reverted in https://github.com/nodejs/node/pull/57793
the tests turned out problematic before because `getReplOutput`,
the function used to return the repl output wasn't taking into
account that input processing and output emitting are asynchronous
operation can resolve with a small delay
the new implementation here replaces `getReplOutput` with
`getReplRunOutput` that resolves repl inputs by running them
and using the repl prompt as an indicator to when the input
processing has completed
PR-URL: https://github.com/nodejs/node/pull/57850
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
the REPL completion logic evaluates code, this is generally ok
but it can be problematic when there are objects with nested
properties since the code evaluation would trigger their potential
getters (e.g. the `obj.foo.b` line would trigger the getter of
`foo`), the changes here disable the completion logic when proxies
and getters are involved thus making sure that code evaluation does
not take place when it can potentially (and surprisingly to the user)
trigger side effectful behaviors
PR-URL: https://github.com/nodejs/node/pull/57909
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Split the `internal/process/esm_loader` file which contains the
singleton cascaded loader:
- The the singleton cascaded loader now directly resides in
`internal/modules/esm/loader`, where the constructor also lives.
This file is the root of most circular dependency of ESM code,
(because components of the loader need the singleton itself),
so this makes the dependency more obvious. Added comments about
loading it lazily to avoid circular dependency.
- The getter to the cascaded loader is also turned into a method
to make the side effect explicit.
- The sequence of `loadESM()` and `handleMainPromise` is now merged
together into `runEntryPointWithESMLoader()` in
`internal/modules/run_main` because this is intended to run entry
points with the ESM loader and not just any module.
- Documents how top-level await is handled.
PR-URL: https://github.com/nodejs/node/pull/51999
Fixes: https://github.com/nodejs/node/issues/42868
Reviewed-By: Moshe Atlow <moshe@atlow.co.il>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com>