2006-11-20 W. Andy Carrel <wac@google.com>
authorap <ap@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 20 Nov 2006 20:24:22 +0000 (20:24 +0000)
committerap <ap@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 20 Nov 2006 20:24:22 +0000 (20:24 +0000)
        Reviewed by Maciej.

        http://bugs.webkit.org/show_bug.cgi?id=11501
        REGRESSION: \u no longer escapes metacharacters in RegExps
        http://bugs.webkit.org/show_bug.cgi?id=11502
        Serializing RegExps doesn't preserve Unicode escapes

JavaScriptCore:
        * kjs/lexer.cpp:
        (Lexer::Lexer):
        (Lexer::setCode):
        (Lexer::shift):
        (Lexer::scanRegExp):
        Push \u parsing back down into the RegExp object rather than in the
        parser. This backs out r17354 in favor of a new fix that better
        matches the behavior of other browsers.

        * kjs/lexer.h:
        * kjs/regexp.cpp:
        (KJS::RegExp::RegExp):
        (KJS::sanitizePattern):
        (KJS::isHexDigit):
        (KJS::convertHex):
        (KJS::convertUnicode):
        * kjs/regexp.h:
        Translate \u escaped unicode characters for the benefit of pcre.

        * kjs/ustring.cpp:
        (KJS::UString::append):
        Fix failure to increment length on the first UChar appended to a
        UString that was copy-on-write.

        * tests/mozilla/ecma_2/RegExp/properties-001.js:
        Adjust tests back to the uniform standards.

LayoutTests:
        * fast/js/kde/RegExp-expected.txt:
        * fast/js/regexp-unicode-handling-expected.txt:
        Adjust these test results to passing as a result of other included
        changes in this revision.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@17862 268f45cc-cd09-0410-ab3c-d52691b4dbfc

JavaScriptCore/ChangeLog
JavaScriptCore/kjs/lexer.cpp
JavaScriptCore/kjs/lexer.h
JavaScriptCore/kjs/regexp.cpp
JavaScriptCore/kjs/regexp.h
JavaScriptCore/kjs/ustring.cpp
JavaScriptCore/tests/mozilla/ecma_2/RegExp/properties-001.js
LayoutTests/ChangeLog
LayoutTests/fast/js/kde/RegExp-expected.txt
LayoutTests/fast/js/regexp-unicode-handling-expected.txt

index bfe3f5b1c14a6a8e7937a83a0e9ff7b1e1656a6e..5902ab9ad9dfd55270d819685a2491c5b245515c 100644 (file)
@@ -1,3 +1,39 @@
+2006-11-20  W. Andy Carrel  <wac@google.com>
+
+        Reviewed by Maciej.
+
+        http://bugs.webkit.org/show_bug.cgi?id=11501
+        REGRESSION: \u no longer escapes metacharacters in RegExps
+        http://bugs.webkit.org/show_bug.cgi?id=11502
+        Serializing RegExps doesn't preserve Unicode escapes
+
+        * kjs/lexer.cpp:
+        (Lexer::Lexer):
+        (Lexer::setCode):
+        (Lexer::shift):
+        (Lexer::scanRegExp):
+        Push \u parsing back down into the RegExp object rather than in the
+        parser. This backs out r17354 in favor of a new fix that better 
+        matches the behavior of other browsers.
+
+        * kjs/lexer.h:
+        * kjs/regexp.cpp:
+        (KJS::RegExp::RegExp):
+        (KJS::sanitizePattern):
+        (KJS::isHexDigit):
+        (KJS::convertHex):
+        (KJS::convertUnicode):
+        * kjs/regexp.h:
+        Translate \u escaped unicode characters for the benefit of pcre.
+
+        * kjs/ustring.cpp:
+        (KJS::UString::append):
+        Fix failure to increment length on the first UChar appended to a 
+        UString that was copy-on-write.
+
+        * tests/mozilla/ecma_2/RegExp/properties-001.js:
+        Adjust tests back to the uniform standards.
+
 2006-11-20  Samuel Weinig  <sam@webkit.org>
 
         Reviewed by Maciej.
index 04aea229d42a0c08986274e17cd361dceb92c359..b6a84e92503420849f91380f1cd35e3c4ad92635 100644 (file)
@@ -61,7 +61,7 @@ Lexer::Lexer()
 #ifndef KJS_PURE_ECMA
     bol(true),
 #endif
