| 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 10 matching lines...) Expand all Loading... |
| 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 | 27 |
| 28 | 28 |
| 29 // This file relies on the fact that the following declaration has been made | 29 // This file relies on the fact that the following declaration has been made |
| 30 // in runtime.js: | 30 // in runtime.js: |
| 31 // const $Function = global.Function |
| 31 // const $String = global.String; | 32 // const $String = global.String; |
| 32 // const $NaN = 0/0; | 33 // const $NaN = 0/0; |
| 33 | 34 |
| 34 | 35 |
| 35 // Set the String function and constructor. | 36 // Set the String function and constructor. |
| 36 %SetCode($String, function(x) { | 37 %SetCode($String, function(x) { |
| 37 var value = %_ArgumentsLength() == 0 ? '' : TO_STRING_INLINE(x); | 38 var value = %_ArgumentsLength() == 0 ? '' : TO_STRING_INLINE(x); |
| 38 if (%_IsConstructCall()) { | 39 if (%_IsConstructCall()) { |
| 39 %_SetValueOf(this, value); | 40 %_SetValueOf(this, value); |
| 40 } else { | 41 } else { |
| (...skipping 336 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 377 var scaled = index << 1; | 378 var scaled = index << 1; |
| 378 // Compute start and end. | 379 // Compute start and end. |
| 379 var start = matchInfo[CAPTURE(scaled)]; | 380 var start = matchInfo[CAPTURE(scaled)]; |
| 380 var end = matchInfo[CAPTURE(scaled + 1)]; | 381 var end = matchInfo[CAPTURE(scaled + 1)]; |
| 381 // If either start or end is missing return. | 382 // If either start or end is missing return. |
| 382 if (start < 0 || end <= start) return; | 383 if (start < 0 || end <= start) return; |
| 383 builder.addSpecialSlice(start, end); | 384 builder.addSpecialSlice(start, end); |
| 384 }; | 385 }; |
| 385 | 386 |
| 386 | 387 |
| 388 var nestedReplace = 0; |
| 389 var staticReplaceResult = $Array(64); |
| 390 var replaceAppliers = { __proto__: null }; // Really empty object. |
| 391 |
| 392 function makeReplaceApplier(n) { |
| 393 var source = "return func.call(null, arr[idx+1],"; |
| 394 for (var i = 0; i < n; i++) { |
| 395 source += " arr[idx+" + (i + 2) +"],"; |
| 396 } |
| 397 source += " arr[idx], subject);"; |
| 398 return $Function("func, arr, idx, subject", source); |
| 399 } |
| 400 |
| 387 // Helper function for replacing regular expressions with the result of a | 401 // Helper function for replacing regular expressions with the result of a |
| 388 // function application in String.prototype.replace. The function application | 402 // function application in String.prototype.replace. |
| 389 // must be interleaved with the regexp matching (contrary to ECMA-262 | |
| 390 // 15.5.4.11) to mimic SpiderMonkey and KJS behavior when the function uses | |
| 391 // the static properties of the RegExp constructor. Example: | |
| 392 // 'abcd'.replace(/(.)/g, function() { return RegExp.$1; } | |
| 393 // should be 'abcd' and not 'dddd' (or anything else). | |
| 394 function StringReplaceRegExpWithFunction(subject, regexp, replace) { | 403 function StringReplaceRegExpWithFunction(subject, regexp, replace) { |
| 395 var matchInfo = DoRegExpExec(regexp, subject, 0); | |
| 396 if (IS_NULL(matchInfo)) return subject; | |
| 397 | |
| 398 var result = new ReplaceResultBuilder(subject); | |
| 399 // There's at least one match. If the regexp is global, we have to loop | |
| 400 // over all matches. The loop is not in C++ code here like the one in | |
| 401 // RegExp.prototype.exec, because of the interleaved function application. | |
| 402 // Unfortunately, that means this code is nearly duplicated, here and in | |
| 403 // jsregexp.cc. | |
| 404 if (regexp.global) { | 404 if (regexp.global) { |
| 405 var previous = 0; | 405 var matches = (nestedReplace > 0) ? $Array(0) : staticReplaceResult; |
| 406 var startOfMatch; | 406 var match_count = %RegExpExecMultiple(regexp, subject, lastMatchInfo, matche
s); |
| 407 if (NUMBER_OF_CAPTURES(matchInfo) == 2) { | 407 regexp.lastIndex = 0; |
| 408 // Both branches contain essentially the same loop except for the call | 408 if (match_count == 0) return subject; // No matches at all. |
| 409 // to the replace function. The branch is put outside of the loop for | 409 // Capture count, not including actual match. |
| 410 // speed | 410 var capture_count = matches[0]; |
| 411 do { | 411 var result = new ReplaceResultBuilder(subject); |
| 412 startOfMatch = matchInfo[CAPTURE0]; | 412 var match_index = 1; |
| 413 result.addSpecialSlice(previous, startOfMatch); | 413 var index = 0; |
| 414 previous = matchInfo[CAPTURE1]; | 414 nestedReplace++; |
| 415 var match = SubString(subject, startOfMatch, previous); | 415 try { |
| 416 // Don't call directly to avoid exposing the built-in global object. | 416 if (capture_count == 0) { |
| 417 result.add(replace.call(null, match, startOfMatch, subject)); | 417 while (match_index < matches.length) { |
| 418 // Can't use matchInfo any more from here, since the function could | 418 var match_start = matches[match_index]; |
| 419 // overwrite it. | 419 if (index < match_start) { |
| 420 // Continue with the next match. | 420 result.addSpecialSlice(index, match_start); |
| 421 // Increment previous if we matched an empty string, as per ECMA-262 | |
| 422 // 15.5.4.10. | |
| 423 if (previous == startOfMatch) { | |
| 424 // Add the skipped character to the output, if any. | |
| 425 if (previous < subject.length) { | |
| 426 result.addSpecialSlice(previous, previous + 1); | |
| 427 } | 421 } |
| 428 previous++; | 422 var match = matches[match_index + 1]; |
| 429 // Per ECMA-262 15.10.6.2, if the previous index is greater than the | 423 var func_result = replace.call(null, match, match_start, subject); |
| 430 // string length, there is no match | 424 if (!IS_STRING(func_result)) func_result = TO_STRING(func_result); |
| 431 if (previous > subject.length) { | 425 result.add(func_result); |
| 432 return result.generate(); | 426 index = match_start + match.length; |
| 427 match_index += 2; |
| 428 } |
| 429 } else { |
| 430 var applier = replaceAppliers[capture_count]; |
| 431 if (!applier) { |
| 432 applier = replaceAppliers[capture_count] = |
| 433 makeReplaceApplier(capture_count); |
| 434 } |
| 435 while (match_index < matches.length) { |
| 436 var match_start = matches[match_index]; |
| 437 if (index < match_start) { |
| 438 result.addSpecialSlice(index, match_start); |
| 433 } | 439 } |
| 440 var match = matches[match_index + 1]; |
| 441 var func_result = applier(replace, matches, match_index, subject); |
| 442 if (!IS_STRING(func_result)) func_result = TO_STRING(func_result); |
| 443 result.add(func_result); |
| 444 index = match_start + match.length; |
| 445 match_index += capture_count + 2; |
| 434 } | 446 } |
| 435 matchInfo = DoRegExpExec(regexp, subject, previous); | 447 } |
| 436 } while (!IS_NULL(matchInfo)); | 448 if (index < subject.length) { |
| 437 } else { | 449 result.addSpecialSlice(index, subject.length); |
| 438 do { | 450 } |
| 439 startOfMatch = matchInfo[CAPTURE0]; | 451 } finally { |
| 440 result.addSpecialSlice(previous, startOfMatch); | 452 nestedReplace--; |
| 441 previous = matchInfo[CAPTURE1]; | |
| 442 result.add(ApplyReplacementFunction(replace, matchInfo, subject)); | |
| 443 // Can't use matchInfo any more from here, since the function could | |
| 444 // overwrite it. | |
| 445 // Continue with the next match. | |
| 446 // Increment previous if we matched an empty string, as per ECMA-262 | |
| 447 // 15.5.4.10. | |
| 448 if (previous == startOfMatch) { | |
| 449 // Add the skipped character to the output, if any. | |
| 450 if (previous < subject.length) { | |
| 451 result.addSpecialSlice(previous, previous + 1); | |
| 452 } | |
| 453 previous++; | |
| 454 // Per ECMA-262 15.10.6.2, if the previous index is greater than the | |
| 455 // string length, there is no match | |
| 456 if (previous > subject.length) { | |
| 457 return result.generate(); | |
| 458 } | |
| 459 } | |
| 460 matchInfo = DoRegExpExec(regexp, subject, previous); | |
| 461 } while (!IS_NULL(matchInfo)); | |
| 462 } | 453 } |
| 463 | 454 return result.generate(); |
| 464 // Tack on the final right substring after the last match. | |
| 465 result.addSpecialSlice(previous, subject.length); | |
| 466 | |
| 467 } else { // Not a global regexp, no need to loop. | 455 } else { // Not a global regexp, no need to loop. |
| 456 var matchInfo = DoRegExpExec(regexp, subject, 0); |
| 457 if (IS_NULL(matchInfo)) return subject; |
| 458 |
| 459 var result = new ReplaceResultBuilder(subject); |
| 468 result.addSpecialSlice(0, matchInfo[CAPTURE0]); | 460 result.addSpecialSlice(0, matchInfo[CAPTURE0]); |
| 469 var endOfMatch = matchInfo[CAPTURE1]; | 461 var endOfMatch = matchInfo[CAPTURE1]; |
| 470 result.add(ApplyReplacementFunction(replace, matchInfo, subject)); | 462 result.add(ApplyReplacementFunction(replace, matchInfo, subject)); |
| 471 // Can't use matchInfo any more from here, since the function could | 463 // Can't use matchInfo any more from here, since the function could |
| 472 // overwrite it. | 464 // overwrite it. |
| 473 result.addSpecialSlice(endOfMatch, subject.length); | 465 result.addSpecialSlice(endOfMatch, subject.length); |
| 466 return result.generate(); |
| 474 } | 467 } |
| 475 | 468 |
| 476 return result.generate(); | |
| 477 } | 469 } |
| 478 | 470 |
| 479 | 471 |
| 480 // Helper function to apply a string replacement function once. | 472 // Helper function to apply a string replacement function once. |
| 481 function ApplyReplacementFunction(replace, matchInfo, subject) { | 473 function ApplyReplacementFunction(replace, matchInfo, subject) { |
| 482 // Compute the parameter list consisting of the match, captures, index, | 474 // Compute the parameter list consisting of the match, captures, index, |
| 483 // and subject for the replace function invocation. | 475 // and subject for the replace function invocation. |
| 484 var index = matchInfo[CAPTURE0]; | 476 var index = matchInfo[CAPTURE0]; |
| 485 // The number of captures plus one for the match. | 477 // The number of captures plus one for the match. |
| 486 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1; | 478 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1; |
| (...skipping 452 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 939 "small", StringSmall, | 931 "small", StringSmall, |
| 940 "strike", StringStrike, | 932 "strike", StringStrike, |
| 941 "sub", StringSub, | 933 "sub", StringSub, |
| 942 "sup", StringSup, | 934 "sup", StringSup, |
| 943 "toJSON", StringToJSON | 935 "toJSON", StringToJSON |
| 944 )); | 936 )); |
| 945 } | 937 } |
| 946 | 938 |
| 947 | 939 |
| 948 SetupString(); | 940 SetupString(); |
| OLD | NEW |