-
Notifications
You must be signed in to change notification settings - Fork 29.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
repl: support mult-line string-keyed objects #21805
Conversation
lib/repl.js
Outdated
@@ -47,10 +47,11 @@ const { | |||
makeRequireFunction, | |||
addBuiltinLibsToObject | |||
} = require('internal/modules/cjs/helpers'); | |||
const acorn = require('internal/deps/acorn/dist/acorn'); | |||
const { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should these additions be placed near the other acorn work in lib/internal/repl/
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what you mean by these additions here. The require isn't new, just the capturing of the name as acorn
is new. More generally, isRecoverableError
isn't new, just completely rewritten.
Perhaps you are suggesting that isRecoverableError
be split out into a separate file and placed into lib/internal/repl/
? If so, I can do that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm suggesting keeping the acorn extensions and other acorn bits closer together.
lib/repl.js
Outdated
nextToken.call(this, pos, message); | ||
|
||
if (parser.type === acorn.tokTypes.eof) recoverable = true; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
acorn.tokTypes
is commonly stored in a var called tt
in acorn plugins.
So then it would be tt.eof
.
lib/repl.js
Outdated
nextToken.call(this, pos, message); | ||
|
||
if (parser.type === acorn.tokTypes.eof) recoverable = true; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe parse.type
can be this.type
lib/repl.js
Outdated
return function(pos, message) { | ||
switch (message) { | ||
case 'Unterminated template': | ||
case 'Unterminated comment': |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👆 Might put a note that these message strings are subject to change and to periodically check in on them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll ponder how to word that. Meanwhile, I'll note that should these message strings change, a test will fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have to manually update acorn to run into this issue. Having a failed test in such a circumstance would be fine and we'd have to invest that one way or the other. So I guess it is fine to keep it is as.
lib/repl.js
Outdated
// here as the point is to test for potentially valid but incomplete | ||
// expressions. | ||
if (/^\s*\{/.test(code) && isRecoverableError(e, `(${code}`)) return true; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👆 Acorn has a thorough regexp for whitespace. Might use that for the leading optional whitespace check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here I think it is very important that the regular expression exactly match the one in defaultEval
as it would be problematic for this code to treat a given input as recoverable only to later find out that defaultEval
would consider it to be a syntax error when done.
lib/repl.js
Outdated
|
||
case 'Unterminated string constant': | ||
const token = parser.input.slice(parser.lastTokStart, parser.pos); | ||
recoverable = token.endsWith('\\\n'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same nit as above, inconsistent use of parser
vs. this
.
lib/repl.js
Outdated
parser.extend('nextToken', (nextToken) => { | ||
return function(pos, message) { | ||
nextToken.call(this, pos, message); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of nextToken.call
and other .call
's we should have const ReflectApply = Reflect.apply
and then use it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For my education, can I ask why? First, I don't see const ReflectApply = Reflect.apply
as a common pattern inside of the node codebase (I only found two occurrences, and even those two differed). Second, I believe that .call
is more concise and clearer (at a minimum, I wouldn't need to put []
around the arguments). What am I missing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since core is not run in its own isolated context but in the one shared with the user, its builtins are the same ones that user-land can touch and interact with. Grabbing references to builtin prototype methods and static builtin methods before user-land code executes is an attempt to strengthen the core against accidental user-land augmentation. While handled inconsistently in Node core, new code added seems to have these guards. It's reasonable, where possible, to keep that up.
The use of ReflectApply
covers the Function#call
case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jdalton there was a long discussion around this but no conclusion. As long as this is not something everyone agrees to I think it is best to keep these things to the PR openers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Great work @rubys and thanks a lot for taking up my suggestion.
lib/repl.js
Outdated
parser.extend('nextToken', (nextToken) => { | ||
return function(pos, message) { | ||
nextToken.call(this, pos, message); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jdalton there was a long discussion around this but no conclusion. As long as this is not something everyone agrees to I think it is best to keep these things to the PR openers.
lib/repl.js
Outdated
return function(pos, message) { | ||
switch (message) { | ||
case 'Unterminated template': | ||
case 'Unterminated comment': |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have to manually update acorn to run into this issue. Having a failed test in such a circumstance would be fine and we'd have to invest that one way or the other. So I guess it is fine to keep it is as.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is awesome. Thanks!
lib/internal/repl/recoverable.js
Outdated
// change these messages in the future, this will lead to a test | ||
// failure, indicating that this code needs to be updated. | ||
// | ||
acorn.plugins.repl = (parser) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: replRecoverable
maybe?
lib/internal/repl/recoverable.js
Outdated
|
||
case 'Unterminated string constant': | ||
const token = this.input.slice(this.lastTokStart, this.pos); | ||
recoverable = token.endsWith('\\\n'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👆 Line continuations allow more than just \
followed by \n
. See this bit of this.
This might be hit by folks copy-pasting code chunks in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. I think it could be more than just Windows users (and potentially more than acorn currently supports). HTML specifies \r\n terminators for form input. And the ECMA standard defines more than \r and \n.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change requested here for Windows user considerations.
lib/internal/repl/recoverable.js
Outdated
const token = this.input.slice(this.lastTokStart, this.pos); | ||
// see https://www.ecma-international.org/ecma-262/#sec-line-terminators | ||
recoverable = token.match(/\\(\r\n?|\n|\u2028|\u2029)$/); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
token.match()
will return an array or null
.
You probably want /\\(?:\r\n?|\n|\u2028|\u2029)$/.test(token)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 🎉, with a minor nit.
lib/internal/repl/recoverable.js
Outdated
// but Acorn detected no issue. Presume that additional text won't | ||
// address this issue. | ||
return false; | ||
} catch (e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: since e
is not being used inside the catch block, can we get rid of it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we have to use them until eslint updates to support optional catch bindings and we update to that eslint.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems to work just fine:
$ git diff
diff --git a/lib/internal/repl/recoverable.js b/lib/internal/repl/recoverable.js
index 29411d6534..465d77451a 100644
--- a/lib/internal/repl/recoverable.js
+++ b/lib/internal/repl/recoverable.js
@@ -69,7 +69,7 @@ function isRecoverableError(e, code) {
// but Acorn detected no issue. Presume that additional text won't
// address this issue.
return false;
- } catch (e) {
+ } catch {
return recoverable;
}
}
$ make jslint
Running JS linter...
Please use lint-js instead of jslint
Should I push it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it's fine. We have babel-eslint and already use optional catch bindings in the codebase
Resume Build: https://ci.nodejs.org/job/node-test-pull-request/16138/ |
isRecoverableError is completely reimplemented using acorn and an acorn plugin that examines the state of the parser at the time of the error to determine if the code could be completed on a subsequent line.
Added comments for regular expressions that need to match and comparisons against exception message strings produced by Acorn.
There wasn't a conflict with master, but I rebased, tested, and pushed it anyway. |
Landed in a2ec808 |
isRecoverableError is completely reimplemented using acorn and an acorn plugin that examines the state of the parser at the time of the error to determine if the code could be completed on a subsequent line. PR-URL: nodejs#21805 Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com> Reviewed-By: John-David Dalton <john.david.dalton@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
isRecoverableError is completely reimplemented using acorn and an acorn plugin that examines the state of the parser at the time of the error to determine if the code could be completed on a subsequent line. PR-URL: #21805 Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com> Reviewed-By: John-David Dalton <john.david.dalton@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
isRecoverableError is completely reimplemented using acorn and an
acorn plugin that examines the state of the parser at the time of the
error to determine if the code could be completed on a subsequent line.
fixes #21657
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passes