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 'use strict'; | 7 'use strict'; |
8 | 8 |
9 %CheckIsBootstrapping(); | 9 %CheckIsBootstrapping(); |
10 | 10 |
11 // ------------------------------------------------------------------- | 11 // ------------------------------------------------------------------- |
12 // Imports | 12 // Imports |
13 | 13 |
14 var AddIndexedProperty; | |
14 var ExpandReplacement; | 15 var ExpandReplacement; |
16 var GlobalArray = global.Array; | |
15 var GlobalObject = global.Object; | 17 var GlobalObject = global.Object; |
16 var GlobalRegExp = global.RegExp; | 18 var GlobalRegExp = global.RegExp; |
17 var GlobalRegExpPrototype; | 19 var GlobalRegExpPrototype; |
18 var InternalArray = utils.InternalArray; | 20 var InternalArray = utils.InternalArray; |
19 var InternalPackedArray = utils.InternalPackedArray; | 21 var InternalPackedArray = utils.InternalPackedArray; |
20 var MakeTypeError; | 22 var MakeTypeError; |
23 var MathMax = global.Math.max; | |
24 var MathMin = global.Math.min; | |
21 var matchSymbol = utils.ImportNow("match_symbol"); | 25 var matchSymbol = utils.ImportNow("match_symbol"); |
22 var replaceSymbol = utils.ImportNow("replace_symbol"); | 26 var replaceSymbol = utils.ImportNow("replace_symbol"); |
23 var searchSymbol = utils.ImportNow("search_symbol"); | 27 var searchSymbol = utils.ImportNow("search_symbol"); |
24 var splitSymbol = utils.ImportNow("split_symbol"); | 28 var splitSymbol = utils.ImportNow("split_symbol"); |
29 var SpeciesConstructor; | |
25 | 30 |
26 utils.Import(function(from) { | 31 utils.Import(function(from) { |
32 AddIndexedProperty = from.AddIndexedProperty; | |
27 ExpandReplacement = from.ExpandReplacement; | 33 ExpandReplacement = from.ExpandReplacement; |
28 MakeTypeError = from.MakeTypeError; | 34 MakeTypeError = from.MakeTypeError; |
35 SpeciesConstructor = from.SpeciesConstructor; | |
29 }); | 36 }); |
30 | 37 |
31 // ------------------------------------------------------------------- | 38 // ------------------------------------------------------------------- |
32 | 39 |
33 // Property of the builtins object for recording the result of the last | 40 // Property of the builtins object for recording the result of the last |
34 // regexp match. The property RegExpLastMatchInfo includes the matchIndices | 41 // regexp match. The property RegExpLastMatchInfo includes the matchIndices |
35 // array of the last successful regexp match (an array of start/end index | 42 // 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 | 43 // 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 | 44 // that there are at least two capture indeces. The array also contains |
38 // the subject string for the last successful match. | 45 // the subject string for the last successful match. |
39 var RegExpLastMatchInfo = new InternalPackedArray( | 46 var RegExpLastMatchInfo = new InternalPackedArray( |
40 2, // REGEXP_NUMBER_OF_CAPTURES | 47 2, // REGEXP_NUMBER_OF_CAPTURES |
41 "", // Last subject. | 48 "", // Last subject. |
42 UNDEFINED, // Last input - settable with RegExpSetInput. | 49 UNDEFINED, // Last input - settable with RegExpSetInput. |
43 0, // REGEXP_FIRST_CAPTURE + 0 | 50 0, // REGEXP_FIRST_CAPTURE + 0 |
44 0 // REGEXP_FIRST_CAPTURE + 1 | 51 0 // REGEXP_FIRST_CAPTURE + 1 |
45 ); | 52 ); |
46 | 53 |
47 // ------------------------------------------------------------------- | 54 // ------------------------------------------------------------------- |
48 | 55 |
56 // ES6 7.2.8 | |
49 function IsRegExp(o) { | 57 function IsRegExp(o) { |
50 if (!IS_RECEIVER(o)) return false; | 58 if (!IS_RECEIVER(o)) return false; |
51 var is_regexp = o[matchSymbol]; | 59 var is_regexp = o[matchSymbol]; |
52 if (!IS_UNDEFINED(is_regexp)) return TO_BOOLEAN(is_regexp); | 60 if (!IS_UNDEFINED(is_regexp)) return TO_BOOLEAN(is_regexp); |
53 return IS_REGEXP(o); | 61 return IS_REGEXP(o); |
54 } | 62 } |
55 | 63 |
56 | 64 |
57 // ES6 section 21.2.3.2.2 | 65 // ES6 section 21.2.3.2.2 |
58 function RegExpInitialize(object, pattern, flags) { | 66 function RegExpInitialize(object, pattern, flags) { |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
158 if (matchInfo !== null) { | 166 if (matchInfo !== null) { |
159 // ES6 21.2.5.2.2 step 18. | 167 // ES6 21.2.5.2.2 step 18. |
160 if (REGEXP_STICKY(regexp)) regexp.lastIndex = matchInfo[CAPTURE1]; | 168 if (REGEXP_STICKY(regexp)) regexp.lastIndex = matchInfo[CAPTURE1]; |
161 RETURN_NEW_RESULT_FROM_MATCH_INFO(matchInfo, string); | 169 RETURN_NEW_RESULT_FROM_MATCH_INFO(matchInfo, string); |
162 } | 170 } |
163 regexp.lastIndex = 0; | 171 regexp.lastIndex = 0; |
164 return null; | 172 return null; |
165 } | 173 } |
166 | 174 |
167 | 175 |
176 function RegExpSubclassExecJS(string) { | |
177 if (!IS_REGEXP(this)) { | |
178 throw MakeTypeError(kIncompatibleMethodReceiver, | |
179 'RegExp.prototype.exec', this); | |
180 } | |
181 | |
182 string = TO_STRING(string); | |
183 var lastIndex = this.lastIndex; | |
184 | |
185 // Conversion is required by the ES2015 specification (RegExpBuiltinExec | |
186 // algorithm, step 4) even if the value is discarded for non-global RegExps. | |
187 var i = TO_LENGTH(lastIndex); | |
188 | |
189 var global = TO_BOOLEAN(this.global); | |
190 var sticky = TO_BOOLEAN(this.sticky); | |
191 var updateLastIndex = global || sticky; | |
192 if (updateLastIndex) { | |
193 if (i < 0 || i > string.length) { | |
194 this.lastIndex = 0; | |
195 return null; | |
196 } | |
197 } else { | |
198 i = 0; | |
199 } | |
200 | |
201 // matchIndices is either null or the RegExpLastMatchInfo array. | |
202 // TODO(littledan): Whether a RegExp is sticky is compiled into the RegExp | |
203 // itself, but ES2015 allows monkey-patching this property to differ from | |
204 // the internal flags. If it differs, recompile a different RegExp? | |
205 var matchIndices = %_RegExpExec(this, string, i, RegExpLastMatchInfo); | |
206 | |
207 if (IS_NULL(matchIndices)) { | |
208 this.lastIndex = 0; | |
209 return null; | |
210 } | |
211 | |
212 // Successful match. | |
213 if (updateLastIndex) { | |
214 this.lastIndex = RegExpLastMatchInfo[CAPTURE1]; | |
215 } | |
216 RETURN_NEW_RESULT_FROM_MATCH_INFO(matchIndices, string); | |
217 } | |
218 %FunctionRemovePrototype(RegExpSubclassExecJS); | |
Yang
2016/03/22 12:49:38
Like previously noted, let's always do this in Ove
Dan Ehrenberg
2016/03/22 18:26:31
See v8:4567 for the bug that this is working aroun
| |
219 | |
220 | |
168 function RegExpExecJS(string) { | 221 function RegExpExecJS(string) { |
169 if (!IS_REGEXP(this)) { | 222 if (!IS_REGEXP(this)) { |
170 throw MakeTypeError(kIncompatibleMethodReceiver, | 223 throw MakeTypeError(kIncompatibleMethodReceiver, |
171 'RegExp.prototype.exec', this); | 224 'RegExp.prototype.exec', this); |
172 } | 225 } |
173 | 226 |
174 string = TO_STRING(string); | 227 string = TO_STRING(string); |
175 var lastIndex = this.lastIndex; | 228 var lastIndex = this.lastIndex; |
176 | 229 |
177 // Conversion is required by the ES2015 specification (RegExpBuiltinExec | 230 // Conversion is required by the ES2015 specification (RegExpBuiltinExec |
(...skipping 19 matching lines...) Expand all Loading... | |
197 } | 250 } |
198 | 251 |
199 // Successful match. | 252 // Successful match. |
200 if (updateLastIndex) { | 253 if (updateLastIndex) { |
201 this.lastIndex = RegExpLastMatchInfo[CAPTURE1]; | 254 this.lastIndex = RegExpLastMatchInfo[CAPTURE1]; |
202 } | 255 } |
203 RETURN_NEW_RESULT_FROM_MATCH_INFO(matchIndices, string); | 256 RETURN_NEW_RESULT_FROM_MATCH_INFO(matchIndices, string); |
204 } | 257 } |
205 | 258 |
206 | 259 |
260 function RegExpSubclassExec(regexp, string) { | |
261 var exec = regexp.exec; | |
262 if (IS_CALLABLE(exec)) { | |
263 var result = %_Call(exec, regexp, string); | |
264 if (!IS_OBJECT(result) && !IS_NULL(result)) { | |
265 throw MakeTypeError(kInvalidRegExpExecResult); | |
266 } | |
267 return result; | |
268 } | |
269 return %_Call(RegExpExecJS, regexp, string); | |
270 } | |
271 | |
272 | |
207 // One-element cache for the simplified test regexp. | 273 // One-element cache for the simplified test regexp. |
208 var regexp_key; | 274 var regexp_key; |
209 var regexp_val; | 275 var regexp_val; |
210 | 276 |
211 // Section 15.10.6.3 doesn't actually make sense, but the intention seems to be | 277 // Section 15.10.6.3 doesn't actually make sense, but the intention seems to be |
212 // that test is defined in terms of String.prototype.exec. However, it probably | 278 // that test is defined in terms of String.prototype.exec. However, it probably |
213 // means the original value of String.prototype.exec, which is what everybody | 279 // means the original value of String.prototype.exec, which is what everybody |
214 // else implements. | 280 // else implements. |
215 function RegExpTest(string) { | 281 function RegExpTest(string) { |
216 if (!IS_REGEXP(this)) { | 282 if (!IS_REGEXP(this)) { |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
254 // matchIndices is either null or the RegExpLastMatchInfo array. | 320 // matchIndices is either null or the RegExpLastMatchInfo array. |
255 var matchIndices = %_RegExpExec(regexp, string, 0, RegExpLastMatchInfo); | 321 var matchIndices = %_RegExpExec(regexp, string, 0, RegExpLastMatchInfo); |
256 if (IS_NULL(matchIndices)) { | 322 if (IS_NULL(matchIndices)) { |
257 this.lastIndex = 0; | 323 this.lastIndex = 0; |
258 return false; | 324 return false; |
259 } | 325 } |
260 return true; | 326 return true; |
261 } | 327 } |
262 } | 328 } |
263 | 329 |
330 function RegExpSubclassTest(string) { | |
331 if (!IS_OBJECT(this)) { | |
332 throw MakeTypeError(kIncompatibleMethodReceiver, | |
333 'RegExp.prototype.test', this); | |
334 } | |
335 string = TO_STRING(string); | |
336 var match = RegExpSubclassExec(this, string); | |
337 return !IS_NULL(match); | |
338 } | |
339 %FunctionRemovePrototype(RegExpSubclassTest); | |
340 | |
264 function TrimRegExp(regexp) { | 341 function TrimRegExp(regexp) { |
265 if (regexp_key !== regexp) { | 342 if (regexp_key !== regexp) { |
266 regexp_key = regexp; | 343 regexp_key = regexp; |
267 regexp_val = | 344 regexp_val = |
268 new GlobalRegExp( | 345 new GlobalRegExp( |
269 %_SubString(REGEXP_SOURCE(regexp), 2, REGEXP_SOURCE(regexp).length), | 346 %_SubString(REGEXP_SOURCE(regexp), 2, REGEXP_SOURCE(regexp).length), |
270 (REGEXP_IGNORE_CASE(regexp) ? REGEXP_MULTILINE(regexp) ? "im" : "i" | 347 (REGEXP_IGNORE_CASE(regexp) ? REGEXP_MULTILINE(regexp) ? "im" : "i" |
271 : REGEXP_MULTILINE(regexp) ? "m" : "")); | 348 : REGEXP_MULTILINE(regexp) ? "m" : "")); |
272 } | 349 } |
273 return regexp_val; | 350 return regexp_val; |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
375 | 452 |
376 startIndex = currentIndex = endIndex; | 453 startIndex = currentIndex = endIndex; |
377 } | 454 } |
378 | 455 |
379 var array_result = []; | 456 var array_result = []; |
380 %MoveArrayContents(result, array_result); | 457 %MoveArrayContents(result, array_result); |
381 return array_result; | 458 return array_result; |
382 } | 459 } |
383 | 460 |
384 | 461 |
462 function RegExpSubclassSplit(string, limit) { | |
463 if (!IS_RECEIVER(this)) { | |
464 throw MakeTypeError(kIncompatibleMethodReceiver, | |
465 "RegExp.prototype.@@split", this); | |
466 } | |
467 string = TO_STRING(string); | |
468 var constructor = SpeciesConstructor(this, GlobalRegExp); | |
469 var flags = TO_STRING(this.flags); | |
470 var unicode = %StringIndexOf(flags, 'u', 0) >= 0; | |
471 var sticky = %StringIndexOf(flags, 'y', 0) >= 0; | |
472 var new_flags = sticky ? flags : flags + "y"; | |
473 var splitter = new constructor(this, new_flags); | |
474 var array = new GlobalArray(); | |
475 var array_index = 0; | |
476 var lim = (IS_UNDEFINED(limit)) ? kMaxUint32 : TO_UINT32(limit); | |
477 var size = string.length; | |
478 var prev_string_index = 0; | |
479 if (lim === 0) return array; | |
480 var result; | |
481 if (size === 0) { | |
482 result = RegExpSubclassExec(splitter, string); | |
483 if (IS_NULL(result)) AddIndexedProperty(array, 0, string); | |
484 return array; | |
485 } | |
486 var string_index = prev_string_index; | |
487 while (string_index < size) { | |
488 splitter.lastIndex = string_index; | |
489 result = RegExpSubclassExec(splitter, string); | |
490 if (IS_NULL(result)) { | |
491 string_index += GetUnicodeAdvancedIncrement(string, string_index, | |
492 unicode); | |
493 } else { | |
494 var end = MathMin(splitter.lastIndex, size); | |
495 if (end === prev_string_index) { | |
496 string_index += GetUnicodeAdvancedIncrement(string, string_index, | |
497 unicode); | |
498 } else { | |
499 AddIndexedProperty( | |
500 array, array_index, | |
501 %_SubString(string, prev_string_index, string_index)); | |
502 array_index++; | |
503 if (array_index === lim) return array; | |
504 prev_string_index = end; | |
505 var number_of_captures = MathMax(TO_LENGTH(result.length), 0); | |
506 for (var i = 1; i < number_of_captures; i++) { | |
507 AddIndexedProperty(array, array_index, result[i]); | |
508 array_index++; | |
509 if (array_index === lim) return array; | |
510 } | |
511 string_index = prev_string_index; | |
512 } | |
513 } | |
514 } | |
515 AddIndexedProperty(array, array_index, | |
516 %_SubString(string, prev_string_index, size)); | |
517 return array; | |
518 } | |
519 %FunctionRemovePrototype(RegExpSubclassSplit); | |
520 | |
521 | |
385 // ES6 21.2.5.6. | 522 // ES6 21.2.5.6. |
386 function RegExpMatch(string) { | 523 function RegExpMatch(string) { |
387 // TODO(yangguo): allow non-regexp receivers. | |
388 if (!IS_REGEXP(this)) { | 524 if (!IS_REGEXP(this)) { |
389 throw MakeTypeError(kIncompatibleMethodReceiver, | 525 throw MakeTypeError(kIncompatibleMethodReceiver, |
390 "RegExp.prototype.@@match", this); | 526 "RegExp.prototype.@@match", this); |
391 } | 527 } |
392 var subject = TO_STRING(string); | 528 var subject = TO_STRING(string); |
393 | 529 |
394 if (!REGEXP_GLOBAL(this)) return RegExpExecNoTests(this, subject, 0); | 530 if (!REGEXP_GLOBAL(this)) return RegExpExecNoTests(this, subject, 0); |
395 this.lastIndex = 0; | 531 this.lastIndex = 0; |
396 var result = %StringMatch(subject, this, RegExpLastMatchInfo); | 532 var result = %StringMatch(subject, this, RegExpLastMatchInfo); |
397 return result; | 533 return result; |
398 } | 534 } |
399 | 535 |
400 | 536 |
537 function RegExpSubclassMatch(string) { | |
538 if (!IS_OBJECT(this)) { | |
539 throw MakeTypeError(kIncompatibleMethodReceiver, | |
540 "RegExp.prototype.@@match", this); | |
541 } | |
542 string = TO_STRING(string); | |
543 var global = this.global; | |
544 if (!global) return RegExpSubclassExec(this, string); | |
545 var unicode = this.unicode; | |
546 this.lastIndex = 0; | |
547 var array = []; | |
548 var n = 0; | |
549 var result; | |
550 while (true) { | |
551 result = RegExpSubclassExec(this, string); | |
552 if (IS_NULL(result)) { | |
553 if (n === 0) return null; | |
554 return array; | |
555 } | |
556 var matchStr = TO_STRING(result[0]); | |
557 %AddElement(array, n, matchStr); | |
558 if (matchStr === "") AdvanceStringIndex(this, string, unicode); | |
559 n++; | |
560 } | |
561 } | |
562 %FunctionRemovePrototype(RegExpSubclassMatch); | |
563 | |
564 | |
401 // ES6 21.2.5.8. | 565 // ES6 21.2.5.8. |
402 | 566 |
403 // TODO(lrn): This array will survive indefinitely if replace is never | 567 // TODO(lrn): This array will survive indefinitely if replace is never |
404 // called again. However, it will be empty, since the contents are cleared | 568 // called again. However, it will be empty, since the contents are cleared |
405 // in the finally block. | 569 // in the finally block. |
406 var reusableReplaceArray = new InternalArray(4); | 570 var reusableReplaceArray = new InternalArray(4); |
407 | 571 |
408 // Helper function for replacing regular expressions with the result of a | 572 // Helper function for replacing regular expressions with the result of a |
409 // function application in String.prototype.replace. | 573 // function application in String.prototype.replace. |
410 function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) { | 574 function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) { |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
518 } | 682 } |
519 | 683 |
520 result += replacement; // The add method converts to string if necessary. | 684 result += replacement; // The add method converts to string if necessary. |
521 // Can't use matchInfo any more from here, since the function could | 685 // Can't use matchInfo any more from here, since the function could |
522 // overwrite it. | 686 // overwrite it. |
523 return result + %_SubString(subject, endOfMatch, subject.length); | 687 return result + %_SubString(subject, endOfMatch, subject.length); |
524 } | 688 } |
525 | 689 |
526 | 690 |
527 function RegExpReplace(string, replace) { | 691 function RegExpReplace(string, replace) { |
528 // TODO(littledan): allow non-regexp receivers. | |
529 if (!IS_REGEXP(this)) { | 692 if (!IS_REGEXP(this)) { |
530 throw MakeTypeError(kIncompatibleMethodReceiver, | 693 throw MakeTypeError(kIncompatibleMethodReceiver, |
531 "RegExp.prototype.@@replace", this); | 694 "RegExp.prototype.@@replace", this); |
532 } | 695 } |
533 var subject = TO_STRING(string); | 696 var subject = TO_STRING(string); |
534 var search = this; | 697 var search = this; |
535 | 698 |
536 if (!IS_CALLABLE(replace)) { | 699 if (!IS_CALLABLE(replace)) { |
537 replace = TO_STRING(replace); | 700 replace = TO_STRING(replace); |
538 | 701 |
(...skipping 21 matching lines...) Expand all Loading... | |
560 | 723 |
561 if (REGEXP_GLOBAL(search)) { | 724 if (REGEXP_GLOBAL(search)) { |
562 // Global regexp search, function replace. | 725 // Global regexp search, function replace. |
563 return StringReplaceGlobalRegExpWithFunction(subject, search, replace); | 726 return StringReplaceGlobalRegExpWithFunction(subject, search, replace); |
564 } | 727 } |
565 // Non-global regexp search, function replace. | 728 // Non-global regexp search, function replace. |
566 return StringReplaceNonGlobalRegExpWithFunction(subject, search, replace); | 729 return StringReplaceNonGlobalRegExpWithFunction(subject, search, replace); |
567 } | 730 } |
568 | 731 |
569 | 732 |
733 // Expand the $-expressions in the string and return a new string with | |
734 // the result. | |
735 function GetCaptures(matched, string, position, captures, replacement) { | |
736 var match_length = matched.length; | |
737 var string_length = string.length; | |
738 var captures_length = captures.length; | |
739 var tail_pos = position + match_length; | |
740 var result = ""; | |
741 var pos, expansion, peek, next, scaled_index, advance, new_scaled_index; | |
742 | |
743 var next = %StringIndexOf(replacement, '$', 0); | |
744 if (next < 0) { | |
745 result += replacement; | |
746 return result; | |
747 } | |
748 | |
749 if (next > 0) result += %_SubString(replacement, 0, next); | |
750 | |
751 while (true) { | |
752 expansion = '$'; | |
753 pos = next + 1; | |
754 if (pos < replacement.length) { | |
755 peek = %_StringCharCodeAt(replacement, pos); | |
756 if (peek == 36) { // $$ | |
757 ++pos; | |
758 result += '$'; | |
759 } else if (peek == 38) { // $& - match | |
760 ++pos; | |
761 result += matched; | |
762 } else if (peek == 96) { // $` - prefix | |
763 ++pos; | |
764 result += %_SubString(string, 0, position); | |
765 } else if (peek == 39) { // $' - suffix | |
766 ++pos; | |
767 result += %_SubString(string, tail_pos, string_length); | |
768 } else if (peek >= 48 && peek <= 57) { | |
769 // Valid indices are $1 .. $9, $01 .. $09 and $10 .. $99 | |
770 scaled_index = (peek - 48); | |
771 advance = 1; | |
772 if (pos + 1 < replacement.length) { | |
773 next = %_StringCharCodeAt(replacement, pos + 1); | |
774 if (next >= 48 && next <= 57) { | |
775 new_scaled_index = scaled_index * 10 + ((next - 48)); | |
776 if (new_scaled_index < captures_length) { | |
777 scaled_index = new_scaled_index; | |
778 advance = 2; | |
779 } | |
780 } | |
781 } | |
782 if (scaled_index != 0 && scaled_index < captures_length) { | |
783 var capture = captures[scaled_index]; | |
784 if (!IS_UNDEFINED(capture)) result += capture; | |
785 pos += advance; | |
786 } else { | |
787 result += '$'; | |
788 } | |
789 } else { | |
790 result += '$'; | |
791 } | |
792 } else { | |
793 result += '$'; | |
794 } | |
795 | |
796 // Go the the next $ in the replacement. | |
797 next = %StringIndexOf(replacement, '$', pos); | |
798 | |
799 // Return if there are no more $ characters in the replacement. If we | |
800 // haven't reached the end, we need to append the suffix. | |
801 if (next < 0) { | |
802 if (pos < replacement.length) { | |
803 result += %_SubString(replacement, pos, replacement.length); | |
804 } | |
805 return result; | |
806 } | |
807 | |
808 // Append substring between the previous and the next $ character. | |
809 if (next > pos) { | |
810 result += %_SubString(replacement, pos, next); | |
811 } | |
812 } | |
813 return result; | |
814 } | |
815 | |
816 | |
817 function GetUnicodeAdvancedIncrement(string, index, unicode) { | |
818 var increment = 1; | |
819 if (unicode) { | |
820 var first = %_StringCharCodeAt(string, index); | |
821 if (first >= 0xD800 && first <= 0xDBFF && string.length > index + 1) { | |
822 var second = %_StringCharCodeAt(string, index + 1); | |
823 if (second >= 0xDC00 && second <= 0xDFFF) { | |
824 increment = 2; | |
825 } | |
826 } | |
827 } | |
828 return increment; | |
829 } | |
830 | |
831 | |
832 function AdvanceStringIndex(regexp, string, unicode) { | |
833 var last_index = regexp.lastIndex; | |
834 regexp.lastIndex = last_index + | |
835 GetUnicodeAdvancedIncrement(string, last_index, unicode); | |
836 } | |
837 | |
838 | |
839 function RegExpSubclassReplace(string, replace) { | |
840 if (!IS_OBJECT(this)) { | |
841 throw MakeTypeError(kIncompatibleMethodReceiver, | |
842 "RegExp.prototype.@@replace", this); | |
843 } | |
844 string = TO_STRING(string); | |
845 var length = string.length; | |
846 var functional_replace = IS_CALLABLE(replace); | |
847 if (!functional_replace) replace = TO_STRING(replace); | |
848 var global = this.global; | |
849 if (global) { | |
850 var unicode = this.unicode; | |
851 this.lastIndex = 0; | |
852 } | |
853 var results = new InternalArray(); | |
854 var result, replacement; | |
855 while (true) { | |
856 result = RegExpSubclassExec(this, string); | |
857 if (IS_NULL(result)) { | |
858 break; | |
859 } else { | |
860 results.push(result); | |
861 if (!global) break; | |
862 var match_str = TO_STRING(result[0]); | |
863 if (match_str === "") AdvanceStringIndex(this, string, unicode); | |
864 } | |
865 } | |
866 var accumulated_result = ""; | |
867 var next_source_position = 0; | |
868 for (var i = 0; i < results.length; i++) { | |
869 result = results[i]; | |
870 var captures_length = MathMax(TO_LENGTH(result.length), 0); | |
871 var matched = TO_STRING(result[0]); | |
872 var matched_length = matched.length; | |
873 var position = MathMax(MathMin(TO_INTEGER(result.index), length), 0); | |
874 var captures = new InternalArray(); | |
875 for (var n = 0; n < captures_length; n++) { | |
876 var capture = result[n]; | |
877 if (!IS_UNDEFINED(capture)) capture = TO_STRING(capture); | |
878 captures[n] = capture; | |
879 } | |
880 if (functional_replace) { | |
881 var parameters = new InternalArray(captures_length + 2); | |
882 for (var j = 0; j < captures_length; j++) { | |
883 parameters[j] = captures[j]; | |
884 } | |
885 parameters[j] = position; | |
886 parameters[j + 1] = string; | |
887 replacement = %reflect_apply(replace, UNDEFINED, parameters, 0, | |
888 parameters.length); | |
889 } else { | |
890 replacement = GetCaptures(matched, string, position, captures, replace); | |
891 } | |
892 if (position >= next_source_position) { | |
893 accumulated_result += | |
894 %_SubString(string, next_source_position, position) + replacement; | |
895 next_source_position = position + matched_length; | |
896 } | |
897 } | |
898 if (next_source_position >= length) return accumulated_result; | |
899 return accumulated_result + %_SubString(string, next_source_position, length); | |
900 } | |
901 %FunctionRemovePrototype(RegExpSubclassReplace); | |
902 | |
903 | |
570 // ES6 21.2.5.9. | 904 // ES6 21.2.5.9. |
571 function RegExpSearch(string) { | 905 function RegExpSearch(string) { |
572 // TODO(yangguo): allow non-regexp receivers. | |
573 if (!IS_REGEXP(this)) { | 906 if (!IS_REGEXP(this)) { |
574 throw MakeTypeError(kIncompatibleMethodReceiver, | 907 throw MakeTypeError(kIncompatibleMethodReceiver, |
575 "RegExp.prototype.@@search", this); | 908 "RegExp.prototype.@@search", this); |
576 } | 909 } |
577 var match = DoRegExpExec(this, TO_STRING(string), 0); | 910 var match = DoRegExpExec(this, TO_STRING(string), 0); |
578 if (match) return match[CAPTURE0]; | 911 if (match) return match[CAPTURE0]; |
579 return -1; | 912 return -1; |
580 } | 913 } |
581 | 914 |
582 | 915 |
916 function RegExpSubclassSearch(string) { | |
917 if (!IS_OBJECT(this)) { | |
918 throw MakeTypeError(kIncompatibleMethodReceiver, | |
919 "RegExp.prototype.@@search", this); | |
920 } | |
921 string = TO_STRING(string); | |
922 var previousLastIndex = this.lastIndex; | |
923 this.lastIndex = 0; | |
924 var result = RegExpSubclassExec(this, string); | |
925 this.lastIndex = previousLastIndex; | |
926 if (IS_NULL(result)) return -1; | |
927 return result.index; | |
928 } | |
929 %FunctionRemovePrototype(RegExpSubclassSearch); | |
930 | |
931 | |
583 // Getters for the static properties lastMatch, lastParen, leftContext, and | 932 // Getters for the static properties lastMatch, lastParen, leftContext, and |
584 // rightContext of the RegExp constructor. The properties are computed based | 933 // rightContext of the RegExp constructor. The properties are computed based |
585 // on the captures array of the last successful match and the subject string | 934 // on the captures array of the last successful match and the subject string |
586 // of the last successful match. | 935 // of the last successful match. |
587 function RegExpGetLastMatch() { | 936 function RegExpGetLastMatch() { |
588 var regExpSubject = LAST_SUBJECT(RegExpLastMatchInfo); | 937 var regExpSubject = LAST_SUBJECT(RegExpLastMatchInfo); |
589 return %_SubString(regExpSubject, | 938 return %_SubString(regExpSubject, |
590 RegExpLastMatchInfo[CAPTURE0], | 939 RegExpLastMatchInfo[CAPTURE0], |
591 RegExpLastMatchInfo[CAPTURE1]); | 940 RegExpLastMatchInfo[CAPTURE1]); |
592 } | 941 } |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
697 %IncrementUseCounter(kRegExpPrototypeSourceGetter); | 1046 %IncrementUseCounter(kRegExpPrototypeSourceGetter); |
698 return UNDEFINED; | 1047 return UNDEFINED; |
699 } | 1048 } |
700 throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.source"); | 1049 throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.source"); |
701 } | 1050 } |
702 return REGEXP_SOURCE(this); | 1051 return REGEXP_SOURCE(this); |
703 } | 1052 } |
704 %FunctionSetName(RegExpGetSource, "RegExp.prototype.source"); | 1053 %FunctionSetName(RegExpGetSource, "RegExp.prototype.source"); |
705 %SetNativeFlag(RegExpGetSource); | 1054 %SetNativeFlag(RegExpGetSource); |
706 | 1055 |
1056 | |
Yang
2016/03/22 12:49:38
stray edit?
Dan Ehrenberg
2016/03/22 18:26:31
Reverted
| |
707 // ------------------------------------------------------------------- | 1057 // ------------------------------------------------------------------- |
708 | 1058 |
709 %FunctionSetInstanceClassName(GlobalRegExp, 'RegExp'); | 1059 %FunctionSetInstanceClassName(GlobalRegExp, 'RegExp'); |
710 GlobalRegExpPrototype = new GlobalObject(); | 1060 GlobalRegExpPrototype = new GlobalObject(); |
711 %FunctionSetPrototype(GlobalRegExp, GlobalRegExpPrototype); | 1061 %FunctionSetPrototype(GlobalRegExp, GlobalRegExpPrototype); |
712 %AddNamedProperty( | 1062 %AddNamedProperty( |
713 GlobalRegExp.prototype, 'constructor', GlobalRegExp, DONT_ENUM); | 1063 GlobalRegExp.prototype, 'constructor', GlobalRegExp, DONT_ENUM); |
714 %SetCode(GlobalRegExp, RegExpConstructor); | 1064 %SetCode(GlobalRegExp, RegExpConstructor); |
715 | 1065 |
716 utils.InstallFunctions(GlobalRegExp.prototype, DONT_ENUM, [ | 1066 utils.InstallFunctions(GlobalRegExp.prototype, DONT_ENUM, [ |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
774 } | 1124 } |
775 %ToFastProperties(GlobalRegExp); | 1125 %ToFastProperties(GlobalRegExp); |
776 | 1126 |
777 // ------------------------------------------------------------------- | 1127 // ------------------------------------------------------------------- |
778 // Exports | 1128 // Exports |
779 | 1129 |
780 utils.Export(function(to) { | 1130 utils.Export(function(to) { |
781 to.RegExpExec = DoRegExpExec; | 1131 to.RegExpExec = DoRegExpExec; |
782 to.RegExpExecNoTests = RegExpExecNoTests; | 1132 to.RegExpExecNoTests = RegExpExecNoTests; |
783 to.RegExpLastMatchInfo = RegExpLastMatchInfo; | 1133 to.RegExpLastMatchInfo = RegExpLastMatchInfo; |
1134 to.RegExpSubclassExecJS = RegExpSubclassExecJS; | |
1135 to.RegExpSubclassMatch = RegExpSubclassMatch; | |
1136 to.RegExpSubclassReplace = RegExpSubclassReplace; | |
1137 to.RegExpSubclassSearch = RegExpSubclassSearch; | |
1138 to.RegExpSubclassSplit = RegExpSubclassSplit; | |
1139 to.RegExpSubclassTest = RegExpSubclassTest; | |
784 to.RegExpTest = RegExpTest; | 1140 to.RegExpTest = RegExpTest; |
785 to.IsRegExp = IsRegExp; | 1141 to.IsRegExp = IsRegExp; |
786 }); | 1142 }); |
787 | 1143 |
788 }) | 1144 }) |
OLD | NEW |