| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,10 +7,19 @@ const { | |||
| 7 | 7 | ArrayPrototypePush, | |
| 8 | 8 | FunctionPrototype, | |
| 9 | 9 | ObjectKeys, | |
| 10 | + RegExpPrototypeSymbolReplace, | ||
| 11 | + StringPrototypeEndsWith, | ||
| 12 | + StringPrototypeIncludes, | ||
| 13 | + StringPrototypeIndexOf, | ||
| 14 | + StringPrototypeRepeat, | ||
| 15 | + StringPrototypeSplit, | ||
| 16 | + StringPrototypeStartsWith, | ||
| 17 | + SyntaxError, | ||
| 10 | 18 | } = primordials; | |
| 11 | 19 | ||
| 12 | 20 | const parser = require('internal/deps/acorn/acorn/dist/acorn').Parser; | |
| 13 | 21 | const walk = require('internal/deps/acorn/acorn-walk/dist/walk'); | |
| 22 | + const { Recoverable } = require('internal/repl'); | ||
| 14 | 23 | ||
| 15 | 24 | const noop = FunctionPrototype; | |
| 16 | 25 | const visitorsWithoutAncestors = { | |
@@ -79,13 +88,40 @@ for (const nodeType of ObjectKeys(walk.base)) { | |||
| 79 | 88 | } | |
| 80 | 89 | ||
| 81 | 90 | function processTopLevelAwait(src) { | |
| 82 | - const wrapped = `(async () => { ${src} })()`; | ||
| 91 | + const wrapPrefix = '(async () => { '; | ||
| 92 | + const wrapped = `${wrapPrefix}${src} })()`; | ||
| 83 | 93 | const wrappedArray = ArrayFrom(wrapped); | |
| 84 | 94 | let root; | |
| 85 | 95 | try { | |
| 86 | 96 | root = parser.parse(wrapped, { ecmaVersion: 'latest' }); | |
| 87 | - } catch { | ||
| 88 | - return null; | ||
| 97 | + } catch (e) { | ||
| 98 | + if (StringPrototypeStartsWith(e.message, 'Unterminated ')) | ||
| 99 | + throw new Recoverable(e); | ||
| 100 | + // If the parse error is before the first "await", then use the execution | ||
| 101 | + // error. Otherwise we must emit this parse error, making it look like a | ||
| 102 | + // proper syntax error. | ||
| 103 | + const awaitPos = StringPrototypeIndexOf(src, 'await'); | ||
| 104 | + const errPos = e.pos - wrapPrefix.length; | ||
| 105 | + if (awaitPos > errPos) | ||
| 106 | + return null; | ||
| 107 | + // Convert keyword parse errors on await into their original errors when | ||
| 108 | + // possible. | ||
| 109 | + if (errPos === awaitPos + 6 && | ||
| 110 | + StringPrototypeIncludes(e.message, 'Expecting Unicode escape sequence')) | ||
| 111 | + return null; | ||
| 112 | + if (errPos === awaitPos + 7 && | ||
| 113 | + StringPrototypeIncludes(e.message, 'Unexpected token')) | ||
| 114 | + return null; | ||
| 115 | + const line = e.loc.line; | ||
| 116 | + const column = line === 1 ? e.loc.column - wrapPrefix.length : e.loc.column; | ||
| 117 | + let message = '\n' + StringPrototypeSplit(src, '\n')[line - 1] + '\n' + | ||
| 118 | + StringPrototypeRepeat(' ', column) + | ||
| 119 | + '^\n\n' + RegExpPrototypeSymbolReplace(/ \([^)]+\)/, e.message, ''); | ||
| 120 | + // V8 unexpected token errors include the token string. | ||
| 121 | + if (StringPrototypeEndsWith(message, 'Unexpected token')) | ||
| 122 | + message += " '" + src[e.pos - wrapPrefix.length] + "'"; | ||
| 123 | + // eslint-disable-next-line no-restricted-syntax | ||
| 124 | + throw new SyntaxError(message); | ||
| 89 | 125 | } | |
| 90 | 126 | const body = root.body[0].expression.callee.body; | |
| 91 | 127 | const state = { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -431,59 +431,66 @@ function REPLServer(prompt, | |||
| 431 | 431 | ({ processTopLevelAwait } = require('internal/repl/await')); | |
| 432 | 432 | } | |
| 433 | 433 | ||
| 434 | - const potentialWrappedCode = processTopLevelAwait(code); | ||
| 435 | - if (potentialWrappedCode !== null) { | ||
| 436 | - code = potentialWrappedCode; | ||
| 437 | - wrappedCmd = true; | ||
| 438 | - awaitPromise = true; | ||
| 434 | + try { | ||
| 435 | + const potentialWrappedCode = processTopLevelAwait(code); | ||
| 436 | + if (potentialWrappedCode !== null) { | ||
| 437 | + code = potentialWrappedCode; | ||
| 438 | + wrappedCmd = true; | ||
| 439 | + awaitPromise = true; | ||
| 440 | + } | ||
| 441 | + } catch (e) { | ||
| 442 | + decorateErrorStack(e); | ||
| 443 | + err = e; | ||
| 439 | 444 | } | |
| 440 | 445 | } | |
| 441 | 446 | ||
| 442 | 447 | // First, create the Script object to check the syntax | |
| 443 | 448 | if (code === '\n') | |
| 444 | 449 | return cb(null); | |
| 445 | 450 | ||
| 446 | - let parentURL; | ||
| 447 | - try { | ||
| 448 | - const { pathToFileURL } = require('url'); | ||
| 449 | - // Adding `/repl` prevents dynamic imports from loading relative | ||
| 450 | - // to the parent of `process.cwd()`. | ||
| 451 | - parentURL = pathToFileURL(path.join(process.cwd(), 'repl')).href; | ||
| 452 | - } catch { | ||
| 453 | - } | ||
| 454 | - while (true) { | ||
| 451 | + if (err === null) { | ||
| 452 | + let parentURL; | ||
| 455 | 453 | try { | |
| 456 | - if (self.replMode === module.exports.REPL_MODE_STRICT && | ||
| 457 | - !RegExpPrototypeTest(/^\s*$/, code)) { | ||
| 458 | - // "void 0" keeps the repl from returning "use strict" as the result | ||
| 459 | - // value for statements and declarations that don't return a value. | ||
| 460 | - code = `'use strict'; void 0;\n${code}`; | ||
| 461 | - } | ||
| 462 | - script = vm.createScript(code, { | ||
| 463 | - filename: file, | ||
| 464 | - displayErrors: true, | ||
| 465 | - importModuleDynamically: async (specifier) => { | ||
| 466 | - return asyncESM.ESMLoader.import(specifier, parentURL); | ||
| 454 | + const { pathToFileURL } = require('url'); | ||
| 455 | + // Adding `/repl` prevents dynamic imports from loading relative | ||
| 456 | + // to the parent of `process.cwd()`. | ||
| 457 | + parentURL = pathToFileURL(path.join(process.cwd(), 'repl')).href; | ||
| 458 | + } catch { | ||
| 459 | + } | ||
| 460 | + while (true) { | ||
| 461 | + try { | ||
| 462 | + if (self.replMode === module.exports.REPL_MODE_STRICT && | ||
| 463 | + !RegExpPrototypeTest(/^\s*$/, code)) { | ||
| 464 | + // "void 0" keeps the repl from returning "use strict" as the result | ||
| 465 | + // value for statements and declarations that don't return a value. | ||
| 466 | + code = `'use strict'; void 0;\n${code}`; | ||
| 467 | 467 | } | |
| 468 | - }); | ||
| 469 | - } catch (e) { | ||
| 470 | - debug('parse error %j', code, e); | ||
| 471 | - if (wrappedCmd) { | ||
| 472 | - // Unwrap and try again | ||
| 473 | - wrappedCmd = false; | ||
| 474 | - awaitPromise = false; | ||
| 475 | - code = input; | ||
| 476 | - wrappedErr = e; | ||
| 477 | - continue; | ||
| 468 | + script = vm.createScript(code, { | ||
| 469 | + filename: file, | ||
| 470 | + displayErrors: true, | ||
| 471 | + importModuleDynamically: async (specifier) => { | ||
| 472 | + return asyncESM.ESMLoader.import(specifier, parentURL); | ||
| 473 | + } | ||
| 474 | + }); | ||
| 475 | + } catch (e) { | ||
| 476 | + debug('parse error %j', code, e); | ||
| 477 | + if (wrappedCmd) { | ||
| 478 | + // Unwrap and try again | ||
| 479 | + wrappedCmd = false; | ||
| 480 | + awaitPromise = false; | ||
| 481 | + code = input; | ||
| 482 | + wrappedErr = e; | ||
| 483 | + continue; | ||
| 484 | + } | ||
| 485 | + // Preserve original error for wrapped command | ||
| 486 | + const error = wrappedErr || e; | ||
| 487 | + if (isRecoverableError(error, code)) | ||
| 488 | + err = new Recoverable(error); | ||
| 489 | + else | ||
| 490 | + err = error; | ||
| 478 | 491 | } | |
| 479 | - // Preserve original error for wrapped command | ||
| 480 | - const error = wrappedErr || e; | ||
| 481 | - if (isRecoverableError(error, code)) | ||
| 482 | - err = new Recoverable(error); | ||
| 483 | - else | ||
| 484 | - err = error; | ||
| 492 | + break; | ||
| 485 | 493 | } | |
| 486 | - break; | ||
| 487 | 494 | } | |
| 488 | 495 | ||
| 489 | 496 | // This will set the values from `savedRegExMatches` to corresponding | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -142,6 +142,16 @@ async function ordinaryTests() { | |||
| 142 | 142 | 'undefined', | |
| 143 | 143 | ], | |
| 144 | 144 | ], | |
| 145 | + ['await Promise..resolve()', | ||
| 146 | + [ | ||
| 147 | + 'await Promise..resolve()\r', | ||
| 148 | + 'Uncaught SyntaxError: ', | ||
| 149 | + 'await Promise..resolve()', | ||
| 150 | + ' ^', | ||
| 151 | + '', | ||
| 152 | + 'Unexpected token \'.\'', | ||
| 153 | + ], | ||
| 154 | + ], | ||
| 145 | 155 | ]; | |
| 146 | 156 | ||
| 147 | 157 | for (const [input, expected = [`${input}\r`], options = {}] of testCases) { | |
| Back | FazBrowse Home | New Git URL |
0 commit comments