-    current(0), next1(0), next2(0), next3(0), next4(0),
+    current(0), next1(0), next2(0), next3(0),
     strings(0), numStrings(0), stringsCapacity(0),
     identifiers(0), numIdentifiers(0), identifiersCapacity(0)
 {
@@ -119,7 +119,6 @@ void Lexer::setCode(const UString &sourceURL, int startingLineNumber, const KJS:
   next1 = (length > 1) ? code[1].uc : -1;
   next2 = (length > 2) ? code[2].uc : -1;
   next3 = (length > 3) ? code[3].uc : -1;
-  next4 = (length > 4) ? code[4].uc : -1;
 }
 
 void Lexer::shift(unsigned int p)
@@ -131,8 +130,7 @@ void Lexer::shift(unsigned int p)
     current = next1;
     next1 = next2;
     next2 = next3;
-    next3 = next4;
-    next4 = (pos + 4 < length) ? code[pos+4].uc : -1;
+    next3 = (pos + 3 < length) ? code[pos + 3].uc : -1;
   }
 }
 
@@ -836,29 +834,14 @@ bool Lexer::scanRegExp()
       return false;
     else if (current != '/' || lastWasEscape == true || inBrackets == true)
     {
-        if (lastWasEscape) {
-          // deal with unicode escapes in inline regexps
-          if (current == 'u') {
-            if (isHexDigit(next1) && isHexDigit(next2) && 
-                isHexDigit(next3) && isHexDigit(next4)) {
-              record16(convertUnicode(next1, next2, next3, next4));
-              shift(5);
-              lastWasEscape = false;
-              continue;
-            } else 
-              // this wasn't unicode after all
-              record16('\\');
-          }
-        } else {
-          // keep track of '[' and ']'
+        // keep track of '[' and ']'
+        if (!lastWasEscape) {
           if ( current == '[' && !inBrackets )
             inBrackets = true;
           if ( current == ']' && inBrackets )
             inBrackets = false;
         }
-        // don't want to capture the '\' for unicode escapes
-        if (current != '\\' || next1 != 'u')
-          record16(current);
+        record16(current);
         lastWasEscape =
             !lastWasEscape && (current == '\\');
     }
index f8e2da03540b9db7b1bedb08b20a0c547e66e55f..9bddf8e48698eb4dd94d34ac843fc61f4c1761de 100644 (file)
@@ -145,7 +145,7 @@ namespace KJS {
     bool error;
 
     // current and following unicode characters (int to allow for -1 for end-of-file marker)
-    int current, next1, next2, next3, next4;
+    int current, next1, next2, next3;
 
     UString **strings;
     unsigned int numStrings;
index ae3010a03a1953ff3b9c78514cacaca26e3b4f19..e2637aa11691c394d7afe64faf6e3437929bb444 100644 (file)
@@ -44,12 +44,20 @@ RegExp::RegExp(const UString &p, int flags)
 
   const char *errorMessage;
   int errorOffset;
-  UString nullTerminated(p);
-  char null(0);
-  nullTerminated.append(null);
-  _regex = pcre_compile(reinterpret_cast<const uint16_t *>(nullTerminated.data()), options, &errorMessage, &errorOffset, NULL);
-  if (!_regex)
-    return;
+  
+  UString pattern(p);
+  
+  pattern.append('\0');
+  _regex = pcre_compile(reinterpret_cast<const uint16_t*>(pattern.data()),
+                        options, &errorMessage, &errorOffset, NULL);
+  if (!_regex) {
+    // Try again, this time handle any \u we might find.
+    UString uPattern = sanitizePattern(pattern);
+    _regex = pcre_compile(reinterpret_cast<const uint16_t*>(uPattern.data()),
+                          options, &errorMessage, &errorOffset, NULL);
+    if (!_regex) 
+      return;
+  }
 
 #ifdef PCRE_INFO_CAPTURECOUNT
   // Get number of subpatterns that will be returned.
@@ -173,4 +181,78 @@ UString RegExp::match(const UString &s, int i, int *pos, int **ovector)
 #endif
 }
 
+UString RegExp::sanitizePattern(const UString& p)
+{
+  UString newPattern;
+  
+  int startPos = 0;
+  int pos = p.find("\\u", 0) + 2; // Skip the \u
+  
+  while (pos != 1) { // p.find failing is -1 + 2 = 1 
+    if (pos + 3 < p.size()) {
+      if (isHexDigit(p[pos]) && isHexDigit(p[pos + 1]) &&
+          isHexDigit(p[pos + 2]) && isHexDigit(p[pos + 3])) {
+        newPattern.append(p.substr(startPos, pos - startPos - 2));
+        UChar escapedUnicode(convertUnicode(p[pos], p[pos + 1], 
+                                            p[pos + 2], p[pos + 3]));
+        // \u encoded characters should be treated as if they were escaped,
+        // so add an escape for certain characters that need it.
+        switch (escapedUnicode.unicode()) {
+          case '|':
+          case '+':
+          case '*':
+          case '(':
+          case ')':
+          case '[':
+          case ']':
+          case '{':
+          case '}':
+          case '?':
+          case '\\':
+            newPattern.append('\\');
+        }
+        newPattern.append(escapedUnicode);
+
+        startPos = pos + 4;
+      }
+    }
+    pos = p.find("\\u", pos) + 2;
+  }
+  newPattern.append(p.substr(startPos, p.size() - startPos));
+
+  return newPattern;
+}
+
+bool RegExp::isHexDigit(UChar uc)
+{
+  int c = uc.unicode();
+  return (c >= '0' && c <= '9' ||
+          c >= 'a' && c <= 'f' ||
+          c >= 'A' && c <= 'F');
+}
+
+unsigned char RegExp::convertHex(int c)
+{
+  if (c >= '0' && c <= '9')
+    return static_cast<unsigned char>(c - '0');
+  if (c >= 'a' && c <= 'f')
+    return static_cast<unsigned char>(c - 'a' + 10);
+  return static_cast<unsigned char>(c - 'A' + 10);
+}
+
+unsigned char RegExp::convertHex(int c1, int c2)
+{
+  return ((convertHex(c1) << 4) + convertHex(c2));
+}
+
+UChar RegExp::convertUnicode(UChar uc1, UChar uc2, UChar uc3, UChar uc4)
+{
+  int c1 = uc1.unicode();
+  int c2 = uc2.unicode();
+  int c3 = uc3.unicode();
+  int c4 = uc4.unicode();
+  return UChar((convertHex(c1) << 4) + convertHex(c2),
+               (convertHex(c3) << 4) + convertHex(c4));
+}
+
 } // namespace KJS
