Title: [110233] trunk
Revision
110233
Author
[email protected]
Date
2012-03-08 16:49:31 -0800 (Thu, 08 Mar 2012)

Log Message

String.prototype.match and replace do not clear global regexp lastIndex per ES5.1 15.5.4.10
https://bugs.webkit.org/show_bug.cgi?id=26890

Reviewed by Oliver Hunt.

Per 15.10.6.2 step 9.a.1 called via the action of the last iteration of 15.5.4.10 8.f.i.

Source/_javascript_Core: 

* runtime/StringPrototype.cpp:
(JSC::replaceUsingRegExpSearch):
(JSC::stringProtoFuncMatch):
    - added calls to setLastIndex.

LayoutTests: 

* fast/js/script-tests/string-match.js: Added.
* fast/js/string-match-expected.txt: Added.
* fast/js/string-match.html: Added.
    - Added test case for match.
* fast/js/script-tests/string-replace-2.js:
* fast/js/string-replace-2-expected.txt:
    - Added tests for update correct lastIndex handling.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (110232 => 110233)


--- trunk/LayoutTests/ChangeLog	2012-03-09 00:46:38 UTC (rev 110232)
+++ trunk/LayoutTests/ChangeLog	2012-03-09 00:49:31 UTC (rev 110233)
@@ -1,3 +1,20 @@
+2012-03-08  Gavin Barraclough  <[email protected]>
+
+        String.prototype.match and replace do not clear global regexp lastIndex per ES5.1 15.5.4.10
+        https://bugs.webkit.org/show_bug.cgi?id=26890
+
+        Reviewed by Oliver Hunt.
+
+        Per 15.10.6.2 step 9.a.1 called via the action of the last iteration of 15.5.4.10 8.f.i.
+
+        * fast/js/script-tests/string-match.js: Added.
+        * fast/js/string-match-expected.txt: Added.
+        * fast/js/string-match.html: Added.
+            - Added test case for match.
+        * fast/js/script-tests/string-replace-2.js:
+        * fast/js/string-replace-2-expected.txt:
+            - Added tests for update correct lastIndex handling.
+
 2012-03-08  Andy Estes  <[email protected]>
 
         NULL renderer possible in WebCore::HTMLInputElement::setCanReceiveDroppedFiles()

Added: trunk/LayoutTests/fast/js/script-tests/string-match.js (0 => 110233)


--- trunk/LayoutTests/fast/js/script-tests/string-match.js	                        (rev 0)
+++ trunk/LayoutTests/fast/js/script-tests/string-match.js	2012-03-09 00:49:31 UTC (rev 110233)
@@ -0,0 +1,24 @@
+description(
+"String.match(&hellip;) test"
+);
+
+// match with a global regexp should set lastIndex to zero; if read-only this should throw.
+// If the regexp is not global, lastIndex is not modified.
+var re;
+function testMatch(_re, readonly)
+{
+    re = _re;
+    re.lastIndex = 3;
+    if (readonly)
+        re = Object.defineProperty(re, 'lastIndex', {writable:false});
+    return '0x1x2'.match(re);
+}
+
+shouldBe("testMatch(/x/g, false)", '["x","x"]');
+shouldThrow("testMatch(/x/g, true)");
+shouldBe("testMatch(/x/, false)", '["x"]');
+shouldBe("testMatch(/x/, true)", '["x"]');
+shouldBe("testMatch(/x/g, false); re.lastIndex", '0');
+shouldThrow("testMatch(/x/g, true); re.lastIndex");
+shouldBe("testMatch(/x/, false); re.lastIndex", '3');
+shouldBe("testMatch(/x/, true); re.lastIndex", '3');

Modified: trunk/LayoutTests/fast/js/script-tests/string-replace-2.js (110232 => 110233)


--- trunk/LayoutTests/fast/js/script-tests/string-replace-2.js	2012-03-09 00:46:38 UTC (rev 110232)
+++ trunk/LayoutTests/fast/js/script-tests/string-replace-2.js	2012-03-09 00:49:31 UTC (rev 110233)
@@ -18,3 +18,53 @@
 shouldBe("testString.replace(/(.*)/g, function replaceWithDollars(matchGroup) { return '$1'; })", "\"$1$1\"");
 shouldBe("testString.replace(/(.)(.*)/g, function replaceWithMultipleDollars(matchGroup) { return '$1$2'; })", "\"$1$2\"");
 shouldBe("testString.replace(/(.)(.*)/, function checkReplacementArguments() { return arguments.length; })", "\"5\"");
