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

Side by Side Diff: src/string.js

Issue 1014005: Attempt at pre-calculating replace regexp matches. (Closed)
Patch Set: Reuse result array. Avoid using apply. 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
« no previous file with comments | « 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 10 matching lines...) Expand all
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
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
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();
OLDNEW
« no previous file with comments | « src/runtime.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698