Home | History | Annotate | Download | only in mjsunit
      1 // Copyright 2012 the V8 project authors. All rights reserved.
      2 // Redistribution and use in source and binary forms, with or without
      3 // modification, are permitted provided that the following conditions are
      4 // met:
      5 //
      6 //     * Redistributions of source code must retain the above copyright
      7 //       notice, this list of conditions and the following disclaimer.
      8 //     * Redistributions in binary form must reproduce the above
      9 //       copyright notice, this list of conditions and the following
     10 //       disclaimer in the documentation and/or other materials provided
     11 //       with the distribution.
     12 //     * Neither the name of Google Inc. nor the names of its
     13 //       contributors may be used to endorse or promote products derived
     14 //       from this software without specific prior written permission.
     15 //
     16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27 
     28 
     29 // Test that an optional capture is cleared between two matches.
     30 var str = "ABX X";
     31 str = str.replace(/(\w)?X/g, function(match, capture) {
     32                                assertTrue(match.indexOf(capture) >= 0 ||
     33                                            capture === undefined);
     34                                return capture ? capture.toLowerCase() : "-";
     35                              });
     36 assertEquals("Ab -", str);
     37 
     38 // Test zero-length matches.
     39 str = "Als Gregor Samsa eines Morgens";
     40 str = str.replace(/\b/g, function(match, capture) {
     41                            return "/";
     42                          });
     43 assertEquals("/Als/ /Gregor/ /Samsa/ /eines/ /Morgens/", str);
     44 
     45 // Test zero-length matches that have non-zero-length sub-captures.
     46 str = "It was a pleasure to burn.";
     47 str = str.replace(/(?=(\w+))\b/g, function(match, capture) {
     48                                     return capture.length;
     49                                   });
     50 assertEquals("2It 3was 1a 8pleasure 2to 4burn.", str);
     51 
     52 // Test multiple captures.
     53 str = "Try not. Do, or do not. There is no try.";
     54 str = str.replace(/(not?)|(do)|(try)/gi,
     55                   function(match, c1, c2, c3) {
     56                     assertTrue((c1 === undefined && c2 === undefined) ||
     57                                (c2 === undefined && c3 === undefined) ||
     58                                (c1 === undefined && c3 === undefined));
     59                     if (c1) return "-";
     60                     if (c2) return "+";
     61                     if (c3) return "="
     62                   });
     63 assertEquals("= -. +, or + -. There is - =.", str);
     64 
     65 // Test multiple alternate captures.
     66 str = "FOUR LEGS GOOD, TWO LEGS BAD!";
     67 str = str.replace(/(FOUR|TWO) LEGS (GOOD|BAD)/g,
     68                   function(match, num_legs, likeability) {
     69                     assertTrue(num_legs !== undefined);
     70                     assertTrue(likeability !== undefined);
     71                     if (num_legs == "FOUR") assertTrue(likeability == "GOOD");
     72                     if (num_legs == "TWO") assertTrue(likeability == "BAD");
     73                     return match.length - 10;
     74                   });
     75 assertEquals("4, 2!", str);
     76 
     77 
     78 // The same tests with UC16.
     79 
     80 //Test that an optional capture is cleared between two matches.
     81 str = "AB\u1234 \u1234";
     82 str = str.replace(/(\w)?\u1234/g,
     83                   function(match, capture) {
     84                     assertTrue(match.indexOf(capture) >= 0 ||
     85                                capture === undefined);
     86                     return capture ? capture.toLowerCase() : "-";
     87                   });
     88 assertEquals("Ab -", str);
     89 
     90 // Test zero-length matches.
     91 str = "Als \u2623\u2642 eines Morgens";
     92 str = str.replace(/\b/g, function(match, capture) {
     93                            return "/";
     94                          });
     95 assertEquals("/Als/ \u2623\u2642 /eines/ /Morgens/", str);
     96 
     97 // Test zero-length matches that have non-zero-length sub-captures.
     98 str = "It was a pleasure to \u70e7.";
     99 str = str.replace(/(?=(\w+))\b/g, function(match, capture) {
    100                                     return capture.length;
    101                                   });
    102 assertEquals("2It 3was 1a 8pleasure 2to \u70e7.", str);
    103 
    104 // Test multiple captures.
    105 str = "Try not. D\u26aa, or d\u26aa not. There is no try.";
    106 str = str.replace(/(not?)|(d\u26aa)|(try)/gi,
    107                   function(match, c1, c2, c3) {
    108                     assertTrue((c1 === undefined && c2 === undefined) ||
    109                                (c2 === undefined && c3 === undefined) ||
    110                                (c1 === undefined && c3 === undefined));
    111                     if (c1) return "-";
    112                     if (c2) return "+";
    113                     if (c3) return "="
    114                   });
    115 assertEquals("= -. +, or + -. There is - =.", str);
    116 
    117 // Test multiple alternate captures.
    118 str = "FOUR \u817f GOOD, TWO \u817f BAD!";
    119 str = str.replace(/(FOUR|TWO) \u817f (GOOD|BAD)/g,
    120                   function(match, num_legs, likeability) {
    121                     assertTrue(num_legs !== undefined);
    122                     assertTrue(likeability !== undefined);
    123                     if (num_legs == "FOUR") assertTrue(likeability == "GOOD");
    124                     if (num_legs == "TWO") assertTrue(likeability == "BAD");
    125                     return match.length - 7;
    126                   });
    127 assertEquals("4, 2!", str);
    128 
    129 // Test capture that is a real substring.
    130 var str = "Beasts of England, beasts of Ireland";
    131 str = str.replace(/(.*)/g, function(match) { return '~'; });
    132 assertEquals("~~", str);
    133 
    134 // Test zero-length matches that have non-zero-length sub-captures that do not
    135 // start at the match start position.
    136 str = "up up up up";
    137 str = str.replace(/\b(?=u(p))/g, function(match, capture) {
    138                                     return capture.length;
    139                                   });
    140 
    141 assertEquals("1up 1up 1up 1up", str);
    142 
    143 
    144 // Create regexp that has a *lot* of captures.
    145 var re_string = "(a)";
    146 for (var i = 0; i < 500; i++) {
    147   re_string = "(" + re_string + ")";
    148 }
    149 re_string = re_string + "1";
    150 // re_string = "(((...((a))...)))1"
    151 
    152 var regexps = new Array();
    153 var last_match_expectations = new Array();
    154 var first_capture_expectations = new Array();
    155 
    156 // Atomic regexp.
    157 regexps.push(/a1/g);
    158 last_match_expectations.push("a1");
    159 first_capture_expectations.push("");
    160 // Small regexp (no capture);
    161 regexps.push(/\w1/g);
    162 last_match_expectations.push("a1");
    163 first_capture_expectations.push("");
    164 // Small regexp (one capture).
    165 regexps.push(/(a)1/g);
    166 last_match_expectations.push("a1");
    167 first_capture_expectations.push("a");
    168 // Large regexp (a lot of captures).
    169 regexps.push(new RegExp(re_string, "g"));
    170 last_match_expectations.push("a1");
    171 first_capture_expectations.push("a");
    172 
    173 function test_replace(result_expectation,
    174                       subject,
    175                       regexp,
    176                       replacement) {
    177   for (var i = 0; i < regexps.length; i++) {
    178     // Overwrite last match info.
    179     "deadbeef".replace(/(dead)beef/, "$1holeycow");
    180     // Conduct tests.
    181     assertEquals(result_expectation, subject.replace(regexps[i], replacement));
    182     if (subject.length == 0) {
    183       assertEquals("deadbeef", RegExp.lastMatch);
    184       assertEquals("dead", RegExp["$1"]);
    185     } else {
    186       assertEquals(last_match_expectations[i], RegExp.lastMatch);
    187       assertEquals(first_capture_expectations[i], RegExp["$1"]);
    188     }
    189   }
    190 }
    191 
    192 
    193 function test_match(result_expectation,
    194                     subject,
    195                     regexp) {
    196   for (var i = 0; i < regexps.length; i++) {
    197     // Overwrite last match info.
    198     "deadbeef".replace(/(dead)beef/, "$1holeycow");
    199     // Conduct tests.
    200     if (result_expectation == null) {
    201       assertNull(subject.match(regexps[i]));
    202     } else {
    203       assertArrayEquals(result_expectation, subject.match(regexps[i]));
    204     }
    205     if (subject.length == 0) {
    206       assertEquals("deadbeef", RegExp.lastMatch);
    207       assertEquals("dead", RegExp["$1"]);
    208     } else {
    209       assertEquals(last_match_expectations[i], RegExp.lastMatch);
    210       assertEquals(first_capture_expectations[i], RegExp["$1"]);
    211     }
    212   }
    213 }
    214 
    215 
    216 // Test for different number of matches.
    217 for (var m = 0; m < 33; m++) {
    218   // Create string that matches m times.
    219   var subject = "";
    220   var test_1_expectation = "";
    221   var test_2_expectation = "";
    222   var test_3_expectation = (m == 0) ? null : new Array();
    223   for (var i = 0; i < m; i++) {
    224     subject += "a11";
    225     test_1_expectation += "x1";
    226     test_2_expectation += "1";
    227     test_3_expectation.push("a1");
    228   }
    229 
    230   // Test 1a: String.replace with string.
    231   test_replace(test_1_expectation, subject, /a1/g, "x");
    232 
    233   // Test 1b: String.replace with function.
    234   function f() { return "x"; }
    235   test_replace(test_1_expectation, subject, /a1/g, f);
    236 
    237   // Test 2a: String.replace with empty string.
    238   test_replace(test_2_expectation, subject, /a1/g, "");
    239 
    240   // Test 3a: String.match.
    241   test_match(test_3_expectation, subject, /a1/g);
    242 }
    243 
    244 
    245 // Test String hashing (compiling regular expression includes hashing).
    246 var crosscheck = "\x80";
    247 for (var i = 0; i < 12; i++) crosscheck += crosscheck;
    248 new RegExp(crosscheck);
    249 
    250 var subject = "ascii~only~string~here~";
    251 var replacement = "\x80";
    252 var result = subject.replace(/~/g, replacement);
    253 for (var i = 0; i < 5; i++) result += result;
    254 new RegExp(result);
    255