OLD | NEW |
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; | |
13 var GlobalArray = global.Array; | 12 var GlobalArray = global.Array; |
14 var GlobalObject = global.Object; | 13 var GlobalObject = global.Object; |
15 var GlobalRegExp = global.RegExp; | 14 var GlobalRegExp = global.RegExp; |
16 var GlobalRegExpPrototype; | 15 var GlobalRegExpPrototype; |
17 var InternalArray = utils.InternalArray; | 16 var InternalArray = utils.InternalArray; |
18 var InternalPackedArray = utils.InternalPackedArray; | 17 var InternalPackedArray = utils.InternalPackedArray; |
19 var MaxSimple; | 18 var MaxSimple; |
20 var MinSimple; | 19 var MinSimple; |
21 var matchSymbol = utils.ImportNow("match_symbol"); | 20 var matchSymbol = utils.ImportNow("match_symbol"); |
22 var replaceSymbol = utils.ImportNow("replace_symbol"); | 21 var replaceSymbol = utils.ImportNow("replace_symbol"); |
23 var searchSymbol = utils.ImportNow("search_symbol"); | 22 var searchSymbol = utils.ImportNow("search_symbol"); |
24 var speciesSymbol = utils.ImportNow("species_symbol"); | 23 var speciesSymbol = utils.ImportNow("species_symbol"); |
25 var splitSymbol = utils.ImportNow("split_symbol"); | 24 var splitSymbol = utils.ImportNow("split_symbol"); |
26 var SpeciesConstructor; | 25 var SpeciesConstructor; |
27 | 26 |
28 utils.Import(function(from) { | 27 utils.Import(function(from) { |
29 ExpandReplacement = from.ExpandReplacement; | |
30 MaxSimple = from.MaxSimple; | 28 MaxSimple = from.MaxSimple; |
31 MinSimple = from.MinSimple; | 29 MinSimple = from.MinSimple; |
32 SpeciesConstructor = from.SpeciesConstructor; | 30 SpeciesConstructor = from.SpeciesConstructor; |
33 }); | 31 }); |
34 | 32 |
35 // ------------------------------------------------------------------- | 33 // ------------------------------------------------------------------- |
36 | 34 |
37 // Property of the builtins object for recording the result of the last | 35 // Property of the builtins object for recording the result of the last |
38 // regexp match. The property RegExpLastMatchInfo includes the matchIndices | 36 // regexp match. The property RegExpLastMatchInfo includes the matchIndices |
39 // array of the last successful regexp match (an array of start/end index | 37 // array of the last successful regexp match (an array of start/end index |
(...skipping 643 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
683 | 681 |
684 replacement = %reflect_apply(replace, UNDEFINED, parameters); | 682 replacement = %reflect_apply(replace, UNDEFINED, parameters); |
685 } | 683 } |
686 | 684 |
687 result += replacement; // The add method converts to string if necessary. | 685 result += replacement; // The add method converts to string if necessary. |
688 // Can't use matchInfo any more from here, since the function could | 686 // Can't use matchInfo any more from here, since the function could |
689 // overwrite it. | 687 // overwrite it. |
690 return result + %_SubString(subject, endOfMatch, subject.length); | 688 return result + %_SubString(subject, endOfMatch, subject.length); |
691 } | 689 } |
692 | 690 |
| 691 // Wraps access to matchInfo's captures into a format understood by |
| 692 // GetSubstitution. |
| 693 function MatchInfoCaptureWrapper(matches, subject) { |
| 694 this.length = NUMBER_OF_CAPTURES(matches) >> 1; |
| 695 this.match = matches; |
| 696 this.subject = subject; |
| 697 } |
| 698 |
| 699 MatchInfoCaptureWrapper.prototype.at = function(ix) { |
| 700 const match = this.match; |
| 701 const start = match[CAPTURE(ix << 1)]; |
| 702 if (start < 0) return UNDEFINED; |
| 703 return %_SubString(this.subject, start, match[CAPTURE((ix << 1) + 1)]); |
| 704 }; |
| 705 %SetForceInlineFlag(MatchInfoCaptureWrapper.prototype.at); |
| 706 |
| 707 function ArrayCaptureWrapper(array) { |
| 708 this.length = array.length; |
| 709 this.array = array; |
| 710 } |
| 711 |
| 712 ArrayCaptureWrapper.prototype.at = function(ix) { |
| 713 return this.array[ix]; |
| 714 }; |
| 715 %SetForceInlineFlag(ArrayCaptureWrapper.prototype.at); |
693 | 716 |
694 function RegExpReplace(string, replace) { | 717 function RegExpReplace(string, replace) { |
695 if (!IS_REGEXP(this)) { | 718 if (!IS_REGEXP(this)) { |
696 throw %make_type_error(kIncompatibleMethodReceiver, | 719 throw %make_type_error(kIncompatibleMethodReceiver, |
697 "RegExp.prototype.@@replace", this); | 720 "RegExp.prototype.@@replace", this); |
698 } | 721 } |
699 var subject = TO_STRING(string); | 722 var subject = TO_STRING(string); |
700 var search = this; | 723 var search = this; |
701 | 724 |
702 if (!IS_CALLABLE(replace)) { | 725 if (!IS_CALLABLE(replace)) { |
703 replace = TO_STRING(replace); | 726 replace = TO_STRING(replace); |
704 | 727 |
705 if (!REGEXP_GLOBAL(search)) { | 728 if (!REGEXP_GLOBAL(search)) { |
706 // Non-global regexp search, string replace. | 729 // Non-global regexp search, string replace. |
707 var match = DoRegExpExec(search, subject, 0); | 730 var match = DoRegExpExec(search, subject, 0); |
708 if (match == null) { | 731 if (match == null) { |
709 search.lastIndex = 0 | 732 search.lastIndex = 0 |
710 return subject; | 733 return subject; |
711 } | 734 } |
712 if (replace.length == 0) { | 735 if (replace.length == 0) { |
713 return %_SubString(subject, 0, match[CAPTURE0]) + | 736 return %_SubString(subject, 0, match[CAPTURE0]) + |
714 %_SubString(subject, match[CAPTURE1], subject.length) | 737 %_SubString(subject, match[CAPTURE1], subject.length) |
715 } | 738 } |
716 return ExpandReplacement(replace, subject, RegExpLastMatchInfo, | 739 const captures = new MatchInfoCaptureWrapper(match, subject); |
717 %_SubString(subject, 0, match[CAPTURE0])) + | 740 const start = match[CAPTURE0]; |
718 %_SubString(subject, match[CAPTURE1], subject.length); | 741 const end = match[CAPTURE1]; |
| 742 |
| 743 const prefix = %_SubString(subject, 0, start); |
| 744 const matched = %_SubString(subject, start, end); |
| 745 const suffix = %_SubString(subject, end, subject.length); |
| 746 |
| 747 return prefix + |
| 748 GetSubstitution(matched, subject, start, captures, replace) + |
| 749 suffix; |
719 } | 750 } |
720 | 751 |
721 // Global regexp search, string replace. | 752 // Global regexp search, string replace. |
722 search.lastIndex = 0; | 753 search.lastIndex = 0; |
723 return %StringReplaceGlobalRegExpWithString( | 754 return %StringReplaceGlobalRegExpWithString( |
724 subject, search, replace, RegExpLastMatchInfo); | 755 subject, search, replace, RegExpLastMatchInfo); |
725 } | 756 } |
726 | 757 |
727 if (REGEXP_GLOBAL(search)) { | 758 if (REGEXP_GLOBAL(search)) { |
728 // Global regexp search, function replace. | 759 // Global regexp search, function replace. |
729 return StringReplaceGlobalRegExpWithFunction(subject, search, replace); | 760 return StringReplaceGlobalRegExpWithFunction(subject, search, replace); |
730 } | 761 } |
731 // Non-global regexp search, function replace. | 762 // Non-global regexp search, function replace. |
732 return StringReplaceNonGlobalRegExpWithFunction(subject, search, replace); | 763 return StringReplaceNonGlobalRegExpWithFunction(subject, search, replace); |
733 } | 764 } |
734 | 765 |
735 | 766 |
736 // ES#sec-getsubstitution | 767 // ES#sec-getsubstitution |
737 // GetSubstitution(matched, str, position, captures, replacement) | 768 // GetSubstitution(matched, str, position, captures, replacement) |
738 // Expand the $-expressions in the string and return a new string with | 769 // Expand the $-expressions in the string and return a new string with |
739 // the result. | 770 // the result. |
740 // TODO(littledan): Call this function from String.prototype.replace instead | |
741 // of the very similar ExpandReplacement in src/js/string.js | |
742 function GetSubstitution(matched, string, position, captures, replacement) { | 771 function GetSubstitution(matched, string, position, captures, replacement) { |
743 var matchLength = matched.length; | 772 var matchLength = matched.length; |
744 var stringLength = string.length; | 773 var stringLength = string.length; |
745 var capturesLength = captures.length; | 774 var capturesLength = captures.length; |
746 var tailPos = position + matchLength; | 775 var tailPos = position + matchLength; |
747 var result = ""; | 776 var result = ""; |
748 var pos, expansion, peek, next, scaledIndex, advance, newScaledIndex; | 777 var pos, expansion, peek, next, scaledIndex, advance, newScaledIndex; |
749 | 778 |
750 var next = %StringIndexOf(replacement, '$', 0); | 779 var next = %StringIndexOf(replacement, '$', 0); |
751 if (next < 0) { | 780 if (next < 0) { |
(...skipping 28 matching lines...) Expand all Loading... |
780 next = %_StringCharCodeAt(replacement, pos + 1); | 809 next = %_StringCharCodeAt(replacement, pos + 1); |
781 if (next >= 48 && next <= 57) { | 810 if (next >= 48 && next <= 57) { |
782 newScaledIndex = scaledIndex * 10 + ((next - 48)); | 811 newScaledIndex = scaledIndex * 10 + ((next - 48)); |
783 if (newScaledIndex < capturesLength) { | 812 if (newScaledIndex < capturesLength) { |
784 scaledIndex = newScaledIndex; | 813 scaledIndex = newScaledIndex; |
785 advance = 2; | 814 advance = 2; |
786 } | 815 } |
787 } | 816 } |
788 } | 817 } |
789 if (scaledIndex != 0 && scaledIndex < capturesLength) { | 818 if (scaledIndex != 0 && scaledIndex < capturesLength) { |
790 var capture = captures[scaledIndex]; | 819 var capture = captures.at(scaledIndex); |
791 if (!IS_UNDEFINED(capture)) result += capture; | 820 if (!IS_UNDEFINED(capture)) result += capture; |
792 pos += advance; | 821 pos += advance; |
793 } else { | 822 } else { |
794 result += '$'; | 823 result += '$'; |
795 } | 824 } |
796 } else { | 825 } else { |
797 result += '$'; | 826 result += '$'; |
798 } | 827 } |
799 } else { | 828 } else { |
800 result += '$'; | 829 result += '$'; |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
904 if (functionalReplace) { | 933 if (functionalReplace) { |
905 var parameters = new InternalArray(capturesLength + 2); | 934 var parameters = new InternalArray(capturesLength + 2); |
906 for (var j = 0; j < capturesLength; j++) { | 935 for (var j = 0; j < capturesLength; j++) { |
907 parameters[j] = captures[j]; | 936 parameters[j] = captures[j]; |
908 } | 937 } |
909 parameters[j] = position; | 938 parameters[j] = position; |
910 parameters[j + 1] = string; | 939 parameters[j + 1] = string; |
911 replacement = %reflect_apply(replace, UNDEFINED, parameters, 0, | 940 replacement = %reflect_apply(replace, UNDEFINED, parameters, 0, |
912 parameters.length); | 941 parameters.length); |
913 } else { | 942 } else { |
914 replacement = GetSubstitution(matched, string, position, captures, | 943 const capturesWrapper = new ArrayCaptureWrapper(captures); |
| 944 replacement = GetSubstitution(matched, string, position, capturesWrapper, |
915 replace); | 945 replace); |
916 } | 946 } |
917 if (position >= nextSourcePosition) { | 947 if (position >= nextSourcePosition) { |
918 accumulatedResult += | 948 accumulatedResult += |
919 %_SubString(string, nextSourcePosition, position) + replacement; | 949 %_SubString(string, nextSourcePosition, position) + replacement; |
920 nextSourcePosition = position + matchedLength; | 950 nextSourcePosition = position + matchedLength; |
921 } | 951 } |
922 } | 952 } |
923 if (nextSourcePosition >= length) return accumulatedResult; | 953 if (nextSourcePosition >= length) return accumulatedResult; |
924 return accumulatedResult + %_SubString(string, nextSourcePosition, length); | 954 return accumulatedResult + %_SubString(string, nextSourcePosition, length); |
(...skipping 287 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1212 | 1242 |
1213 function InternalRegExpReplace(regexp, subject, replacement) { | 1243 function InternalRegExpReplace(regexp, subject, replacement) { |
1214 return %StringReplaceGlobalRegExpWithString( | 1244 return %StringReplaceGlobalRegExpWithString( |
1215 subject, regexp, replacement, InternalRegExpMatchInfo); | 1245 subject, regexp, replacement, InternalRegExpMatchInfo); |
1216 } | 1246 } |
1217 | 1247 |
1218 // ------------------------------------------------------------------- | 1248 // ------------------------------------------------------------------- |
1219 // Exports | 1249 // Exports |
1220 | 1250 |
1221 utils.Export(function(to) { | 1251 utils.Export(function(to) { |
| 1252 to.GetSubstitution = GetSubstitution; |
1222 to.InternalRegExpMatch = InternalRegExpMatch; | 1253 to.InternalRegExpMatch = InternalRegExpMatch; |
1223 to.InternalRegExpReplace = InternalRegExpReplace; | 1254 to.InternalRegExpReplace = InternalRegExpReplace; |
1224 to.IsRegExp = IsRegExp; | 1255 to.IsRegExp = IsRegExp; |
1225 to.RegExpExec = DoRegExpExec; | 1256 to.RegExpExec = DoRegExpExec; |
1226 to.RegExpInitialize = RegExpInitialize; | 1257 to.RegExpInitialize = RegExpInitialize; |
1227 to.RegExpLastMatchInfo = RegExpLastMatchInfo; | 1258 to.RegExpLastMatchInfo = RegExpLastMatchInfo; |
1228 to.RegExpTest = RegExpTest; | 1259 to.RegExpTest = RegExpTest; |
1229 }); | 1260 }); |
1230 | 1261 |
1231 }) | 1262 }) |
OLD | NEW |