index 88674aec8a95a028a1b2c2b545bfb0963750d1f2..cc4a672446f4079f64145576df4ca6b2777cdbb6 100644 (file)
@@ -61,6 +61,13 @@ namespace KJS {
 
     RegExp(const RegExp &);
     RegExp &operator=(const RegExp &);
+
+    static UString sanitizePattern(const UString&);
+
+    static bool isHexDigit(UChar);
+    static unsigned char convertHex(int);
+    static unsigned char convertHex(int, int);
+    static UChar convertUnicode(UChar, UChar, UChar, UChar);
   };
 
 } // namespace
index af626eea711fe7ad8a9ca866523c6affa7b86bce..522bf96bb68502e991caebd87722016999b0e640 100644 (file)
@@ -749,7 +749,7 @@ UString &UString::append(unsigned short c)
     UChar *d = static_cast<UChar *>(fastMalloc(sizeof(UChar) * newCapacity));
     memcpy(d, data(), length * sizeof(UChar));
     d[length] = c;
-    m_rep = Rep::create(d, length);
+    m_rep = Rep::create(d, length + 1);
     m_rep->capacity = newCapacity;
   }
 
index 835a25ab09cc410693153d9d97482a625d0376a7..16f265c352be90f252f86160e3c03c2e5f456d5a 100644 (file)
@@ -22,7 +22,7 @@
     AddRegExpCases( /[a-zA-Z0-9]*/gm, "[a-zA-Z0-9]*", true, false, true, 0 );
     AddRegExpCases( /x|y|z/gim, "x|y|z", true, true, true, 0 );
 
-    AddRegExpCases( /\u0051/im, "\u0051", false, true, true, 0 );
+    AddRegExpCases( /\u0051/im, "\\u0051", false, true, true, 0 );
     AddRegExpCases( /\x45/gm, "\\x45", true, false, true, 0 );
     AddRegExpCases( /\097/gi, "\\097", true, true, false, 0 );
 
index 2ca25e211ba67854548eac6d2a20f620c2188c62..6e930846f74fb1c7d3b4341249376b990599773e 100644 (file)
@@ -1,3 +1,17 @@
+2006-11-20  W. Andy Carrel  <wac@google.com>
+
+        Reviewed by Maciej.
+
+        http://bugs.webkit.org/show_bug.cgi?id=11501
+        REGRESSION: \u no longer escapes metacharacters in RegExps
+        http://bugs.webkit.org/show_bug.cgi?id=11502
+        Serializing RegExps doesn't preserve Unicode escapes
+
+        * fast/js/kde/RegExp-expected.txt:
+        * fast/js/regexp-unicode-handling-expected.txt:
+        Adjust these test results to passing as a result of other included
+        changes in this revision.
+
 2006-11-20  Alexey Proskuryakov  <ap@webkit.org>
 
         Reviewed by Maciej.
index e87954d6dbfbca3cdb0a25ee55fce51d2473e411..78bf976900a9f69aea36de3048cd0bc7dde9982e 100644 (file)
@@ -88,7 +88,7 @@ PASS reg.x = 1; reg.x is 1
 PASS var r2 = reg; r2.x = 2; reg.x is 2
 PASS str.match(re).toString() is 'Chapter 3.4.5.1,Chapter 3.4.5.1,.1'
 PASS str.match(/d/gi).toString() is 'D,d'