+
+// replace with a global regexp should set lastIndex to zero; if read-only this should throw.
+// If the regexp is not global, lastIndex is not modified.
+var re;
+var replacer;
+function testReplace(_re, readonly)
+{
+    re = _re;
+    re.lastIndex = 3;
+    if (readonly)
+        re = Object.defineProperty(re, 'lastIndex', {writable:false});
+    return '0x1x2'.replace(re, replacer);
+}
+
+replacer = 'y';
+shouldBe("testReplace(/x/g, false)", '"0y1y2"');
+shouldThrow("testReplace(/x/g, true)");
+shouldBe("testReplace(/x/, false)", '"0y1x2"');
+shouldBe("testReplace(/x/, true)", '"0y1x2"');
+shouldBe("testReplace(/x/g, false); re.lastIndex", '0');
+shouldThrow("testReplace(/x/g, true); re.lastIndex");
+shouldBe("testReplace(/x/, false); re.lastIndex", '3');
+shouldBe("testReplace(/x/, true); re.lastIndex", '3');
+
+replacer = function() { return 'y'; };
+shouldBe("testReplace(/x/g, false)", '"0y1y2"');
+shouldThrow("testReplace(/x/g, true)");
+shouldBe("testReplace(/x/, false)", '"0y1x2"');
+shouldBe("testReplace(/x/, true)", '"0y1x2"');
+shouldBe("testReplace(/x/g, false); re.lastIndex", '0');
+shouldThrow("testReplace(/x/g, true); re.lastIndex");
+shouldBe("testReplace(/x/, false); re.lastIndex", '3');
+shouldBe("testReplace(/x/, true); re.lastIndex", '3');
+
+replacer = function() { "use strict"; return ++re.lastIndex; };
+shouldBe("testReplace(/x/g, false)", '"01122"');
+shouldThrow("testReplace(/x/g, true)");
+shouldBe("testReplace(/x/, false)", '"041x2"');
+shouldThrow("testReplace(/x/, true)");
+shouldBe("testReplace(/x/g, false); re.lastIndex", '2');
+shouldThrow("testReplace(/x/g, true); re.lastIndex");
+shouldBe("testReplace(/x/, false); re.lastIndex", '4');
+shouldThrow("testReplace(/x/, true); re.lastIndex");
+
+var replacerCalled = false;
+replacer = function() { replacerCalled = true; };
+shouldBeTrue("try { testReplace(/x/g, false); throw 0; } catch (e) { }; replacerCalled;");
+var replacerCalled = false;
+replacer = function() { replacerCalled = true; };
+shouldBeFalse("try { testReplace(/x/g, true); throw 0; } catch (e) { }; replacerCalled;");

Added: trunk/LayoutTests/fast/js/string-match-expected.txt (0 => 110233)


--- trunk/LayoutTests/fast/js/string-match-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/js/string-match-expected.txt	2012-03-09 00:49:31 UTC (rev 110233)
@@ -0,0 +1,17 @@
+String.match(…) test
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS testMatch(/x/g, false) is ["x","x"]
+PASS testMatch(/x/g, true) threw exception TypeError: Attempted to assign to readonly property..
+PASS testMatch(/x/, false) is ["x"]
+PASS testMatch(/x/, true) is ["x"]
+PASS testMatch(/x/g, false); re.lastIndex is 0
+PASS testMatch(/x/g, true); re.lastIndex threw exception TypeError: Attempted to assign to readonly property..
+PASS testMatch(/x/, false); re.lastIndex is 3
+PASS testMatch(/x/, true); re.lastIndex is 3
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/js/string-match.html (0 => 110233)


--- trunk/LayoutTests/fast/js/string-match.html	                        (rev 0)
+++ trunk/LayoutTests/fast/js/string-match.html	2012-03-09 00:49:31 UTC (rev 110233)
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+</body>
+</html>

Modified: trunk/LayoutTests/fast/js/string-replace-2-expected.txt (110232 => 110233)


--- trunk/LayoutTests/fast/js/string-replace-2-expected.txt	2012-03-09 00:46:38 UTC (rev 110232)
+++ trunk/LayoutTests/fast/js/string-replace-2-expected.txt	2012-03-09 00:49:31 UTC (rev 110233)
@@ -12,6 +12,32 @@
 PASS testString.replace(/(.*)/g, function replaceWithDollars(matchGroup) { return '$1'; }) is "$1$1"
 PASS testString.replace(/(.)(.*)/g, function replaceWithMultipleDollars(matchGroup) { return '$1$2'; }) is "$1$2"
 PASS testString.replace(/(.)(.*)/, function checkReplacementArguments() { return arguments.length; }) is "5"
