Bug 1317310: Disallow trailing decimal digit after null-escape in character classes in Unicode regular expressions. r=arai
authorAndré Bargull <andre.bargull@gmail.com>
Mon, 14 Nov 2016 12:26:59 -0800
changeset 322709 8f843fadcf4e74a47246dea72342324c97ce65c7
parent 322708 4419ad4be612d186fb5cf00b7b1590d41cbb0b07
child 322710 a4bc362f24cc07f7ccf7a12f9001aced0ec5d47c
push id30961
push userkwierso@gmail.com
push dateThu, 17 Nov 2016 01:08:03 +0000
treeherdermozilla-central@c27117f67fa3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarai
bugs1317310
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1317310: Disallow trailing decimal digit after null-escape in character classes in Unicode regular expressions. r=arai
js/src/irregexp/RegExpParser.cpp
js/src/tests/ecma_6/RegExp/unicode-disallow-extended.js
--- a/js/src/irregexp/RegExpParser.cpp
+++ b/js/src/irregexp/RegExpParser.cpp
@@ -441,16 +441,31 @@ IsSyntaxCharacter(widechar c)
     case '|':
     case '/':
       return true;
     default:
       return false;
   }
 }
 
+inline bool
+IsInRange(int value, int lower_limit, int higher_limit)
+{
+    MOZ_ASSERT(lower_limit <= higher_limit);
+    return static_cast<unsigned int>(value - lower_limit) <=
+           static_cast<unsigned int>(higher_limit - lower_limit);
+}
+
+inline bool
+IsDecimalDigit(widechar c)
+{
+    // ECMA-262, 3rd, 7.8.3 (p 16)
+    return IsInRange(c, '0', '9');
+}
+
 #ifdef DEBUG
 // Currently only used in an assert.kASSERT.
 static bool
 IsSpecialClassEscape(widechar c)
 {
   switch (c) {
     case 'd': case 'D':
     case 's': case 'S':
@@ -517,24 +532,27 @@ RegExpParser<CharT>::ParseClassCharacter
             ReportError(JSMSG_INVALID_IDENTITY_ESCAPE);
             return false;
         }
         // We match JSC in reading the backslash as a literal
         // character instead of as starting an escape.
         *code = '\\';
         return true;
       }
-      case '0': case '1': case '2': case '3': case '4': case '5':
-      case '6': case '7':
+      case '0':
         if (unicode_) {
-            if (current() == '0') {
-                Advance();
-                *code = 0;
-                return true;
-            }
+            Advance();
+            if (IsDecimalDigit(current()))
+                return ReportError(JSMSG_INVALID_DECIMAL_ESCAPE);
+            *code = 0;
+            return true;
+        }
+        MOZ_FALLTHROUGH;
+      case '1': case '2': case '3': case '4': case '5': case '6': case '7':
+        if (unicode_) {
             ReportError(JSMSG_INVALID_IDENTITY_ESCAPE);
             return false;
         }
         // For compatibility, outside of unicode mode, we interpret a decimal
         // escape that isn't a back reference (and therefore either \0 or not
         // valid according to the specification) as a 1..3 digit octal
         // character code.
         *code = ParseOctalLiteral();
@@ -1153,31 +1171,16 @@ RegExpParser<CharT>::ScanForCaptures()
             if (current() != '?') capture_count++;
             break;
         }
     }
     capture_count_ = capture_count;
     is_scanned_for_captures_ = true;
 }
 
