From 9a9c021001bd5a647ae2aa7859d6281ba1f9efcd Mon Sep 17 00:00:00 2001 From: Prince J Wesley Date: Thu, 11 Feb 2016 03:46:20 +0530 Subject: [PATCH] repl: Handle unicode escape sequence --- lib/repl.js | 89 ++++++++++++++++++++++++++++++++++++++ test/parallel/test-repl.js | 13 ++++++ 2 files changed, 102 insertions(+) diff --git a/lib/repl.js b/lib/repl.js index b9f6f334d98d..1f1b0190beb4 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -73,6 +73,46 @@ exports._builtinLibs = ['assert', 'buffer', 'child_process', 'cluster', const BLOCK_SCOPED_ERROR = 'Block-scoped declarations (let, ' + 'const, function, class) not yet supported outside strict mode'; +class LiteralPattern { + + constructor(prefix, suffixOrMaxLength) { + this._prefix = prefix; + this._prefixLength = prefix.length; + if (typeof suffixOrMaxLength === 'string') { + this._suffix = suffixOrMaxLength; + this._suffixLength = suffixOrMaxLength.length; + } else { + this._maxLength = suffixOrMaxLength; + } + this.lastIndex = 0; + } + + exec(input) { + input = input.substring(this.lastIndex); + const idx = input.indexOf(this._prefix); + if (idx !== -1) { + const start = this._prefixLength + idx; + if (this._suffix) { + input = input.substring(start); + const end = input.indexOf(this._suffix); + if (end !== -1) { + this.lastIndex += start + end + this._suffixLength; + return input.substring(0, end); + } + } else { + this.lastIndex += start + this._maxLength; + return input.substring(this._prefixLength + idx, this._maxLength); + } + } + this.lastIndex = 0; + return null; + } +} + +const unicodePointLikePattern = new LiteralPattern('\\u{', '}'); +const unicodeLikePattern = new LiteralPattern('\\u', 4); +const hexLikePattern = new LiteralPattern('\\x', 2); +const hexPattern = /^[\da-f]+$/i; class LineParser { @@ -82,17 +122,59 @@ class LineParser { reset() { this._literal = null; + this._literalValue = []; this.shouldFail = false; this.blockComment = false; this.regExpLiteral = false; } + parseEscapePattern(literal, pattern, predicate) { + let codePoint; + while ((codePoint = pattern.exec(literal)) !== null) { + if (!predicate(codePoint)) { + pattern.lastIndex = 0; + return false; + } + } + return true; + } + + parseHexSequencePredicate(codePoint) { + return codePoint.length === 2 && hexPattern.test(codePoint); + } + + parseUnicodePredicate(codePoint) { + return codePoint.length === 4 && hexPattern.test(codePoint); + } + + parseUnicodePointPredicate(codePoint) { + return hexPattern.test(codePoint) && parseInt(codePoint, 16) <= 0x10FFFF; + } + + parseEscapeSequence(literal) { + return this.parseEscapePattern(literal, + unicodePointLikePattern, + this.parseUnicodePointPredicate) && + this.parseEscapePattern(literal, + unicodeLikePattern, + this.parseUnicodePredicate) && + this.parseEscapePattern(literal, + hexLikePattern, + this.parseHexSequencePredicate); + } + parseLine(line) { var previous = null; this.shouldFail = false; const wasWithinStrLiteral = this._literal !== null; + if (this._literal) { + this._literalValue.push('\n'); + } for (const current of line) { + if (this._literal && current !== this._literal) { + this._literalValue.push(current); + } if (previous === '\\') { // valid escaping, skip processing. previous doesn't matter anymore previous = null; @@ -135,6 +217,13 @@ class LineParser { if (current === this._literal) { this._literal = null; + const literal = this._literalValue.join(''); + this._literalValue = []; + + if (!this.parseEscapeSequence(literal)) { + this.shouldFail = true; + break; + } } else if (current === '\'' || current === '"') { this._literal = this._literal || current; } diff --git a/test/parallel/test-repl.js b/test/parallel/test-repl.js index ab7df802ada8..b63aca6f89fe 100644 --- a/test/parallel/test-repl.js +++ b/test/parallel/test-repl.js @@ -325,6 +325,19 @@ function error_test() { 'undefined\n' + prompt_unix }, { client: client_unix, send: '{ var x = 4; }', expect: 'undefined\n' + prompt_unix }, + // REPL should handle invalid escape sequences + { client: client_unix, send: "'unicode point \\u{0000000041}'", + expect: "'unicode point A'\n" + prompt_unix }, + { client: client_unix, send: "'unicode \u0041'", + expect: "'unicode A'\n" + prompt_unix }, + { client: client_unix, send: "'hexadecimal \x41'", + expect: "'hexadecimal A'\n" + prompt_unix }, + { client: client_unix, send: "'\\u{10000000041} failure case'", + expect: /^SyntaxError: Unexpected token ILLEGAL/ }, + { client: client_unix, send: "'\\uea failure case'", + expect: /^SyntaxError: Unexpected token ILLEGAL/ }, + { client: client_unix, send: "'\\x1 edge case'", + expect: /^SyntaxError: Unexpected token ILLEGAL/ }, ]); }