+PASS testReplace(/x/g, false) is "0y1y2"
+PASS testReplace(/x/g, true) threw exception TypeError: Attempted to assign to readonly property..
+PASS testReplace(/x/, false) is "0y1x2"
+PASS testReplace(/x/, true) is "0y1x2"
+PASS testReplace(/x/g, false); re.lastIndex is 0
+PASS testReplace(/x/g, true); re.lastIndex threw exception TypeError: Attempted to assign to readonly property..
+PASS testReplace(/x/, false); re.lastIndex is 3
+PASS testReplace(/x/, true); re.lastIndex is 3
+PASS testReplace(/x/g, false) is "0y1y2"
+PASS testReplace(/x/g, true) threw exception TypeError: Attempted to assign to readonly property..
+PASS testReplace(/x/, false) is "0y1x2"
+PASS testReplace(/x/, true) is "0y1x2"
+PASS testReplace(/x/g, false); re.lastIndex is 0
+PASS testReplace(/x/g, true); re.lastIndex threw exception TypeError: Attempted to assign to readonly property..
+PASS testReplace(/x/, false); re.lastIndex is 3
+PASS testReplace(/x/, true); re.lastIndex is 3
+PASS testReplace(/x/g, false) is "01122"
+PASS testReplace(/x/g, true) threw exception TypeError: Attempted to assign to readonly property..
+PASS testReplace(/x/, false) is "041x2"
+PASS testReplace(/x/, true) threw exception TypeError: Attempted to assign to readonly property..
+PASS testReplace(/x/g, false); re.lastIndex is 2
+PASS testReplace(/x/g, true); re.lastIndex threw exception TypeError: Attempted to assign to readonly property..
+PASS testReplace(/x/, false); re.lastIndex is 4
+PASS testReplace(/x/, true); re.lastIndex threw exception TypeError: Attempted to assign to readonly property..
+PASS try { testReplace(/x/g, false); throw 0; } catch (e) { }; replacerCalled; is true
+PASS try { testReplace(/x/g, true); throw 0; } catch (e) { }; replacerCalled; is false
 PASS successfullyParsed is true
 
 TEST COMPLETE

Modified: trunk/Source/_javascript_Core/ChangeLog (110232 => 110233)


--- trunk/Source/_javascript_Core/ChangeLog	2012-03-09 00:46:38 UTC (rev 110232)
+++ trunk/Source/_javascript_Core/ChangeLog	2012-03-09 00:49:31 UTC (rev 110233)
@@ -1,3 +1,17 @@
+2012-03-08  Gavin Barraclough  <[email protected]>
+
+        String.prototype.match and replace do not clear global regexp lastIndex per ES5.1 15.5.4.10
+        https://bugs.webkit.org/show_bug.cgi?id=26890
+
+        Reviewed by Oliver Hunt.
+
+        Per 15.10.6.2 step 9.a.1 called via the action of the last iteration of 15.5.4.10 8.f.i.
+
+        * runtime/StringPrototype.cpp:
+        (JSC::replaceUsingRegExpSearch):
+        (JSC::stringProtoFuncMatch):
+            - added calls to setLastIndex.
+
 2012-03-07  Jon Lee  <[email protected]>
 
         Add support for ENABLE(LEGACY_NOTIFICATIONS)

Modified: trunk/Source/_javascript_Core/runtime/StringPrototype.cpp (110232 => 110233)


--- trunk/Source/_javascript_Core/runtime/StringPrototype.cpp	2012-03-09 00:46:38 UTC (rev 110232)
+++ trunk/Source/_javascript_Core/runtime/StringPrototype.cpp	2012-03-09 00:49:31 UTC (rev 110233)
@@ -455,12 +455,20 @@
     unsigned sourceLen = source.length();
     if (exec->hadException())
         return JSValue::encode(JSValue());
-    RegExp* regExp = asRegExpObject(searchValue)->regExp();
+    RegExpObject* regExpObject = asRegExpObject(searchValue);
+    RegExp* regExp = regExpObject->regExp();
     bool global = regExp->global();
 
-    if (global && callType == CallTypeNone && !replacementString.length())
-        return removeUsingRegExpSearch(exec, string, source, regExp);
+    if (global) {
+        // ES5.1 15.5.4.10 step 8.a.
+        regExpObject->setLastIndex(exec, 0);
+        if (exec->hadException())
+            return JSValue::encode(JSValue());
 
+        if (callType == CallTypeNone && !replacementString.length())
+            return removeUsingRegExpSearch(exec, string, source, regExp);
+    }
+
     RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
 
     int lastIndex = 0;
@@ -808,9 +816,17 @@
     JSValue a0 = exec->argument(0);
 
     RegExp* reg;
-    if (a0.inherits(&RegExpObject::s_info))
-        reg = asRegExpObject(a0)->regExp();
-    else {
+    bool global = false;
+    if (a0.inherits(&RegExpObject::s_info)) {
+        RegExpObject* regExpObject = asRegExpObject(a0);
+        reg = regExpObject->regExp();
+        if ((global = reg->global())) {
+            // ES5.1 15.5.4.10 step 8.a.
+            regExpObject->setLastIndex(exec, 0);
+            if (exec->hadException())
+                return JSValue::encode(JSValue());
+        }
+    } else {
         /*
          *  ECMA 15.5.4.12 String.prototype.search (regexp)
          *  If regexp is not an object whose [[Class]] property is "RegExp", it is
@@ -825,7 +841,7 @@
     int pos;
     int matchLength = 0;
     regExpConstructor->performMatch(*globalData, reg, s, 0, pos, matchLength);
-    if (!(reg->global())) {
+    if (!global) {
         // case without 'g' flag is handled like RegExp.prototype.exec
         if (pos < 0)
             return JSValue::encode(jsNull());
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to