-inline bool
-IsInRange(int value, int lower_limit, int higher_limit)
-{
-    MOZ_ASSERT(lower_limit <= higher_limit);
-    return static_cast<unsigned int>(value - lower_limit) <=
-           static_cast<unsigned int>(higher_limit - lower_limit);
-}
-
-inline bool
-IsDecimalDigit(widechar c)
-{
-    // ECMA-262, 3rd, 7.8.3 (p 16)
-    return IsInRange(c, '0', '9');
-}
-
 template <typename CharT>
 bool
 RegExpParser<CharT>::ParseBackReferenceIndex(int* index_out)
 {
     MOZ_ASSERT('\\' == current());
     MOZ_ASSERT('1' <= Next() && Next() <= '9');
 
     // Try to parse a decimal literal that is no greater than the total number
--- a/js/src/tests/ecma_6/RegExp/unicode-disallow-extended.js
+++ b/js/src/tests/ecma_6/RegExp/unicode-disallow-extended.js
@@ -76,20 +76,28 @@ assertThrowsInstanceOf(() => eval(`/[\\c
 assertThrowsInstanceOf(() => eval(`/[\\c_]/u`), SyntaxError);
 
 // HexEscapeSequence
 assertThrowsInstanceOf(() => eval(`/\\x/u`), SyntaxError);
 assertThrowsInstanceOf(() => eval(`/\\x0/u`), SyntaxError);
 assertThrowsInstanceOf(() => eval(`/\\x1/u`), SyntaxError);
 assertThrowsInstanceOf(() => eval(`/\\x1G/u`), SyntaxError);
 
+assertThrowsInstanceOf(() => eval(`/[\\x]/u`), SyntaxError);
+assertThrowsInstanceOf(() => eval(`/[\\x0]/u`), SyntaxError);
+assertThrowsInstanceOf(() => eval(`/[\\x1]/u`), SyntaxError);
+assertThrowsInstanceOf(() => eval(`/[\\x1G]/u`), SyntaxError);
+
 // LegacyOctalEscapeSequence
 assertThrowsInstanceOf(() => eval(`/\\52/u`), SyntaxError);
 assertThrowsInstanceOf(() => eval(`/\\052/u`), SyntaxError);
 
+assertThrowsInstanceOf(() => eval(`/[\\52]/u`), SyntaxError);
+assertThrowsInstanceOf(() => eval(`/[\\052]/u`), SyntaxError);
+
 // DecimalEscape
 assertEqArray(/\0/u.exec("\0"),
               ["\0"]);
 assertEqArray(/[\0]/u.exec("\0"),
               ["\0"]);
 assertEqArray(/\0A/u.exec("\0A"),
               ["\0A"]);
 assertEqArray(/\0G/u.exec("\0G"),
@@ -108,10 +116,24 @@ assertThrowsInstanceOf(() => eval(`/\\3/
 assertThrowsInstanceOf(() => eval(`/\\4/u`), SyntaxError);
 assertThrowsInstanceOf(() => eval(`/\\5/u`), SyntaxError);
 assertThrowsInstanceOf(() => eval(`/\\6/u`), SyntaxError);
 assertThrowsInstanceOf(() => eval(`/\\7/u`), SyntaxError);
 assertThrowsInstanceOf(() => eval(`/\\8/u`), SyntaxError);
 assertThrowsInstanceOf(() => eval(`/\\9/u`), SyntaxError);
 assertThrowsInstanceOf(() => eval(`/\\10/u`), SyntaxError);
 
+assertThrowsInstanceOf(() => eval(`/[\\00]/u`), SyntaxError);
+assertThrowsInstanceOf(() => eval(`/[\\01]/u`), SyntaxError);
+assertThrowsInstanceOf(() => eval(`/[\\09]/u`), SyntaxError);
+assertThrowsInstanceOf(() => eval(`/[\\1]/u`), SyntaxError);
+assertThrowsInstanceOf(() => eval(`/[\\2]/u`), SyntaxError);
+assertThrowsInstanceOf(() => eval(`/[\\3]/u`), SyntaxError);
+assertThrowsInstanceOf(() => eval(`/[\\4]/u`), SyntaxError);
+assertThrowsInstanceOf(() => eval(`/[\\5]/u`), SyntaxError);
+assertThrowsInstanceOf(() => eval(`/[\\6]/u`), SyntaxError);
+assertThrowsInstanceOf(() => eval(`/[\\7]/u`), SyntaxError);
+assertThrowsInstanceOf(() => eval(`/[\\8]/u`), SyntaxError);
+assertThrowsInstanceOf(() => eval(`/[\\9]/u`), SyntaxError);
+assertThrowsInstanceOf(() => eval(`/[\\10]/u`), SyntaxError);
+
 if (typeof reportCompare === "function")
     reportCompare(true, true);