| OLD | NEW |
| 1 // Copyright 2006-2009 the V8 project authors. All rights reserved. | 1 // Copyright 2006-2009 the V8 project authors. All rights reserved. |
| 2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
| 3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
| 4 // met: | 4 // met: |
| 5 // | 5 // |
| 6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
| 7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
| 8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
| 9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
| 10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
| (...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 234 | 234 |
| 235 // ECMA-262, section 15.5.4.11 | 235 // ECMA-262, section 15.5.4.11 |
| 236 function StringReplace(search, replace) { | 236 function StringReplace(search, replace) { |
| 237 var subject = TO_STRING_INLINE(this); | 237 var subject = TO_STRING_INLINE(this); |
| 238 | 238 |
| 239 // Delegate to one of the regular expression variants if necessary. | 239 // Delegate to one of the regular expression variants if necessary. |
| 240 if (IS_REGEXP(search)) { | 240 if (IS_REGEXP(search)) { |
| 241 %_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]); | 241 %_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]); |
| 242 if (IS_FUNCTION(replace)) { | 242 if (IS_FUNCTION(replace)) { |
| 243 regExpCache.type = 'none'; | 243 regExpCache.type = 'none'; |
| 244 return StringReplaceRegExpWithFunction(subject, search, replace); | 244 if (search.global) { |
| 245 return StringReplaceGlobalRegExpWithFunction(subject, search, replace); |
| 246 } else { |
| 247 return StringReplaceNonGlobalRegExpWithFunction(subject, |
| 248 search, |
| 249 replace); |
| 250 } |
| 245 } else { | 251 } else { |
| 246 return StringReplaceRegExp(subject, search, replace); | 252 return StringReplaceRegExp(subject, search, replace); |
| 247 } | 253 } |
| 248 } | 254 } |
| 249 | 255 |
| 250 // Convert the search argument to a string and search for it. | 256 // Convert the search argument to a string and search for it. |
| 251 search = TO_STRING_INLINE(search); | 257 search = TO_STRING_INLINE(search); |
| 252 var start = %StringIndexOf(subject, search, 0); | 258 var start = %StringIndexOf(subject, search, 0); |
| 253 if (start < 0) return subject; | 259 if (start < 0) return subject; |
| 254 var end = start + search.length; | 260 var end = start + search.length; |
| (...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 389 } | 395 } |
| 390 }; | 396 }; |
| 391 | 397 |
| 392 | 398 |
| 393 // Compute the string of a given regular expression capture. | 399 // Compute the string of a given regular expression capture. |
| 394 function CaptureString(string, lastCaptureInfo, index) { | 400 function CaptureString(string, lastCaptureInfo, index) { |
| 395 // Scale the index. | 401 // Scale the index. |
| 396 var scaled = index << 1; | 402 var scaled = index << 1; |
| 397 // Compute start and end. | 403 // Compute start and end. |
| 398 var start = lastCaptureInfo[CAPTURE(scaled)]; | 404 var start = lastCaptureInfo[CAPTURE(scaled)]; |
| 405 // If start isn't valid, return undefined. |
| 406 if (start < 0) return; |
| 399 var end = lastCaptureInfo[CAPTURE(scaled + 1)]; | 407 var end = lastCaptureInfo[CAPTURE(scaled + 1)]; |
| 400 // If either start or end is missing return undefined. | |
| 401 if (start < 0 || end < 0) return; | |
| 402 return SubString(string, start, end); | 408 return SubString(string, start, end); |
| 403 }; | 409 }; |
| 404 | 410 |
| 405 | 411 |
| 406 // Add the string of a given regular expression capture to the | 412 // Add the string of a given regular expression capture to the |
| 407 // ReplaceResultBuilder | 413 // ReplaceResultBuilder |
| 408 function addCaptureString(builder, matchInfo, index) { | 414 function addCaptureString(builder, matchInfo, index) { |
| 409 // Scale the index. | 415 // Scale the index. |
| 410 var scaled = index << 1; | 416 var scaled = index << 1; |
| 411 // Compute start and end. | 417 // Compute start and end. |
| 412 var start = matchInfo[CAPTURE(scaled)]; | 418 var start = matchInfo[CAPTURE(scaled)]; |
| 419 if (start < 0) return; |
| 413 var end = matchInfo[CAPTURE(scaled + 1)]; | 420 var end = matchInfo[CAPTURE(scaled + 1)]; |
| 414 // If either start or end is missing return. | |
| 415 if (start < 0 || end <= start) return; | |
| 416 builder.addSpecialSlice(start, end); | 421 builder.addSpecialSlice(start, end); |
| 417 }; | 422 }; |
| 418 | 423 |
| 419 // TODO(lrn): This array will survive indefinitely if replace is never | 424 // TODO(lrn): This array will survive indefinitely if replace is never |
| 420 // called again. However, it will be empty, since the contents are cleared | 425 // called again. However, it will be empty, since the contents are cleared |
| 421 // in the finally block. | 426 // in the finally block. |
| 422 var reusableReplaceArray = $Array(16); | 427 var reusableReplaceArray = $Array(16); |
| 423 | 428 |
| 424 // Helper function for replacing regular expressions with the result of a | 429 // Helper function for replacing regular expressions with the result of a |
| 425 // function application in String.prototype.replace. | 430 // function application in String.prototype.replace. |
| 426 function StringReplaceRegExpWithFunction(subject, regexp, replace) { | 431 function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) { |
| 427 if (regexp.global) { | 432 var resultArray = reusableReplaceArray; |
| 428 var resultArray = reusableReplaceArray; | 433 if (resultArray) { |
| 429 if (resultArray) { | 434 reusableReplaceArray = null; |
| 430 reusableReplaceArray = null; | 435 } else { |
| 431 } else { | 436 // Inside a nested replace (replace called from the replacement function |
| 432 // Inside a nested replace (replace called from the replacement function | 437 // of another replace) or we have failed to set the reusable array |
| 433 // of another replace) or we have failed to set the reusable array | 438 // back due to an exception in a replacement function. Create a new |
| 434 // back due to an exception in a replacement function. Create a new | 439 // array to use in the future, or until the original is written back. |
| 435 // array to use in the future, or until the original is written back. | 440 resultArray = $Array(16); |
| 436 resultArray = $Array(16); | 441 } |
| 442 var res = %RegExpExecMultiple(regexp, |
| 443 subject, |
| 444 lastMatchInfo, |
| 445 resultArray); |
| 446 regexp.lastIndex = 0; |
| 447 if (IS_NULL(res)) { |
| 448 // No matches at all. |
| 449 reusableReplaceArray = resultArray; |
| 450 return subject; |
| 451 } |
| 452 var len = res.length; |
| 453 var i = 0; |
| 454 if (NUMBER_OF_CAPTURES(lastMatchInfo) == 2) { |
| 455 var match_start = 0; |
| 456 var override = [null, 0, subject]; |
| 457 var receiver = %GetGlobalReceiver(); |
| 458 while (i < len) { |
| 459 var elem = res[i]; |
| 460 if (%_IsSmi(elem)) { |
| 461 if (elem > 0) { |
| 462 match_start = (elem >> 11) + (elem & 0x7ff); |
| 463 } else { |
| 464 match_start = res[++i] - elem; |
| 465 } |
| 466 } else { |
| 467 override[0] = elem; |
| 468 override[1] = match_start; |
| 469 lastMatchInfoOverride = override; |
| 470 var func_result = |
| 471 %_CallFunction(receiver, elem, match_start, subject, replace); |
| 472 if (!IS_STRING(func_result)) { |
| 473 func_result = NonStringToString(func_result); |
| 474 } |
| 475 res[i] = func_result; |
| 476 match_start += elem.length; |
| 477 } |
| 478 i++; |
| 437 } | 479 } |
| 438 | 480 } else { |
| 439 var res = %RegExpExecMultiple(regexp, | 481 while (i < len) { |
| 440 subject, | 482 var elem = res[i]; |
| 441 lastMatchInfo, | 483 if (!%_IsSmi(elem)) { |
| 442 resultArray); | 484 // elem must be an Array. |
| 443 regexp.lastIndex = 0; | 485 // Use the apply argument as backing for global RegExp properties. |
| 444 if (IS_NULL(res)) { | 486 lastMatchInfoOverride = elem; |
| 445 // No matches at all. | 487 var func_result = replace.apply(null, elem); |
| 446 return subject; | 488 if (!IS_STRING(func_result)) { |
| 489 func_result = NonStringToString(func_result); |
| 490 } |
| 491 res[i] = func_result; |
| 492 } |
| 493 i++; |
| 447 } | 494 } |
| 448 var len = res.length; | |
| 449 var i = 0; | |
| 450 if (NUMBER_OF_CAPTURES(lastMatchInfo) == 2) { | |
| 451 var match_start = 0; | |
| 452 var override = [null, 0, subject]; | |
| 453 while (i < len) { | |
| 454 var elem = res[i]; | |
| 455 if (%_IsSmi(elem)) { | |
| 456 if (elem > 0) { | |
| 457 match_start = (elem >> 11) + (elem & 0x7ff); | |
| 458 } else { | |
| 459 match_start = res[++i] - elem; | |
| 460 } | |
| 461 } else { | |
| 462 override[0] = elem; | |
| 463 override[1] = match_start; | |
| 464 lastMatchInfoOverride = override; | |
| 465 var func_result = replace.call(null, elem, match_start, subject); | |
| 466 if (!IS_STRING(func_result)) { | |
| 467 func_result = NonStringToString(func_result); | |
| 468 } | |
| 469 res[i] = func_result; | |
| 470 match_start += elem.length; | |
| 471 } | |
| 472 i++; | |
| 473 } | |
| 474 } else { | |
| 475 while (i < len) { | |
| 476 var elem = res[i]; | |
| 477 if (!%_IsSmi(elem)) { | |
| 478 // elem must be an Array. | |
| 479 // Use the apply argument as backing for global RegExp properties. | |
| 480 lastMatchInfoOverride = elem; | |
| 481 var func_result = replace.apply(null, elem); | |
| 482 if (!IS_STRING(func_result)) { | |
| 483 func_result = NonStringToString(func_result); | |
| 484 } | |
| 485 res[i] = func_result; | |
| 486 } | |
| 487 i++; | |
| 488 } | |
| 489 } | |
| 490 var resultBuilder = new ReplaceResultBuilder(subject, res); | |
| 491 var result = resultBuilder.generate(); | |
| 492 resultArray.length = 0; | |
| 493 reusableReplaceArray = resultArray; | |
| 494 return result; | |
| 495 } else { // Not a global regexp, no need to loop. | |
| 496 var matchInfo = DoRegExpExec(regexp, subject, 0); | |
| 497 if (IS_NULL(matchInfo)) return subject; | |
| 498 | |
| 499 var result = new ReplaceResultBuilder(subject); | |
| 500 result.addSpecialSlice(0, matchInfo[CAPTURE0]); | |
| 501 var endOfMatch = matchInfo[CAPTURE1]; | |
| 502 result.add(ApplyReplacementFunction(replace, matchInfo, subject)); | |
| 503 // Can't use matchInfo any more from here, since the function could | |
| 504 // overwrite it. | |
| 505 result.addSpecialSlice(endOfMatch, subject.length); | |
| 506 return result.generate(); | |
| 507 } | 495 } |
| 496 var resultBuilder = new ReplaceResultBuilder(subject, res); |
| 497 var result = resultBuilder.generate(); |
| 498 resultArray.length = 0; |
| 499 reusableReplaceArray = resultArray; |
| 500 return result; |
| 508 } | 501 } |
| 509 | 502 |
| 510 | 503 |
| 511 // Helper function to apply a string replacement function once. | 504 function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) { |
| 512 function ApplyReplacementFunction(replace, matchInfo, subject) { | 505 var matchInfo = DoRegExpExec(regexp, subject, 0); |
| 506 if (IS_NULL(matchInfo)) return subject; |
| 507 var result = new ReplaceResultBuilder(subject); |
| 508 var index = matchInfo[CAPTURE0]; |
| 509 result.addSpecialSlice(0, index); |
| 510 var endOfMatch = matchInfo[CAPTURE1]; |
| 513 // Compute the parameter list consisting of the match, captures, index, | 511 // Compute the parameter list consisting of the match, captures, index, |
| 514 // and subject for the replace function invocation. | 512 // and subject for the replace function invocation. |
| 515 var index = matchInfo[CAPTURE0]; | |
| 516 // The number of captures plus one for the match. | 513 // The number of captures plus one for the match. |
| 517 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1; | 514 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1; |
| 515 var replacement; |
| 518 if (m == 1) { | 516 if (m == 1) { |
| 519 var s = CaptureString(subject, matchInfo, 0); | 517 // No captures, only the match, which is always valid. |
| 518 var s = SubString(subject, index, endOfMatch); |
| 520 // Don't call directly to avoid exposing the built-in global object. | 519 // Don't call directly to avoid exposing the built-in global object. |
| 521 return replace.call(null, s, index, subject); | 520 replacement = |
| 521 %_CallFunction(%GetGlobalReceiver(), s, index, subject, replace); |
| 522 } else { |
| 523 var parameters = $Array(m + 2); |
| 524 for (var j = 0; j < m; j++) { |
| 525 parameters[j] = CaptureString(subject, matchInfo, j); |
| 526 } |
| 527 parameters[j] = index; |
| 528 parameters[j + 1] = subject; |
| 529 |
| 530 replacement = replace.apply(null, parameters); |
| 522 } | 531 } |
| 523 var parameters = $Array(m + 2); | 532 |
| 524 for (var j = 0; j < m; j++) { | 533 result.add(replacement); // The add method converts to string if necessary. |
| 525 parameters[j] = CaptureString(subject, matchInfo, j); | 534 // Can't use matchInfo any more from here, since the function could |
| 526 } | 535 // overwrite it. |
| 527 parameters[j] = index; | 536 result.addSpecialSlice(endOfMatch, subject.length); |
| 528 parameters[j + 1] = subject; | 537 return result.generate(); |
| 529 return replace.apply(null, parameters); | |
| 530 } | 538 } |
| 531 | 539 |
| 540 |
| 532 // ECMA-262 section 15.5.4.12 | 541 // ECMA-262 section 15.5.4.12 |
| 533 function StringSearch(re) { | 542 function StringSearch(re) { |
| 534 var regexp; | 543 var regexp; |
| 535 if (IS_STRING(re)) { | 544 if (IS_STRING(re)) { |
| 536 regexp = %_GetFromCache(STRING_TO_REGEXP_CACHE_ID, re); | 545 regexp = %_GetFromCache(STRING_TO_REGEXP_CACHE_ID, re); |
| 537 } else if (IS_REGEXP(re)) { | 546 } else if (IS_REGEXP(re)) { |
| 538 regexp = re; | 547 regexp = re; |
| 539 } else { | 548 } else { |
| 540 regexp = new $RegExp(re); | 549 regexp = new $RegExp(re); |
| 541 } | 550 } |
| (...skipping 461 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1003 "small", StringSmall, | 1012 "small", StringSmall, |
| 1004 "strike", StringStrike, | 1013 "strike", StringStrike, |
| 1005 "sub", StringSub, | 1014 "sub", StringSub, |
| 1006 "sup", StringSup, | 1015 "sup", StringSup, |
| 1007 "toJSON", StringToJSON | 1016 "toJSON", StringToJSON |
| 1008 )); | 1017 )); |
| 1009 } | 1018 } |
| 1010 | 1019 |
| 1011 | 1020 |
| 1012 SetupString(); | 1021 SetupString(); |
| OLD | NEW |