1// Copyright 2009 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 * @fileoverview Test String.prototype.replace 30 */ 31 32function replaceTest(result, subject, pattern, replacement) { 33 var name = 34 "\"" + subject + "\".replace(" + pattern + ", " + replacement + ")"; 35 assertEquals(result, subject.replace(pattern, replacement), name); 36} 37 38 39var short = "xaxbxcx"; 40 41replaceTest("axbxcx", short, "x", ""); 42replaceTest("axbxcx", short, /x/, ""); 43replaceTest("abc", short, /x/g, ""); 44 45replaceTest("xaxxcx", short, "b", ""); 46replaceTest("xaxxcx", short, /b/, ""); 47replaceTest("xaxxcx", short, /b/g, ""); 48 49 50replaceTest("[]axbxcx", short, "x", "[]"); 51replaceTest("[]axbxcx", short, /x/, "[]"); 52replaceTest("[]a[]b[]c[]", short, /x/g, "[]"); 53 54replaceTest("xax[]xcx", short, "b", "[]"); 55replaceTest("xax[]xcx", short, /b/, "[]"); 56replaceTest("xax[]xcx", short, /b/g, "[]"); 57 58 59replaceTest("[$]axbxcx", short, "x", "[$$]"); 60replaceTest("[$]axbxcx", short, /x/, "[$$]"); 61replaceTest("[$]a[$]b[$]c[$]", short, /x/g, "[$$]"); 62 63replaceTest("xax[$]xcx", short, "b", "[$$]"); 64replaceTest("xax[$]xcx", short, /b/, "[$$]"); 65replaceTest("xax[$]xcx", short, /b/g, "[$$]"); 66 67 68replaceTest("[]axbxcx", short, "x", "[$`]"); 69replaceTest("[]axbxcx", short, /x/, "[$`]"); 70replaceTest("[]a[xa]b[xaxb]c[xaxbxc]", short, /x/g, "[$`]"); 71 72replaceTest("xax[xax]xcx", short, "b", "[$`]"); 73replaceTest("xax[xax]xcx", short, /b/, "[$`]"); 74replaceTest("xax[xax]xcx", short, /b/g, "[$`]"); 75 76 77replaceTest("[x]axbxcx", short, "x", "[$&]"); 78replaceTest("[x]axbxcx", short, /x/, "[$&]"); 79replaceTest("[x]a[x]b[x]c[x]", short, /x/g, "[$&]"); 80 81replaceTest("xax[b]xcx", short, "b", "[$&]"); 82replaceTest("xax[b]xcx", short, /b/, "[$&]"); 83replaceTest("xax[b]xcx", short, /b/g, "[$&]"); 84 85 86replaceTest("[axbxcx]axbxcx", short, "x", "[$']"); 87replaceTest("[axbxcx]axbxcx", short, /x/, "[$']"); 88replaceTest("[axbxcx]a[bxcx]b[cx]c[]", short, /x/g, "[$']"); 89 90replaceTest("xax[xcx]xcx", short, "b", "[$']"); 91replaceTest("xax[xcx]xcx", short, /b/, "[$']"); 92replaceTest("xax[xcx]xcx", short, /b/g, "[$']"); 93 94 95replaceTest("[$1]axbxcx", short, "x", "[$1]"); 96replaceTest("[$1]axbxcx", short, /x/, "[$1]"); 97replaceTest("[]axbxcx", short, /x()/, "[$1]"); 98replaceTest("[$1]a[$1]b[$1]c[$1]", short, /x/g, "[$1]"); 99replaceTest("[]a[]b[]c[]", short, /x()/g, "[$1]"); 100 101replaceTest("xax[$1]xcx", short, "b", "[$1]"); 102replaceTest("xax[$1]xcx", short, /b/, "[$1]"); 103replaceTest("xax[]xcx", short, /b()/, "[$1]"); 104replaceTest("xax[$1]xcx", short, /b/g, "[$1]"); 105replaceTest("xax[]xcx", short, /b()/g, "[$1]"); 106 107// Bug 317 look-alikes. If "$e" has no meaning, the "$" must be retained. 108replaceTest("xax$excx", short, "b", "$e"); 109replaceTest("xax$excx", short, /b/, "$e"); 110replaceTest("xax$excx", short, /b/g, "$e"); 111 112replaceTest("xaxe$xcx", short, "b", "e$"); 113replaceTest("xaxe$xcx", short, /b/, "e$"); 114replaceTest("xaxe$xcx", short, /b/g, "e$"); 115 116 117replaceTest("[$$$1$$a1abb1bb0$002$3$03][$$$1$$b1bcc1cc0$002$3$03]c", 118 "abc", /(.)(?=(.))/g, "[$$$$$$1$$$$$11$01$2$21$02$020$002$3$03]"); 119 120// Replace with functions. 121 122 123var ctr = 0; 124replaceTest("0axbxcx", short, "x", function r(m, i, s) { 125 assertEquals(3, arguments.length, "replace('x',func) func-args"); 126 assertEquals("x", m, "replace('x',func(m,..))"); 127 assertEquals(0, i, "replace('x',func(..,i,..))"); 128 assertEquals(short, s, "replace('x',func(..,s))"); 129 return String(ctr++); 130}); 131assertEquals(1, ctr, "replace('x',func) num-match"); 132 133ctr = 0; 134replaceTest("0axbxcx", short, /x/, function r(m, i, s) { 135 assertEquals(3, arguments.length, "replace(/x/,func) func-args"); 136 assertEquals("x", m, "replace(/x/,func(m,..))"); 137 assertEquals(0, i, "replace(/x/,func(..,i,..))"); 138 assertEquals(short, s, "replace(/x/,func(..,s))"); 139 return String(ctr++); 140}); 141assertEquals(1, ctr, "replace(/x/,func) num-match"); 142 143ctr = 0; 144replaceTest("0a1b2c3", short, /x/g, function r(m, i, s) { 145 assertEquals(3, arguments.length, "replace(/x/g,func) func-args"); 146 assertEquals("x", m, "replace(/x/g,func(m,..))"); 147 assertEquals(ctr * 2, i, "replace(/x/g,func(..,i,.))"); 148 assertEquals(short, s, "replace(/x/g,func(..,s))"); 149 return String(ctr++); 150}); 151assertEquals(4, ctr, "replace(/x/g,func) num-match"); 152 153ctr = 0; 154replaceTest("0a1b2cx", short, /(x)(?=(.))/g, function r(m, c1, c2, i, s) { 155 assertEquals(5, arguments.length, "replace(/(x)(?=(.))/g,func) func-args"); 156 assertEquals("x", m, "replace(/(x)(?=(.))/g,func(m,..))"); 157 assertEquals("x", c1, "replace(/(x)(?=(.))/g,func(..,c1,..))"); 158 assertEquals(["a","b","c"][ctr], c2, "replace(/(x)(?=(.))/g,func(..,c2,..))"); 159 assertEquals(ctr * 2, i, "replace(/(x)(?=(.))/g,func(..,i,..))"); 160 assertEquals(short, s, "replace(/(x)(?=(.))/g,func(..,s))"); 161 return String(ctr++); 162}); 163assertEquals(3, ctr, "replace(/x/g,func) num-match"); 164 165 166replaceTest("ABCD", "abcd", /(.)/g, function r(m, c1, i, s) { 167 assertEquals("d", RegExp.lastMatch); 168 assertEquals("d", RegExp.$1); 169 assertEquals("abc", RegExp.leftContext); 170 return m.toUpperCase(); 171}); 172 173 174var long = ""; 175while (long.length < 0x2000) { 176 long += String.fromCharCode(65 + Math.random() * 26); 177} 178 179for (var i = 0; i < 3; i++) { 180 replaceTest(long.toLowerCase(), long, /(..)/g, function r(m, c1, i, s) { 181 var expected = long.substring(0x1ffe, 0x2000); 182 assertEquals(expected, RegExp.lastMatch); 183 assertEquals(expected, RegExp.$1); 184 assertEquals(long.substring(0, 0x1ffe), RegExp.leftContext); 185 return m.toLowerCase(); 186 }); 187} 188 189 190// Test special cases of replacement parts longer than 1<<11. 191var longstring = "xyzzy"; 192longstring = longstring + longstring; 193longstring = longstring + longstring; 194longstring = longstring + longstring; 195longstring = longstring + longstring; 196longstring = longstring + longstring; 197longstring = longstring + longstring; 198longstring = longstring + longstring; 199longstring = longstring + longstring; 200longstring = longstring + longstring; 201longstring = longstring + longstring; 202longstring = longstring + longstring; 203// longstring.length == 5 << 11 204 205replaceTest(longstring + longstring, 206 "<" + longstring + ">", /<(.*)>/g, "$1$1"); 207 208replaceTest("string 42", "string x", /x/g, function() { return 42; }); 209replaceTest("string 42", "string x", /x/, function() { return 42; }); 210replaceTest("string 42", "string x", /[xy]/g, function() { return 42; }); 211replaceTest("string 42", "string x", /[xy]/, function() { return 42; }); 212replaceTest("string true", "string x", /x/g, function() { return true; }); 213replaceTest("string null", "string x", /x/g, function() { return null; }); 214replaceTest("string undefined", "string x", /x/g, function() { return undefined; }); 215 216replaceTest("aundefinedbundefinedcundefined", 217 "abc", /(.)|(.)/g, function(m, m1, m2, i, s) { return m1+m2; }); 218 219// Test nested calls to replace, including that it sets RegExp.$& correctly. 220 221var str = 'She sells seashells by the seashore.'; 222var re = /sh/g; 223assertEquals('She sells sea$schells by the sea$schore.', 224 str.replace(re,"$$" + 'sch')) 225 226 227var replace_obj = { length: 0, toString: function() { return "x"; }}; 228assertEquals("axc", "abc".replace(/b/, replace_obj)); 229assertEquals("axc", "abc".replace(/b/g, replace_obj)); 230 231var search_obj = { length: 1, toString: function() { return "b"; }}; 232assertEquals("axc", "abc".replace(search_obj, function() { return "x"; })); 233 234var side_effect_flag = false; 235var replace_obj_side_effects = { 236 toString: function() { side_effect_flag = true; return "x" } 237} 238assertEquals("abc", "abc".replace(/z/g, replace_obj_side_effects)); 239assertTrue(side_effect_flag); // Side effect triggers even without a match. 240 241var regexp99pattern = ""; 242var subject = ""; 243for (var i = 0; i < 99; i++) { 244 regexp99pattern += "(.)"; 245 subject += String.fromCharCode(i + 24); 246} 247 248function testIndices99(re) { 249 // Test $1 .. $99 250 for (var i = 1; i < 100; i++) { 251 assertEquals(String.fromCharCode(i + 23), 252 subject.replace(re, "$" + i)); 253 } 254 255 // Test $01 .. $09 256 for (var i = 1; i < 10; i++) { 257 assertEquals(String.fromCharCode(i + 23), 258 subject.replace(re, "$0" + i)); 259 } 260 261 assertEquals("$0", subject.replace(re, "$0")); 262 assertEquals("$00", subject.replace(re, "$00")); 263 assertEquals(String.fromCharCode(10 + 23) + "0", 264 subject.replace(re, "$100")); 265} 266 267testIndices99(new RegExp(regexp99pattern)); 268testIndices99(new RegExp(regexp99pattern, "g")); 269 270var regexp59pattern = ""; 271for (var i = 0; i < 59; i++) regexp59pattern += "(.)"; 272 273function testIndices59(re) { 274 // Test $60 .. $99. Captures reach up to 59. Per spec, how to deal 275 // with this is implementation-dependent. We interpret $60 as $6 276 // followed by "0", $61 as $6, followed by "1" and so on. 277 var tail = subject.substr(59); 278 for (var i = 60; i < 100; i++) { 279 assertEquals(String.fromCharCode(i / 10 + 23) + (i % 10) + tail, 280 subject.replace(re, "$" + i)); 281 } 282} 283 284testIndices59(new RegExp(regexp59pattern)); 285testIndices59(new RegExp(regexp59pattern, "g")); 286