Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(36)

Side by Side Diff: src/string.js

Issue 1109010: Run string replace regexp with function in C++ code loop. (Closed)
Patch Set: Fix to also work in debug mode. Created 10 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« src/runtime.cc ('K') | « src/runtime.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 387 matching lines...) Expand 10 before | Expand all | Expand 10 after
398 // Scale the index. 398 // Scale the index.
399 var scaled = index << 1; 399 var scaled = index << 1;
400 // Compute start and end. 400 // Compute start and end.
401 var start = matchInfo[CAPTURE(scaled)]; 401 var start = matchInfo[CAPTURE(scaled)];
402 var end = matchInfo[CAPTURE(scaled + 1)]; 402 var end = matchInfo[CAPTURE(scaled + 1)];
403 // If either start or end is missing return. 403 // If either start or end is missing return.
404 if (start < 0 || end <= start) return; 404 if (start < 0 || end <= start) return;
405 builder.addSpecialSlice(start, end); 405 builder.addSpecialSlice(start, end);
406 }; 406 };
407 407
408 // TODO(lrn): This array will survive indefinitely if replace is never
409 // called again. However, it will be empty, since the contents are cleared
410 // in the finally block.
411 var reusableReplaceArray = $Array(16);
408 412
409 // Helper function for replacing regular expressions with the result of a 413 // Helper function for replacing regular expressions with the result of a
410 // function application in String.prototype.replace. The function application 414 // function application in String.prototype.replace.
411 // must be interleaved with the regexp matching (contrary to ECMA-262
412 // 15.5.4.11) to mimic SpiderMonkey and KJS behavior when the function uses
413 // the static properties of the RegExp constructor. Example:
414 // 'abcd'.replace(/(.)/g, function() { return RegExp.$1; }
415 // should be 'abcd' and not 'dddd' (or anything else).
416 function StringReplaceRegExpWithFunction(subject, regexp, replace) { 415 function StringReplaceRegExpWithFunction(subject, regexp, replace) {
417 var matchInfo = DoRegExpExec(regexp, subject, 0);
418 if (IS_NULL(matchInfo)) return subject;
419
420 var result = new ReplaceResultBuilder(subject);
421 // There's at least one match. If the regexp is global, we have to loop
422 // over all matches. The loop is not in C++ code here like the one in
423 // RegExp.prototype.exec, because of the interleaved function application.
424 // Unfortunately, that means this code is nearly duplicated, here and in
425 // jsregexp.cc.
426 if (regexp.global) { 416 if (regexp.global) {
427 var previous = 0; 417 var resultArray = reusableReplaceArray;
428 var startOfMatch; 418 if (resultArray) {
429 if (NUMBER_OF_CAPTURES(matchInfo) == 2) { 419 reusableReplaceArray = null;
430 // Both branches contain essentially the same loop except for the call 420 } else {
431 // to the replace function. The branch is put outside of the loop for 421 // Inside a nested replace (replace called from the replacement function
432 // speed 422 // of another replace) or we have failed to set the reusable array
433 do { 423 // back due to an exception in a replacement function. Create a new
434 startOfMatch = matchInfo[CAPTURE0]; 424 // array to use in the future, or until the original is written back.
435 result.addSpecialSlice(previous, startOfMatch); 425 resultArray = $Array(16);
436 previous = matchInfo[CAPTURE1]; 426 }
437 var match = SubString(subject, startOfMatch, previous); 427 try {
438 // Don't call directly to avoid exposing the built-in global object. 428 // Must handle exceptions thrown by the replace functions correctly,
439 result.add(replace.call(null, match, startOfMatch, subject)); 429 // including unregistering global regexps.
440 // Can't use matchInfo any more from here, since the function could 430 var res = %RegExpExecMultiple(regexp,
441 // overwrite it. 431 subject,
442 // Continue with the next match. 432 lastMatchInfo,
443 // Increment previous if we matched an empty string, as per ECMA-262 433 resultArray);
444 // 15.5.4.10. 434 regexp.lastIndex = 0; // why this?
fschneider 2010/03/25 10:26:06 Clarify this comment.
Lasse Reichstein 2010/03/26 11:18:26 Removed. There is no doubt why if one reads the sp
445 if (previous == startOfMatch) { 435 if (IS_NULL(res)) {
446 // Add the skipped character to the output, if any. 436 // No matches at all.
447 if (previous < subject.length) { 437 return subject;
448 result.addSpecialSlice(previous, previous + 1); 438 }
439 var len = res.length;
440 var i = 0;
441 if (NUMBER_OF_CAPTURES(lastMatchInfo) == 2) {
442 var match_start = 0;
443 while (i < len) {
444 var elem = res[i];
445 if (%_IsSmi(elem)) {
446 if (elem > 0) {
447 match_start = (elem >> 11) + (elem & 0x7ff);
448 } else {
449 match_start = res[++i] - elem;
450 }
451 } else {
452 var func_result = replace.call(null, elem, match_start, subject);
453 if (!IS_STRING(func_result)) func_result = TO_STRING(func_result);
454 res[i] = func_result;
455 match_start += elem.length;
449 } 456 }
450 previous++; 457 i++;
451 // Per ECMA-262 15.10.6.2, if the previous index is greater than the 458 }
452 // string length, there is no match 459 } else {
453 if (previous > subject.length) { 460 while (i < len) {
454 return result.generate(); 461 var elem = res[i];
462 if (!%_IsSmi(elem)) {
463 // elem must be an Array.
464 // Use the apply argument as backing for global RegExp properties.
465 lastMatchInfoOverride = elem;
466 var func_result = replace.apply(null, elem);
467 if (!IS_STRING(func_result)) func_result = TO_STRING(func_result);
468 res[i] = func_result;
455 } 469 }
470 i++;
456 } 471 }
457 matchInfo = DoRegExpExec(regexp, subject, previous); 472 }
458 } while (!IS_NULL(matchInfo)); 473 var result = new ReplaceResultBuilder(subject, res);
459 } else { 474 return result.generate();
460 do { 475 } finally {
461 startOfMatch = matchInfo[CAPTURE0]; 476 lastMatchInfoOverride = null;
462 result.addSpecialSlice(previous, startOfMatch); 477 resultArray.length = 0;
463 previous = matchInfo[CAPTURE1]; 478 reusableReplaceArray = resultArray;
464 result.add(ApplyReplacementFunction(replace, matchInfo, subject));
465 // Can't use matchInfo any more from here, since the function could
466 // overwrite it.
467 // Continue with the next match.
468 // Increment previous if we matched an empty string, as per ECMA-262
469 // 15.5.4.10.
470 if (previous == startOfMatch) {
471 // Add the skipped character to the output, if any.
472 if (previous < subject.length) {
473 result.addSpecialSlice(previous, previous + 1);
474 }
475 previous++;
476 // Per ECMA-262 15.10.6.2, if the previous index is greater than the
477 // string length, there is no match
478 if (previous > subject.length) {
479 return result.generate();
480 }
481 }
482 matchInfo = DoRegExpExec(regexp, subject, previous);
483 } while (!IS_NULL(matchInfo));
484 } 479 }
485
486 // Tack on the final right substring after the last match.
487 result.addSpecialSlice(previous, subject.length);
488
489 } else { // Not a global regexp, no need to loop. 480 } else { // Not a global regexp, no need to loop.
481 var matchInfo = DoRegExpExec(regexp, subject, 0);
482 if (IS_NULL(matchInfo)) return subject;
483
484 var result = new ReplaceResultBuilder(subject);
490 result.addSpecialSlice(0, matchInfo[CAPTURE0]); 485 result.addSpecialSlice(0, matchInfo[CAPTURE0]);
491 var endOfMatch = matchInfo[CAPTURE1]; 486 var endOfMatch = matchInfo[CAPTURE1];
492 result.add(ApplyReplacementFunction(replace, matchInfo, subject)); 487 result.add(ApplyReplacementFunction(replace, matchInfo, subject));
493 // Can't use matchInfo any more from here, since the function could 488 // Can't use matchInfo any more from here, since the function could
494 // overwrite it. 489 // overwrite it.
495 result.addSpecialSlice(endOfMatch, subject.length); 490 result.addSpecialSlice(endOfMatch, subject.length);
491 return result.generate();
496 } 492 }
497
498 return result.generate();
499 } 493 }
500 494
501 495
502 // Helper function to apply a string replacement function once. 496 // Helper function to apply a string replacement function once.
503 function ApplyReplacementFunction(replace, matchInfo, subject) { 497 function ApplyReplacementFunction(replace, matchInfo, subject) {
504 // Compute the parameter list consisting of the match, captures, index, 498 // Compute the parameter list consisting of the match, captures, index,
505 // and subject for the replace function invocation. 499 // and subject for the replace function invocation.
506 var index = matchInfo[CAPTURE0]; 500 var index = matchInfo[CAPTURE0];
507 // The number of captures plus one for the match. 501 // The number of captures plus one for the match.
508 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1; 502 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1;
(...skipping 380 matching lines...) Expand 10 before | Expand all | Expand 10 after
889 } 883 }
890 884
891 885
892 function StringSup() { 886 function StringSup() {
893 return "<sup>" + this + "</sup>"; 887 return "<sup>" + this + "</sup>";
894 } 888 }
895 889
896 890
897 // ReplaceResultBuilder support. 891 // ReplaceResultBuilder support.
898 function ReplaceResultBuilder(str) { 892 function ReplaceResultBuilder(str) {
899 this.elements = new $Array(); 893 if (%_ArgumentsLength() > 1) {
894 this.elements = %_Arguments(1);
895 } else {
896 this.elements = new $Array();
897 }
900 this.special_string = str; 898 this.special_string = str;
901 } 899 }
902 900
903 901
904 ReplaceResultBuilder.prototype.add = function(str) { 902 ReplaceResultBuilder.prototype.add = function(str) {
905 str = TO_STRING_INLINE(str); 903 str = TO_STRING_INLINE(str);
906 if (str.length > 0) { 904 if (str.length > 0) {
907 var elements = this.elements; 905 var elements = this.elements;
908 elements[elements.length] = str; 906 elements[elements.length] = str;
909 } 907 }
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
985 "small", StringSmall, 983 "small", StringSmall,
986 "strike", StringStrike, 984 "strike", StringStrike,
987 "sub", StringSub, 985 "sub", StringSub,
988 "sup", StringSup, 986 "sup", StringSup,
989 "toJSON", StringToJSON 987 "toJSON", StringToJSON
990 )); 988 ));
991 } 989 }
992 990
993 991
994 SetupString(); 992 SetupString();
OLDNEW
« src/runtime.cc ('K') | « src/runtime.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698