1// Copyright JS Foundation and other contributors, http://js.foundation 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15var replace = RegExp.prototype[Symbol.replace]; 16 17try { 18 replace.call (0, "string", "replace"); 19 assert (false); 20} catch (e) { 21 assert (e instanceof TypeError); 22} 23 24try { 25 replace.call (new RegExp(), { 26 toString: () => { 27 throw "abrupt string" 28 } 29 }, "replace"); 30 assert (false); 31} catch (e) { 32 assert (e === "abrupt string"); 33} 34 35try { 36 replace.call (new RegExp(), "string", { 37 toString: () => { 38 throw "abrupt replace" 39 } 40 }); 41 assert (false); 42} catch (e) { 43 assert (e === "abrupt replace"); 44} 45 46try { 47 replace.call ({ 48 get global() { 49 throw "abrupt global" 50 } 51 }, "string", "replace"); 52 assert (false); 53} catch (e) { 54 assert (e === "abrupt global"); 55} 56 57try { 58 replace.call ({ 59 global: true, 60 set lastIndex(idx) { 61 throw "abrupt lastIndex" 62 } 63 }, "string", "replace"); 64 assert (false); 65} catch (e) { 66 assert (e === "abrupt lastIndex"); 67} 68 69try { 70 replace.call ({ 71 get exec() { 72 throw "abrupt exec" 73 } 74 }, "string", "replace"); 75 assert (false); 76} catch (e) { 77 assert (e === "abrupt exec"); 78} 79 80try { 81 replace.call ({ 82 exec: RegExp.prototype.exec 83 }, "string", "replace"); 84 assert (false); 85} catch (e) { 86 assert (e instanceof TypeError); 87} 88 89try { 90 replace.call ({ 91 exec: 42 92 }, "string", "replace"); 93 assert (false); 94} catch (e) { 95 assert (e instanceof TypeError); 96} 97 98try { 99 replace.call ({ 100 exec: () => { 101 throw "abrupt exec result" 102 } 103 }, "string", "replace"); 104 assert (false); 105} catch (e) { 106 assert (e === "abrupt exec result"); 107} 108 109try { 110 replace.call ({ 111 exec: () => { 112 return 1 113 } 114 }, "string", "replace"); 115 assert (false); 116} catch (e) { 117 assert (e instanceof TypeError); 118} 119 120try { 121 replace.call ({ 122 exec: () => { 123 return { 124 get length() { 125 throw "abrupt result length" 126 } 127 } 128 } 129 }, "string", "replace"); 130 assert (false); 131} catch (e) { 132 assert (e === "abrupt result length"); 133} 134 135try { 136 replace.call ({ 137 global: true, 138 exec: () => { 139 return { 140 length: 1, 141 get 0() { 142 throw "abrupt match" 143 } 144 } 145 } 146 }, 147 "string", 148 "replace"); 149 assert (false); 150} catch (e) { 151 assert (e === "abrupt match"); 152} 153 154try { 155 replace.call ({ 156 global: true, 157 exec: () => { 158 return { 159 length: 1, 160 get 0() { 161 return { 162 toString: () => { 163 throw "abrupt match toString" 164 } 165 } 166 } 167 } 168 } 169 }, 170 "string", 171 "replace"); 172 assert (false); 173} catch (e) { 174 assert (e === "abrupt match toString"); 175} 176 177var result_obj = { 178 toString: () => { 179 Object.defineProperty (result_obj, 'toString', { 180 value: () => { 181 throw "abrupt match toString delayed"; 182 } 183 }); 184 return "str"; 185 } 186} 187 188var first = true; 189try { 190 replace.call ({ 191 global: true, 192 exec: () => { 193 if (!first) { 194 return null; 195 } 196 197 first = false; 198 return { 199 length: 1, 200 get 0() { 201 return result_obj; 202 } 203 } 204 } 205 }, 206 "string", 207 "replace"); 208 assert (false); 209} catch (e) { 210 assert (e === "abrupt match toString delayed"); 211} 212 213try { 214 replace.call ({ 215 global: true, 216 get lastIndex() { 217 throw "abrupt lastIndex get" 218 }, 219 set lastIndex(i) {}, 220 exec: () => { 221 return { 222 length: 1, 223 get 0() { 224 return { 225 toString: () => { 226 return "" 227 } 228 } 229 } 230 } 231 } 232 }, 233 "string", 234 "replace"); 235 assert (false); 236} catch (e) { 237 assert (e === "abrupt lastIndex get"); 238} 239 240try { 241 replace.call ({ 242 global: true, 243 get lastIndex() { 244 return { 245 valueOf: () => { 246 throw "abrupt lastIndex toNumber" 247 } 248 } 249 }, 250 set lastIndex(i) {}, 251 exec: () => { 252 return { 253 length: 1, 254 get 0() { 255 return { 256 toString: () => { 257 return "" 258 } 259 } 260 } 261 } 262 } 263 }, 264 "string", 265 "replace"); 266 assert (false); 267} catch (e) { 268 assert (e === "abrupt lastIndex toNumber"); 269} 270 271var o = { 272 global: true, 273 exec: () => { 274 return { 275 length: 1, 276 get 0() { 277 return { 278 toString: () => { 279 return "" 280 } 281 } 282 } 283 } 284 } 285} 286Object.defineProperty (o, 'lastIndex', { 287 configurable: true, 288 get: () => { 289 Object.defineProperty (o, 'lastIndex', { 290 get: () => { 291 return { 292 valueOf: () => { 293 return 42 294 } 295 }; 296 }, 297 set: (i) => { 298 throw "abrupt lastIndex put"; 299 }, 300 configurable: true 301 }); 302 return { 303 valueOf: () => { 304 return 24 305 } 306 }; 307 }, 308 set: (i) => {} 309}); 310 311try { 312 replace.call (o, 313 "string", 314 "replace"); 315 assert (false); 316} catch (e) { 317 assert (e === "abrupt lastIndex put"); 318} 319 320o = { 321 global: true, 322 exec: () => { 323 return { 324 length: 1, 325 get 0() { 326 return { 327 toString: () => { 328 return "" 329 } 330 } 331 } 332 } 333 }, 334}; 335Object.defineProperty (o, 'lastIndex', { 336 get: () => { 337 Object.defineProperty (o, 'lastIndex', { 338 value: 0, 339 writable: false 340 }); 341 return 0; 342 }, 343 set: () => {} 344}); 345 346try { 347 replace.call (o, 348 "string", 349 "replace"); 350 assert (false); 351} catch (e) { 352 assert (e instanceof TypeError); 353} 354 355o = { 356 global: true 357}; 358Object.defineProperty (o, 'exec', { 359 configurable: true, 360 value: () => { 361 Object.defineProperty (o, 'exec', { 362 get: () => { 363 throw "abrupt exec" 364 }, 365 set: (v) => {} 366 }); 367 return { 368 length: 1, 369 0: "thisisastring" 370 } 371 } 372}); 373 374try { 375 replace.call (o, 376 "string", 377 "replace"); 378 assert (false); 379} catch (e) { 380 assert (e === "abrupt exec"); 381} 382 383try { 384 replace.call ({ 385 exec: () => { 386 return { 387 length: 1, 388 0: "str", 389 get index() { 390 throw "abrupt index" 391 } 392 } 393 } 394 }, "string", "replace"); 395 assert (false); 396} catch (e) { 397 assert (e === "abrupt index"); 398} 399 400try { 401 replace.call ({ 402 exec: () => { 403 return { 404 length: 1, 405 0: "str", 406 get index() { 407 return { 408 valueOf: () => { 409 throw "abrupt index toNumber" 410 } 411 } 412 } 413 } 414 } 415 }, "string", "replace"); 416 assert (false); 417} catch (e) { 418 assert (e === "abrupt index toNumber"); 419} 420 421try { 422 replace.call ({ 423 exec: () => { 424 return { 425 length: 2, 426 0: "str", 427 index: 0, 428 get 1() { 429 throw "abrupt capture" 430 } 431 } 432 } 433 }, "string", "replace"); 434 assert (false); 435} catch (e) { 436 assert (e === "abrupt capture"); 437} 438 439try { 440 replace.call ({ 441 exec: () => { 442 return { 443 length: 2, 444 0: "str", 445 index: 0, 446 1: { 447 toString: () => { 448 throw "abrupt capture toString" 449 } 450 } 451 } 452 } 453 }, "string", "replace"); 454 assert (false); 455} catch (e) { 456 assert (e === "abrupt capture toString"); 457} 458 459try { 460 replace.call ({ 461 exec: () => { 462 return { 463 length: 2, 464 0: "str", 465 index: 0, 466 1: "st" 467 } 468 } 469 }, "string", () => { 470 throw "abrupt replace" 471 }); 472 assert (false); 473} catch (e) { 474 assert (e === "abrupt replace"); 475} 476 477try { 478 replace.call ({ 479 exec: () => { 480 return { 481 length: 2, 482 0: "str", 483 index: 0, 484 1: "st" 485 } 486 } 487 }, "string", () => { 488 return { 489 toString: () => { 490 throw "abrupt replace toString" 491 } 492 } 493 }); 494 assert (false); 495} catch (e) { 496 assert (e === "abrupt replace toString"); 497} 498 499try { 500 replace.call (/abc/, "abc", () => { 501 throw "fastpath abrupt replace" 502 }); 503 assert (false); 504} catch (e) { 505 assert (e === "fastpath abrupt replace"); 506} 507 508try { 509 replace.call (/abc/, "abc", () => { 510 return { 511 toString: () => { 512 throw "fastpath abrupt replace" 513 } 514 } 515 }); 516 assert (false); 517} catch (e) { 518 assert (e === "fastpath abrupt replace"); 519} 520 521assert (replace.call (/abc/, "abc", "xyz") === "xyz"); 522assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "xyz") === "abxyzfg"); 523assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$$-") === "ab-$-fg"); 524assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$&-") === "ab-cde-fg"); 525assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$`-") === "ab-ab-fg"); 526assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$'-") === "ab-fg-fg"); 527assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$0-") === "ab-$0-fg"); 528assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$1-") === "ab-c-fg"); 529assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$2-") === "ab-d-fg"); 530assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$3-") === "ab-d-fg"); 531assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$4-") === "ab--fg"); 532assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$5-") === "ab-e-fg"); 533assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$6-") === "ab-$6-fg"); 534assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$00-") === "ab-$00-fg"); 535assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$01-") === "ab-c-fg"); 536assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$10-") === "ab-c0-fg"); 537assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$99-") === "ab-$99-fg"); 538assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$$1-") === "ab-$1-fg"); 539assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "$") === "ab$fg"); 540assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "$@") === "ab$@fg"); 541 542replace.call (/(c)((d)|(x))(e)/, "abcdefg", function () { 543 assert (arguments[0] === "cde"); 544 assert (arguments[1] === "c"); 545 assert (arguments[2] === "d"); 546 assert (arguments[3] === "d"); 547 assert (arguments[4] === undefined); 548 assert (arguments[5] === "e"); 549 assert (arguments[6] === 2); 550 assert (arguments[7] === "abcdefg"); 551}); 552 553var re = /ab/g; 554assert (replace.call (re, "-ab-ab-ab-ab-", "cd") === "-cd-cd-cd-cd-"); 555assert (re.lastIndex === 0); 556 557re.lastIndex = 5; 558assert (replace.call (re, "-ab-ab-ab-ab-", "cd") === "-cd-cd-cd-cd-"); 559assert (re.lastIndex === 0); 560 561assert (replace.call (/(?:)/g, "string", "Duck") === "DucksDucktDuckrDuckiDucknDuckgDuck"); 562 563class Regexplike { 564 constructor() { 565 this.index = 0; 566 this.global = true; 567 } 568 569 exec() { 570 if (this.index > 0) { 571 return null; 572 } 573 574 this.index = 39; 575 var result = { 576 length: 1, 577 0: "Duck", 578 index: this.index 579 }; 580 return result; 581 } 582} 583 584re = new Regexplike(); 585 586/* Well-behaved RegExp-like object. */ 587assert (replace.call (re, "What have you brought upon this cursed land", "$&") === "What have you brought upon this cursed Duck"); 588 589var replace_count = 0; 590 591function replacer() { 592 replace_count++; 593 return arguments[0]; 594} 595 596re.index = 0; 597re.exec = function () { 598 if (this.index > 3) { 599 return null; 600 } 601 602 var result = { 603 length: 1, 604 0: "Duck", 605 index: this.index++ 606 }; 607 return result; 608} 609 610/* Mis-behaving RegExp-like object, replace function is called on each match, but the result is ignored for inconsistent matches. */ 611assert (replace.call (re, "Badger", replacer) === "Ducker"); 612assert (replace_count === 4); 613 614re.index = 0; 615assert (replace.call (re, "Badger", "Ord") === "Order"); 616 617try { 618 replace.call (RegExp.prototype, "string", "replace"); 619 assert (false); 620} catch (e) { 621 assert (e instanceof TypeError); 622} 623 624assert(replace.call({ exec : ( ) => { return { } } }, 'һ', "a") === "a"); 625assert(replace.call({ exec : ( ) => { return { } } }, 'һһһһһһһһһ', "a") === "a"); 626assert(replace.call({ exec : ( ) => { return { } } }, 'һһһһһһһһһһ', "a") === "aһ"); 627 628/* Object with custom @@replace method */ 629var o = {} 630o[Symbol.replace] = function () { 631 return "Duck" 632}; 633assert ("string".replace (o, "Mallard") === "Duck"); 634 635o[Symbol.replace] = 42; 636try { 637 "string".replace (o, "Duck"); 638 assert (false); 639} catch (e) { 640 assert (e instanceof TypeError); 641} 642 643Object.defineProperty (o, Symbol.replace, { 644 get: () => { 645 throw "abrupt @@replace get" 646 }, 647 set: (v) => {} 648}); 649 650try { 651 "string".replace (o, "Duck"); 652 assert (false); 653} catch (e) { 654 assert (e === "abrupt @@replace get"); 655} 656 657o = {}; 658o[Symbol.replace] = function () { 659 throw "abrupt @@replace" 660}; 661try { 662 "string".replace (o, "str"); 663 assert (false); 664} catch (e) { 665 assert (e === "abrupt @@replace") 666} 667 668class Regexplike2 { 669 exec() { 670 return {} 671 } 672} 673re = new Regexplike2(); 674assert (replace.call (re, "1") === "undefined"); 675