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; | 12 var ExpandReplacement; |
13 var GlobalArray = global.Array; | 13 var GlobalArray = global.Array; |
14 var GlobalObject = global.Object; | 14 var GlobalObject = global.Object; |
15 var GlobalRegExp = global.RegExp; | 15 var GlobalRegExp = global.RegExp; |
16 var InternalArray = utils.InternalArray; | 16 var InternalArray = utils.InternalArray; |
17 var InternalPackedArray = utils.InternalPackedArray; | 17 var InternalPackedArray = utils.InternalPackedArray; |
18 var MaxSimple; | 18 var MaxSimple; |
19 var MinSimple; | 19 var MinSimple; |
20 var lastMatchInfoSymbol = utils.ImportNow("regexp_last_match_info_symbol"); | 20 var lastMatchInfoSymbol = utils.ImportNow("regexp_last_match_info_symbol"); |
21 var matchSymbol = utils.ImportNow("match_symbol"); | 21 var matchSymbol = utils.ImportNow("match_symbol"); |
22 var replaceSymbol = utils.ImportNow("replace_symbol"); | 22 var replaceSymbol = utils.ImportNow("replace_symbol"); |
23 var searchSymbol = utils.ImportNow("search_symbol"); | 23 var searchSymbol = utils.ImportNow("search_symbol"); |
24 var speciesSymbol = utils.ImportNow("species_symbol"); | 24 var speciesSymbol = utils.ImportNow("species_symbol"); |
25 var splitSymbol = utils.ImportNow("split_symbol"); | 25 var splitSymbol = utils.ImportNow("split_symbol"); |
26 var SpeciesConstructor; | 26 var SpeciesConstructor; |
| 27 var RegExpSubclassExecJS; |
27 | 28 |
28 utils.Import(function(from) { | 29 utils.Import(function(from) { |
29 ExpandReplacement = from.ExpandReplacement; | 30 ExpandReplacement = from.ExpandReplacement; |
30 MaxSimple = from.MaxSimple; | 31 MaxSimple = from.MaxSimple; |
31 MinSimple = from.MinSimple; | 32 MinSimple = from.MinSimple; |
32 SpeciesConstructor = from.SpeciesConstructor; | 33 SpeciesConstructor = from.SpeciesConstructor; |
33 }); | 34 }); |
34 | 35 |
35 // ------------------------------------------------------------------- | 36 // ------------------------------------------------------------------- |
36 | 37 |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
95 if (start != -1) { | 96 if (start != -1) { |
96 end = MATCHINFO[j]; | 97 end = MATCHINFO[j]; |
97 result[i] = %_SubString(STRING, start, end); | 98 result[i] = %_SubString(STRING, start, end); |
98 } | 99 } |
99 j++; | 100 j++; |
100 } | 101 } |
101 return result; | 102 return result; |
102 endmacro | 103 endmacro |
103 | 104 |
104 | 105 |
105 function RegExpExecNoTests(regexp, string, start) { | |
106 // Must be called with RegExp, string and positive integer as arguments. | |
107 var matchInfo = %_RegExpExec(regexp, string, start, RegExpLastMatchInfo); | |
108 if (matchInfo !== null) { | |
109 // ES6 21.2.5.2.2 step 18. | |
110 if (REGEXP_STICKY(regexp)) regexp.lastIndex = matchInfo[CAPTURE1]; | |
111 RETURN_NEW_RESULT_FROM_MATCH_INFO(matchInfo, string); | |
112 } | |
113 regexp.lastIndex = 0; | |
114 return null; | |
115 } | |
116 | |
117 | |
118 // ES#sec-regexp.prototype.exec | |
119 // RegExp.prototype.exec ( string ) | |
120 function RegExpSubclassExecJS(string) { | |
121 if (!IS_REGEXP(this)) { | |
122 throw %make_type_error(kIncompatibleMethodReceiver, | |
123 'RegExp.prototype.exec', this); | |
124 } | |
125 | |
126 string = TO_STRING(string); | |
127 var lastIndex = this.lastIndex; | |
128 | |
129 // Conversion is required by the ES2015 specification (RegExpBuiltinExec | |
130 // algorithm, step 4) even if the value is discarded for non-global RegExps. | |
131 var i = TO_LENGTH(lastIndex); | |
132 | |
133 var global = TO_BOOLEAN(REGEXP_GLOBAL(this)); | |
134 var sticky = TO_BOOLEAN(REGEXP_STICKY(this)); | |
135 var updateLastIndex = global || sticky; | |
136 if (updateLastIndex) { | |
137 if (i > string.length) { | |
138 this.lastIndex = 0; | |
139 return null; | |
140 } | |
141 } else { | |
142 i = 0; | |
143 } | |
144 | |
145 // matchIndices is either null or the RegExpLastMatchInfo array. | |
146 // TODO(littledan): Whether a RegExp is sticky is compiled into the RegExp | |
147 // itself, but ES2015 allows monkey-patching this property to differ from | |
148 // the internal flags. If it differs, recompile a different RegExp? | |
149 var matchIndices = %_RegExpExec(this, string, i, RegExpLastMatchInfo); | |
150 | |
151 if (IS_NULL(matchIndices)) { | |
152 this.lastIndex = 0; | |
153 return null; | |
154 } | |
155 | |
156 // Successful match. | |
157 if (updateLastIndex) { | |
158 this.lastIndex = RegExpLastMatchInfo[CAPTURE1]; | |
159 } | |
160 RETURN_NEW_RESULT_FROM_MATCH_INFO(matchIndices, string); | |
161 } | |
162 %FunctionRemovePrototype(RegExpSubclassExecJS); | |
163 | |
164 | |
165 // ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S ) | 106 // ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S ) |
166 // Also takes an optional exec method in case our caller | 107 // Also takes an optional exec method in case our caller |
167 // has already fetched exec. | 108 // has already fetched exec. |
168 function RegExpSubclassExec(regexp, string, exec) { | 109 function RegExpSubclassExec(regexp, string, exec) { |
169 if (IS_UNDEFINED(exec)) { | 110 if (IS_UNDEFINED(exec)) { |
170 exec = regexp.exec; | 111 exec = regexp.exec; |
171 } | 112 } |
172 if (IS_CALLABLE(exec)) { | 113 if (IS_CALLABLE(exec)) { |
173 var result = %_Call(exec, regexp, string); | 114 var result = %_Call(exec, regexp, string); |
174 if (!IS_RECEIVER(result) && !IS_NULL(result)) { | 115 if (!IS_RECEIVER(result) && !IS_NULL(result)) { |
175 throw %make_type_error(kInvalidRegExpExecResult); | 116 throw %make_type_error(kInvalidRegExpExecResult); |
176 } | 117 } |
177 return result; | 118 return result; |
178 } | 119 } |
179 return %_Call(RegExpSubclassExecJS, regexp, string); | 120 return %_Call(RegExpSubclassExecJS, regexp, string); |
180 } | 121 } |
181 %SetForceInlineFlag(RegExpSubclassExec); | 122 %SetForceInlineFlag(RegExpSubclassExec); |
182 | 123 |
183 | 124 |
184 // ES#sec-regexp.prototype.test RegExp.prototype.test ( S ) | |
185 function RegExpSubclassTest(string) { | |
186 if (!IS_RECEIVER(this)) { | |
187 throw %make_type_error(kIncompatibleMethodReceiver, | |
188 'RegExp.prototype.test', this); | |
189 } | |
190 string = TO_STRING(string); | |
191 var match = RegExpSubclassExec(this, string); | |
192 return !IS_NULL(match); | |
193 } | |
194 %FunctionRemovePrototype(RegExpSubclassTest); | |
195 | |
196 | |
197 function AtSurrogatePair(subject, index) { | 125 function AtSurrogatePair(subject, index) { |
198 if (index + 1 >= subject.length) return false; | 126 if (index + 1 >= subject.length) return false; |
199 var first = %_StringCharCodeAt(subject, index); | 127 var first = %_StringCharCodeAt(subject, index); |
200 if (first < 0xD800 || first > 0xDBFF) return false; | 128 if (first < 0xD800 || first > 0xDBFF) return false; |
201 var second = %_StringCharCodeAt(subject, index + 1); | 129 var second = %_StringCharCodeAt(subject, index + 1); |
202 return second >= 0xDC00 || second <= 0xDFFF; | 130 return second >= 0xDC00 || second <= 0xDFFF; |
203 } | 131 } |
204 | 132 |
205 | 133 |
206 // Legacy implementation of RegExp.prototype[Symbol.split] which | 134 // Legacy implementation of RegExp.prototype[Symbol.split] which |
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
347 } | 275 } |
348 } | 276 } |
349 } | 277 } |
350 %AddElement(array, arrayIndex, | 278 %AddElement(array, arrayIndex, |
351 %_SubString(string, prevStringIndex, size)); | 279 %_SubString(string, prevStringIndex, size)); |
352 return array; | 280 return array; |
353 } | 281 } |
354 %FunctionRemovePrototype(RegExpSubclassSplit); | 282 %FunctionRemovePrototype(RegExpSubclassSplit); |
355 | 283 |
356 | 284 |
357 // ES#sec-regexp.prototype-@@match | |
358 // RegExp.prototype [ @@match ] ( string ) | |
359 function RegExpSubclassMatch(string) { | |
360 if (!IS_RECEIVER(this)) { | |
361 throw %make_type_error(kIncompatibleMethodReceiver, | |
362 "RegExp.prototype.@@match", this); | |
363 } | |
364 string = TO_STRING(string); | |
365 var global = this.global; | |
366 if (!global) return RegExpSubclassExec(this, string); | |
367 var unicode = this.unicode; | |
368 this.lastIndex = 0; | |
369 var array = new InternalArray(); | |
370 var n = 0; | |
371 var result; | |
372 while (true) { | |
373 result = RegExpSubclassExec(this, string); | |
374 if (IS_NULL(result)) { | |
375 if (n === 0) return null; | |
376 break; | |
377 } | |
378 var matchStr = TO_STRING(result[0]); | |
379 array[n] = matchStr; | |
380 if (matchStr === "") SetAdvancedStringIndex(this, string, unicode); | |
381 n++; | |
382 } | |
383 var resultArray = []; | |
384 %MoveArrayContents(array, resultArray); | |
385 return resultArray; | |
386 } | |
387 %FunctionRemovePrototype(RegExpSubclassMatch); | |
388 | |
389 | |
390 // Legacy implementation of RegExp.prototype[Symbol.replace] which | 285 // Legacy implementation of RegExp.prototype[Symbol.replace] which |
391 // doesn't properly call the underlying exec method. | 286 // doesn't properly call the underlying exec method. |
392 | 287 |
393 // TODO(lrn): This array will survive indefinitely if replace is never | 288 // TODO(lrn): This array will survive indefinitely if replace is never |
394 // called again. However, it will be empty, since the contents are cleared | 289 // called again. However, it will be empty, since the contents are cleared |
395 // in the finally block. | 290 // in the finally block. |
396 var reusableReplaceArray = new InternalArray(4); | 291 var reusableReplaceArray = new InternalArray(4); |
397 | 292 |
398 // Helper function for replacing regular expressions with the result of a | 293 // Helper function for replacing regular expressions with the result of a |
399 // function application in String.prototype.replace. | 294 // function application in String.prototype.replace. |
(...skipping 346 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
746 %_SubString(string, nextSourcePosition, position) + replacement; | 641 %_SubString(string, nextSourcePosition, position) + replacement; |
747 nextSourcePosition = position + matchedLength; | 642 nextSourcePosition = position + matchedLength; |
748 } | 643 } |
749 } | 644 } |
750 if (nextSourcePosition >= length) return accumulatedResult; | 645 if (nextSourcePosition >= length) return accumulatedResult; |
751 return accumulatedResult + %_SubString(string, nextSourcePosition, length); | 646 return accumulatedResult + %_SubString(string, nextSourcePosition, length); |
752 } | 647 } |
753 %FunctionRemovePrototype(RegExpSubclassReplace); | 648 %FunctionRemovePrototype(RegExpSubclassReplace); |
754 | 649 |
755 | 650 |
756 // ES#sec-regexp.prototype-@@search | |
757 // RegExp.prototype [ @@search ] ( string ) | |
758 function RegExpSubclassSearch(string) { | |
759 if (!IS_RECEIVER(this)) { | |
760 throw %make_type_error(kIncompatibleMethodReceiver, | |
761 "RegExp.prototype.@@search", this); | |
762 } | |
763 string = TO_STRING(string); | |
764 var previousLastIndex = this.lastIndex; | |
765 this.lastIndex = 0; | |
766 var result = RegExpSubclassExec(this, string); | |
767 this.lastIndex = previousLastIndex; | |
768 if (IS_NULL(result)) return -1; | |
769 return result.index; | |
770 } | |
771 %FunctionRemovePrototype(RegExpSubclassSearch); | |
772 | |
773 | |
774 // ------------------------------------------------------------------- | 651 // ------------------------------------------------------------------- |
775 | 652 |
776 utils.InstallFunctions(GlobalRegExp.prototype, DONT_ENUM, [ | 653 utils.InstallFunctions(GlobalRegExp.prototype, DONT_ENUM, [ |
777 "exec", RegExpSubclassExecJS, | |
778 "test", RegExpSubclassTest, | |
779 matchSymbol, RegExpSubclassMatch, | |
780 replaceSymbol, RegExpSubclassReplace, | 654 replaceSymbol, RegExpSubclassReplace, |
781 searchSymbol, RegExpSubclassSearch, | |
782 splitSymbol, RegExpSubclassSplit, | 655 splitSymbol, RegExpSubclassSplit, |
783 ]); | 656 ]); |
784 | 657 |
785 // Temporary until all RegExpLastMatchInfo accesses are ported to C++. | 658 // Temporary until all RegExpLastMatchInfo accesses are ported to C++. |
786 SET_PRIVATE(GlobalRegExp, lastMatchInfoSymbol, RegExpLastMatchInfo); | 659 SET_PRIVATE(GlobalRegExp, lastMatchInfoSymbol, RegExpLastMatchInfo); |
787 | 660 |
| 661 var RegExpSubclassExecJS = GlobalRegExp.prototype.exec; |
| 662 |
788 // ------------------------------------------------------------------- | 663 // ------------------------------------------------------------------- |
789 // Internal | 664 // Internal |
790 | 665 |
791 var InternalRegExpMatchInfo = { | 666 var InternalRegExpMatchInfo = { |
792 REGEXP_NUMBER_OF_CAPTURES: 2, | 667 REGEXP_NUMBER_OF_CAPTURES: 2, |
793 REGEXP_LAST_SUBJECT: "", | 668 REGEXP_LAST_SUBJECT: "", |
794 REGEXP_LAST_INPUT: UNDEFINED, | 669 REGEXP_LAST_INPUT: UNDEFINED, |
795 CAPTURE0: 0, | 670 CAPTURE0: 0, |
796 CAPTURE1: 0 | 671 CAPTURE1: 0 |
797 }; | 672 }; |
(...skipping 15 matching lines...) Expand all Loading... |
813 // Exports | 688 // Exports |
814 | 689 |
815 utils.Export(function(to) { | 690 utils.Export(function(to) { |
816 to.InternalRegExpMatch = InternalRegExpMatch; | 691 to.InternalRegExpMatch = InternalRegExpMatch; |
817 to.InternalRegExpReplace = InternalRegExpReplace; | 692 to.InternalRegExpReplace = InternalRegExpReplace; |
818 to.IsRegExp = IsRegExp; | 693 to.IsRegExp = IsRegExp; |
819 to.RegExpInitialize = RegExpInitialize; | 694 to.RegExpInitialize = RegExpInitialize; |
820 }); | 695 }); |
821 | 696 |
822 }) | 697 }) |
OLD | NEW |