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

Side by Side Diff: src/js/regexp.js

Issue 1590673002: Separate String.prototype.replace into RegExp.prototype[Symbol.replace] (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Remove extra newline from test Created 4 years, 11 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 | « no previous file | src/js/string.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2012 the V8 project authors. All rights reserved. 1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 (function(global, utils) { 5 (function(global, utils) {
6 6
7 %CheckIsBootstrapping(); 7 %CheckIsBootstrapping();
8 8
9 // ------------------------------------------------------------------- 9 // -------------------------------------------------------------------
10 // Imports 10 // Imports
11 11
12 var ExpandReplacement;
12 var FLAG_harmony_tolength; 13 var FLAG_harmony_tolength;
13 var GlobalObject = global.Object; 14 var GlobalObject = global.Object;
14 var GlobalRegExp = global.RegExp; 15 var GlobalRegExp = global.RegExp;
15 var GlobalRegExpPrototype; 16 var GlobalRegExpPrototype;
16 var InternalArray = utils.InternalArray; 17 var InternalArray = utils.InternalArray;
17 var InternalPackedArray = utils.InternalPackedArray; 18 var InternalPackedArray = utils.InternalPackedArray;
18 var MakeTypeError; 19 var MakeTypeError;
19 var matchSymbol = utils.ImportNow("match_symbol"); 20 var matchSymbol = utils.ImportNow("match_symbol");
21 var replaceSymbol = utils.ImportNow("replace_symbol");
20 var searchSymbol = utils.ImportNow("search_symbol"); 22 var searchSymbol = utils.ImportNow("search_symbol");
21 var splitSymbol = utils.ImportNow("split_symbol"); 23 var splitSymbol = utils.ImportNow("split_symbol");
22 24
23 utils.ImportFromExperimental(function(from) { 25 utils.ImportFromExperimental(function(from) {
24 FLAG_harmony_tolength = from.FLAG_harmony_tolength; 26 FLAG_harmony_tolength = from.FLAG_harmony_tolength;
25 }); 27 });
26 28
27 utils.Import(function(from) { 29 utils.Import(function(from) {
30 ExpandReplacement = from.ExpandReplacement;
28 MakeTypeError = from.MakeTypeError; 31 MakeTypeError = from.MakeTypeError;
29 }); 32 });
30 33
31 // ------------------------------------------------------------------- 34 // -------------------------------------------------------------------
32 35
33 // Property of the builtins object for recording the result of the last 36 // Property of the builtins object for recording the result of the last
34 // regexp match. The property RegExpLastMatchInfo includes the matchIndices 37 // regexp match. The property RegExpLastMatchInfo includes the matchIndices
35 // array of the last successful regexp match (an array of start/end index 38 // array of the last successful regexp match (an array of start/end index
36 // pairs for the match and all the captured substrings), the invariant is 39 // pairs for the match and all the captured substrings), the invariant is
37 // that there are at least two capture indeces. The array also contains 40 // that there are at least two capture indeces. The array also contains
(...skipping 334 matching lines...) Expand 10 before | Expand all | Expand 10 after
372 } 375 }
373 var subject = TO_STRING(string); 376 var subject = TO_STRING(string);
374 377
375 if (!REGEXP_GLOBAL(this)) return RegExpExecNoTests(this, subject, 0); 378 if (!REGEXP_GLOBAL(this)) return RegExpExecNoTests(this, subject, 0);
376 this.lastIndex = 0; 379 this.lastIndex = 0;
377 var result = %StringMatch(subject, this, RegExpLastMatchInfo); 380 var result = %StringMatch(subject, this, RegExpLastMatchInfo);
378 return result; 381 return result;
379 } 382 }
380 383
381 384
385 // ES6 21.2.5.8.
386
387 // TODO(lrn): This array will survive indefinitely if replace is never
388 // called again. However, it will be empty, since the contents are cleared
389 // in the finally block.
390 var reusableReplaceArray = new InternalArray(4);
391
392 // Helper function for replacing regular expressions with the result of a
393 // function application in String.prototype.replace.
394 function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) {
395 var resultArray = reusableReplaceArray;
396 if (resultArray) {
397 reusableReplaceArray = null;
398 } else {
399 // Inside a nested replace (replace called from the replacement function
400 // of another replace) or we have failed to set the reusable array
401 // back due to an exception in a replacement function. Create a new
402 // array to use in the future, or until the original is written back.
403 resultArray = new InternalArray(16);
404 }
405 var res = %RegExpExecMultiple(regexp,
406 subject,
407 RegExpLastMatchInfo,
408 resultArray);
409 regexp.lastIndex = 0;
410 if (IS_NULL(res)) {
411 // No matches at all.
412 reusableReplaceArray = resultArray;
413 return subject;
414 }
415 var len = res.length;
416 if (NUMBER_OF_CAPTURES(RegExpLastMatchInfo) == 2) {
417 // If the number of captures is two then there are no explicit captures in
418 // the regexp, just the implicit capture that captures the whole match. In
419 // this case we can simplify quite a bit and end up with something faster.
420 // The builder will consist of some integers that indicate slices of the
421 // input string and some replacements that were returned from the replace
422 // function.
423 var match_start = 0;
424 for (var i = 0; i < len; i++) {
425 var elem = res[i];
426 if (%_IsSmi(elem)) {
427 // Integers represent slices of the original string.
428 if (elem > 0) {
429 match_start = (elem >> 11) + (elem & 0x7ff);
430 } else {
431 match_start = res[++i] - elem;
432 }
433 } else {
434 var func_result = replace(elem, match_start, subject);
435 // Overwrite the i'th element in the results with the string we got
436 // back from the callback function.
437 res[i] = TO_STRING(func_result);
438 match_start += elem.length;
439 }
440 }
441 } else {
442 for (var i = 0; i < len; i++) {
443 var elem = res[i];
444 if (!%_IsSmi(elem)) {
445 // elem must be an Array.
446 // Use the apply argument as backing for global RegExp properties.
447 var func_result = %Apply(replace, UNDEFINED, elem, 0, elem.length);
448 // Overwrite the i'th element in the results with the string we got
449 // back from the callback function.
450 res[i] = TO_STRING(func_result);
451 }
452 }
453 }
454 var result = %StringBuilderConcat(res, len, subject);
455 resultArray.length = 0;
456 reusableReplaceArray = resultArray;
457 return result;
458 }
459
460
461 // Compute the string of a given regular expression capture.
462 function CaptureString(string, lastCaptureInfo, index) {
463 // Scale the index.
464 var scaled = index << 1;
465 // Compute start and end.
466 var start = lastCaptureInfo[CAPTURE(scaled)];
467 // If start isn't valid, return undefined.
468 if (start < 0) return;
469 var end = lastCaptureInfo[CAPTURE(scaled + 1)];
470 return %_SubString(string, start, end);
471 }
472
473
474 function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) {
475 var matchInfo = DoRegExpExec(regexp, subject, 0);
476 if (IS_NULL(matchInfo)) {
477 regexp.lastIndex = 0;
478 return subject;
479 }
480 var index = matchInfo[CAPTURE0];
481 var result = %_SubString(subject, 0, index);
482 var endOfMatch = matchInfo[CAPTURE1];
483 // Compute the parameter list consisting of the match, captures, index,
484 // and subject for the replace function invocation.
485 // The number of captures plus one for the match.
486 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1;
487 var replacement;
488 if (m == 1) {
489 // No captures, only the match, which is always valid.
490 var s = %_SubString(subject, index, endOfMatch);
491 // Don't call directly to avoid exposing the built-in global object.
492 replacement = replace(s, index, subject);
493 } else {
494 var parameters = new InternalArray(m + 2);
495 for (var j = 0; j < m; j++) {
496 parameters[j] = CaptureString(subject, matchInfo, j);
497 }
498 parameters[j] = index;
499 parameters[j + 1] = subject;
500
501 replacement = %Apply(replace, UNDEFINED, parameters, 0, j + 2);
502 }
503
504 result += replacement; // The add method converts to string if necessary.
505 // Can't use matchInfo any more from here, since the function could
506 // overwrite it.
507 return result + %_SubString(subject, endOfMatch, subject.length);
508 }
509
510
511 function RegExpReplace(string, replace) {
512 // TODO(littledan): allow non-regexp receivers.
513 if (!IS_REGEXP(this)) {
514 throw MakeTypeError(kIncompatibleMethodReceiver,
515 "RegExp.prototype.@@replace", this);
516 }
517 var subject = TO_STRING(string);
518 var search = this;
519
520 if (!IS_CALLABLE(replace)) {
521 replace = TO_STRING(replace);
522
523 if (!REGEXP_GLOBAL(search)) {
524 // Non-global regexp search, string replace.
525 var match = DoRegExpExec(search, subject, 0);
526 if (match == null) {
527 search.lastIndex = 0
528 return subject;
529 }
530 if (replace.length == 0) {
531 return %_SubString(subject, 0, match[CAPTURE0]) +
532 %_SubString(subject, match[CAPTURE1], subject.length)
533 }
534 return ExpandReplacement(replace, subject, RegExpLastMatchInfo,
535 %_SubString(subject, 0, match[CAPTURE0])) +
536 %_SubString(subject, match[CAPTURE1], subject.length);
537 }
538
539 // Global regexp search, string replace.
540 search.lastIndex = 0;
541 return %StringReplaceGlobalRegExpWithString(
542 subject, search, replace, RegExpLastMatchInfo);
543 }
544
545 if (REGEXP_GLOBAL(search)) {
546 // Global regexp search, function replace.
547 return StringReplaceGlobalRegExpWithFunction(subject, search, replace);
548 }
549 // Non-global regexp search, function replace.
550 return StringReplaceNonGlobalRegExpWithFunction(subject, search, replace);
551 }
552
553
382 // ES6 21.2.5.9. 554 // ES6 21.2.5.9.
383 function RegExpSearch(string) { 555 function RegExpSearch(string) {
384 // TODO(yangguo): allow non-regexp receivers. 556 // TODO(yangguo): allow non-regexp receivers.
385 if (!IS_REGEXP(this)) { 557 if (!IS_REGEXP(this)) {
386 throw MakeTypeError(kIncompatibleMethodReceiver, 558 throw MakeTypeError(kIncompatibleMethodReceiver,
387 "RegExp.prototype.@@search", this); 559 "RegExp.prototype.@@search", this);
388 } 560 }
389 var match = DoRegExpExec(this, TO_STRING(string), 0); 561 var match = DoRegExpExec(this, TO_STRING(string), 0);
390 if (match) return match[CAPTURE0]; 562 if (match) return match[CAPTURE0];
391 return -1; 563 return -1;
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
504 %AddNamedProperty( 676 %AddNamedProperty(
505 GlobalRegExp.prototype, 'constructor', GlobalRegExp, DONT_ENUM); 677 GlobalRegExp.prototype, 'constructor', GlobalRegExp, DONT_ENUM);
506 %SetCode(GlobalRegExp, RegExpConstructor); 678 %SetCode(GlobalRegExp, RegExpConstructor);
507 679
508 utils.InstallFunctions(GlobalRegExp.prototype, DONT_ENUM, [ 680 utils.InstallFunctions(GlobalRegExp.prototype, DONT_ENUM, [
509 "exec", RegExpExecJS, 681 "exec", RegExpExecJS,
510 "test", RegExpTest, 682 "test", RegExpTest,
511 "toString", RegExpToString, 683 "toString", RegExpToString,
512 "compile", RegExpCompileJS, 684 "compile", RegExpCompileJS,
513 matchSymbol, RegExpMatch, 685 matchSymbol, RegExpMatch,
686 replaceSymbol, RegExpReplace,
514 searchSymbol, RegExpSearch, 687 searchSymbol, RegExpSearch,
515 splitSymbol, RegExpSplit, 688 splitSymbol, RegExpSplit,
516 ]); 689 ]);
517 690
518 utils.InstallGetter(GlobalRegExp.prototype, 'global', RegExpGetGlobal); 691 utils.InstallGetter(GlobalRegExp.prototype, 'global', RegExpGetGlobal);
519 utils.InstallGetter(GlobalRegExp.prototype, 'ignoreCase', RegExpGetIgnoreCase); 692 utils.InstallGetter(GlobalRegExp.prototype, 'ignoreCase', RegExpGetIgnoreCase);
520 utils.InstallGetter(GlobalRegExp.prototype, 'multiline', RegExpGetMultiline); 693 utils.InstallGetter(GlobalRegExp.prototype, 'multiline', RegExpGetMultiline);
521 utils.InstallGetter(GlobalRegExp.prototype, 'source', RegExpGetSource); 694 utils.InstallGetter(GlobalRegExp.prototype, 'source', RegExpGetSource);
522 695
523 // The length of compile is 1 in SpiderMonkey. 696 // The length of compile is 1 in SpiderMonkey.
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
572 // Exports 745 // Exports
573 746
574 utils.Export(function(to) { 747 utils.Export(function(to) {
575 to.RegExpExec = DoRegExpExec; 748 to.RegExpExec = DoRegExpExec;
576 to.RegExpExecNoTests = RegExpExecNoTests; 749 to.RegExpExecNoTests = RegExpExecNoTests;
577 to.RegExpLastMatchInfo = RegExpLastMatchInfo; 750 to.RegExpLastMatchInfo = RegExpLastMatchInfo;
578 to.RegExpTest = RegExpTest; 751 to.RegExpTest = RegExpTest;
579 }); 752 });
580 753
581 }) 754 })
OLDNEW
« no previous file with comments | « no previous file | src/js/string.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698