-FAIL /\u0061/.source should be \u0061 (of type string). Was a (of type string).
+PASS /\u0061/.source is '\\u0061'
 PASS 'abc'.match(/\u0062/).toString() is 'b'
 FAIL Object.prototype.toString.apply(RegExp.prototype) should be [object RegExp] (of type string). Was [object RegExpPrototype] (of type string).
 PASS typeof RegExp.prototype.toString() is 'string'
index ffca77ae8d5a79da3b4b03cb78ffcd84824cbbb6..1416afb7d39964e4ce9051a460ba65cd7c521150 100644 (file)
@@ -10,39 +10,39 @@ PASS inlineRe.source is evalFromInlineRe.source
 PASS inlineRe.source is evalInlineRe.source
 PASS inlineRe.source is newFromEvalInlineRe.source
 PASS inlineRe.source is evalFromEvalInlineRe.source
-FAIL inlineRe.source should be .m\u2820p (of type string). Was .m⠠p (of type string).
-FAIL inlineRe.source should be .m\u2820p (of type string). Was .m⠠p (of type string).
+PASS inlineRe.source is explicitRe.source
+PASS inlineRe.source is newFromExplicitRe.source
 PASS inlineRe.source is evalFromExplicitRe.source
 PASS inlineRe.toString() is newFromInlineRe.toString()
 PASS inlineRe.toString() is evalFromInlineRe.toString()
 PASS inlineRe.toString() is evalInlineRe.toString()
 PASS inlineRe.toString() is newFromEvalInlineRe.toString()
 PASS inlineRe.toString() is evalFromEvalInlineRe.toString()
-FAIL inlineRe.toString() should be /.m\u2820p/ (of type string). Was /.m⠠p/ (of type string).
-FAIL inlineRe.toString() should be /.m\u2820p/ (of type string). Was /.m⠠p/ (of type string).
+PASS inlineRe.toString() is explicitRe.toString()
+PASS inlineRe.toString() is newFromExplicitRe.toString()
 PASS inlineRe.toString() is evalFromExplicitRe.toString()
 PASS inlineRe.exec(sample)[0] is 'bm⠠p'
 PASS evalInlineRe.exec(sample)[0] is 'bm⠠p'
-FAIL explicitRe.exec(sample)[0] should be bm⠠p. Threw exception TypeError: Null value
+PASS explicitRe.exec(sample)[0] is 'bm⠠p'
 PASS binlineRe.source is bnewFromInlineRe.source
 PASS binlineRe.source is bevalFromInlineRe.source
 PASS binlineRe.source is bevalInlineRe.source
 PASS binlineRe.source is bnewFromEvalInlineRe.source
 PASS binlineRe.source is bevalFromEvalInlineRe.source
-FAIL binlineRe.source should be .m\u007cp (of type string). Was .m|p (of type string).
-FAIL binlineRe.source should be .m\u007cp (of type string). Was .m|p (of type string).
+PASS binlineRe.source is bexplicitRe.source
+PASS binlineRe.source is bnewFromExplicitRe.source
 PASS binlineRe.source is bevalFromExplicitRe.source
 PASS binlineRe.toString() is bnewFromInlineRe.toString()
 PASS binlineRe.toString() is bevalFromInlineRe.toString()
 PASS binlineRe.toString() is bevalInlineRe.toString()
 PASS binlineRe.toString() is bnewFromEvalInlineRe.toString()
 PASS binlineRe.toString() is bevalFromEvalInlineRe.toString()
-FAIL binlineRe.toString() should be /.m\u007cp/ (of type string). Was /.m|p/ (of type string).
-FAIL binlineRe.toString() should be /.m\u007cp/ (of type string). Was /.m|p/ (of type string).
+PASS binlineRe.toString() is bexplicitRe.toString()
+PASS binlineRe.toString() is bnewFromExplicitRe.toString()
 PASS binlineRe.toString() is bevalFromExplicitRe.toString()
-FAIL binlineRe.exec(bsample)[0] should be bm|p (of type string). Was am (of type string).
-FAIL bevalInlineRe.exec(bsample)[0] should be bm|p (of type string). Was am (of type string).
-FAIL bexplicitRe.exec(bsample)[0] should be bm|p. Threw exception TypeError: Null value
+PASS binlineRe.exec(bsample)[0] is 'bm|p'
+PASS bevalInlineRe.exec(bsample)[0] is 'bm|p'
+PASS bexplicitRe.exec(bsample)[0] is 'bm|p'
 PASS successfullyParsed is true
 
 TEST COMPLETE