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

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

Issue 1596483005: Add ES2015 RegExp full subclassing semantics behind a flag (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: camelCase Created 4 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/js/prologue.js ('k') | src/messages.h » ('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 '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 MaxSimple;
24 var MinSimple;
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 MaxSimple = from.MaxSimple;
36 MinSimple = from.MinSimple;
37 SpeciesConstructor = from.SpeciesConstructor;
29 }); 38 });
30 39
31 // ------------------------------------------------------------------- 40 // -------------------------------------------------------------------
32 41
33 // Property of the builtins object for recording the result of the last 42 // Property of the builtins object for recording the result of the last
34 // regexp match. The property RegExpLastMatchInfo includes the matchIndices 43 // regexp match. The property RegExpLastMatchInfo includes the matchIndices
35 // array of the last successful regexp match (an array of start/end index 44 // 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 45 // 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 46 // that there are at least two capture indeces. The array also contains
38 // the subject string for the last successful match. 47 // the subject string for the last successful match.
39 var RegExpLastMatchInfo = new InternalPackedArray( 48 var RegExpLastMatchInfo = new InternalPackedArray(
40 2, // REGEXP_NUMBER_OF_CAPTURES 49 2, // REGEXP_NUMBER_OF_CAPTURES
41 "", // Last subject. 50 "", // Last subject.
42 UNDEFINED, // Last input - settable with RegExpSetInput. 51 UNDEFINED, // Last input - settable with RegExpSetInput.
43 0, // REGEXP_FIRST_CAPTURE + 0 52 0, // REGEXP_FIRST_CAPTURE + 0
44 0 // REGEXP_FIRST_CAPTURE + 1 53 0 // REGEXP_FIRST_CAPTURE + 1
45 ); 54 );
46 55
47 // ------------------------------------------------------------------- 56 // -------------------------------------------------------------------
48 57
58 // ES#sec-isregexp IsRegExp ( argument )
49 function IsRegExp(o) { 59 function IsRegExp(o) {
50 if (!IS_RECEIVER(o)) return false; 60 if (!IS_RECEIVER(o)) return false;
51 var is_regexp = o[matchSymbol]; 61 var is_regexp = o[matchSymbol];
52 if (!IS_UNDEFINED(is_regexp)) return TO_BOOLEAN(is_regexp); 62 if (!IS_UNDEFINED(is_regexp)) return TO_BOOLEAN(is_regexp);
53 return IS_REGEXP(o); 63 return IS_REGEXP(o);
54 } 64 }
55 65
56 66
57 // ES6 section 21.2.3.2.2 67 // ES#sec-regexpinitialize
68 // Runtime Semantics: RegExpInitialize ( obj, pattern, flags )
58 function RegExpInitialize(object, pattern, flags) { 69 function RegExpInitialize(object, pattern, flags) {
59 pattern = IS_UNDEFINED(pattern) ? '' : TO_STRING(pattern); 70 pattern = IS_UNDEFINED(pattern) ? '' : TO_STRING(pattern);
60 flags = IS_UNDEFINED(flags) ? '' : TO_STRING(flags); 71 flags = IS_UNDEFINED(flags) ? '' : TO_STRING(flags);
61 %RegExpInitializeAndCompile(object, pattern, flags); 72 %RegExpInitializeAndCompile(object, pattern, flags);
62 return object; 73 return object;
63 } 74 }
64 75
65 76
66 function PatternFlags(pattern) { 77 function PatternFlags(pattern) {
67 return (REGEXP_GLOBAL(pattern) ? 'g' : '') + 78 return (REGEXP_GLOBAL(pattern) ? 'g' : '') +
68 (REGEXP_IGNORE_CASE(pattern) ? 'i' : '') + 79 (REGEXP_IGNORE_CASE(pattern) ? 'i' : '') +
69 (REGEXP_MULTILINE(pattern) ? 'm' : '') + 80 (REGEXP_MULTILINE(pattern) ? 'm' : '') +
70 (REGEXP_UNICODE(pattern) ? 'u' : '') + 81 (REGEXP_UNICODE(pattern) ? 'u' : '') +
71 (REGEXP_STICKY(pattern) ? 'y' : ''); 82 (REGEXP_STICKY(pattern) ? 'y' : '');
72 } 83 }
73 84
74 85
86 // ES#sec-regexp-pattern-flags
87 // RegExp ( pattern, flags )
75 function RegExpConstructor(pattern, flags) { 88 function RegExpConstructor(pattern, flags) {
76 var newtarget = new.target; 89 var newtarget = new.target;
77 var pattern_is_regexp = IsRegExp(pattern); 90 var pattern_is_regexp = IsRegExp(pattern);
78 91
79 if (IS_UNDEFINED(newtarget)) { 92 if (IS_UNDEFINED(newtarget)) {
80 newtarget = GlobalRegExp; 93 newtarget = GlobalRegExp;
81 94
82 // ES6 section 21.2.3.1 step 3.b 95 // ES6 section 21.2.3.1 step 3.b
83 if (pattern_is_regexp && IS_UNDEFINED(flags) && 96 if (pattern_is_regexp && IS_UNDEFINED(flags) &&
84 pattern.constructor === newtarget) { 97 pattern.constructor === newtarget) {
85 return pattern; 98 return pattern;
86 } 99 }
87 } 100 }
88 101
89 if (IS_REGEXP(pattern)) { 102 if (IS_REGEXP(pattern)) {
90 if (IS_UNDEFINED(flags)) flags = PatternFlags(pattern); 103 if (IS_UNDEFINED(flags)) flags = PatternFlags(pattern);
91 pattern = REGEXP_SOURCE(pattern); 104 pattern = REGEXP_SOURCE(pattern);
92 105
93 } else if (pattern_is_regexp) { 106 } else if (pattern_is_regexp) {
94 var input_pattern = pattern; 107 var input_pattern = pattern;
95 pattern = pattern.source; 108 pattern = pattern.source;
96 if (IS_UNDEFINED(flags)) flags = input_pattern.flags; 109 if (IS_UNDEFINED(flags)) flags = input_pattern.flags;
97 } 110 }
98 111
99 var object = %NewObject(GlobalRegExp, newtarget); 112 var object = %NewObject(GlobalRegExp, newtarget);
100 return RegExpInitialize(object, pattern, flags); 113 return RegExpInitialize(object, pattern, flags);
101 } 114 }
102 115
103 116
117 // ES#sec-regexp.prototype.compile RegExp.prototype.compile (pattern, flags)
104 function RegExpCompileJS(pattern, flags) { 118 function RegExpCompileJS(pattern, flags) {
105 if (!IS_REGEXP(this)) { 119 if (!IS_REGEXP(this)) {
106 throw MakeTypeError(kIncompatibleMethodReceiver, 120 throw MakeTypeError(kIncompatibleMethodReceiver,
107 "RegExp.prototype.compile", this); 121 "RegExp.prototype.compile", this);
108 } 122 }
109 123
110 if (IS_REGEXP(pattern)) { 124 if (IS_REGEXP(pattern)) {
111 if (!IS_UNDEFINED(flags)) throw MakeTypeError(kRegExpFlags); 125 if (!IS_UNDEFINED(flags)) throw MakeTypeError(kRegExpFlags);
112 126
113 flags = PatternFlags(pattern); 127 flags = PatternFlags(pattern);
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
158 if (matchInfo !== null) { 172 if (matchInfo !== null) {
159 // ES6 21.2.5.2.2 step 18. 173 // ES6 21.2.5.2.2 step 18.
160 if (REGEXP_STICKY(regexp)) regexp.lastIndex = matchInfo[CAPTURE1]; 174 if (REGEXP_STICKY(regexp)) regexp.lastIndex = matchInfo[CAPTURE1];
161 RETURN_NEW_RESULT_FROM_MATCH_INFO(matchInfo, string); 175 RETURN_NEW_RESULT_FROM_MATCH_INFO(matchInfo, string);
162 } 176 }
163 regexp.lastIndex = 0; 177 regexp.lastIndex = 0;
164 return null; 178 return null;
165 } 179 }
166 180
167 181
182 // ES#sec-regexp.prototype.exec
183 // RegExp.prototype.exec ( string )
184 function RegExpSubclassExecJS(string) {
185 if (!IS_REGEXP(this)) {
186 throw MakeTypeError(kIncompatibleMethodReceiver,
187 'RegExp.prototype.exec', this);
188 }
189
190 string = TO_STRING(string);
191 var lastIndex = this.lastIndex;
192
193 // Conversion is required by the ES2015 specification (RegExpBuiltinExec
194 // algorithm, step 4) even if the value is discarded for non-global RegExps.
195 var i = TO_LENGTH(lastIndex);
196
197 var global = TO_BOOLEAN(this.global);
198 var sticky = TO_BOOLEAN(this.sticky);
199 var updateLastIndex = global || sticky;
200 if (updateLastIndex) {
201 if (i > string.length) {
202 this.lastIndex = 0;
203 return null;
204 }
205 } else {
206 i = 0;
207 }
208
209 // matchIndices is either null or the RegExpLastMatchInfo array.
210 // TODO(littledan): Whether a RegExp is sticky is compiled into the RegExp
211 // itself, but ES2015 allows monkey-patching this property to differ from
212 // the internal flags. If it differs, recompile a different RegExp?
213 var matchIndices = %_RegExpExec(this, string, i, RegExpLastMatchInfo);
214
215 if (IS_NULL(matchIndices)) {
216 this.lastIndex = 0;
217 return null;
218 }
219
220 // Successful match.
221 if (updateLastIndex) {
222 this.lastIndex = RegExpLastMatchInfo[CAPTURE1];
223 }
224 RETURN_NEW_RESULT_FROM_MATCH_INFO(matchIndices, string);
225 }
226 %FunctionRemovePrototype(RegExpSubclassExecJS);
227
228
229 // Legacy implementation of RegExp.prototype.exec
168 function RegExpExecJS(string) { 230 function RegExpExecJS(string) {
169 if (!IS_REGEXP(this)) { 231 if (!IS_REGEXP(this)) {
170 throw MakeTypeError(kIncompatibleMethodReceiver, 232 throw MakeTypeError(kIncompatibleMethodReceiver,
171 'RegExp.prototype.exec', this); 233 'RegExp.prototype.exec', this);
172 } 234 }
173 235
174 string = TO_STRING(string); 236 string = TO_STRING(string);
175 var lastIndex = this.lastIndex; 237 var lastIndex = this.lastIndex;
176 238
177 // Conversion is required by the ES2015 specification (RegExpBuiltinExec 239 // Conversion is required by the ES2015 specification (RegExpBuiltinExec
(...skipping 19 matching lines...) Expand all
197 } 259 }
198 260
199 // Successful match. 261 // Successful match.
200 if (updateLastIndex) { 262 if (updateLastIndex) {
201 this.lastIndex = RegExpLastMatchInfo[CAPTURE1]; 263 this.lastIndex = RegExpLastMatchInfo[CAPTURE1];
202 } 264 }
203 RETURN_NEW_RESULT_FROM_MATCH_INFO(matchIndices, string); 265 RETURN_NEW_RESULT_FROM_MATCH_INFO(matchIndices, string);
204 } 266 }
205 267
206 268
269 // ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S )
270 function RegExpSubclassExec(regexp, string) {
271 var exec = regexp.exec;
272 if (IS_CALLABLE(exec)) {
273 var result = %_Call(exec, regexp, string);
274 if (!IS_OBJECT(result) && !IS_NULL(result)) {
275 throw MakeTypeError(kInvalidRegExpExecResult);
276 }
277 return result;
278 }
279 return %_Call(RegExpExecJS, regexp, string);
280 }
281
282
207 // One-element cache for the simplified test regexp. 283 // One-element cache for the simplified test regexp.
208 var regexp_key; 284 var regexp_key;
209 var regexp_val; 285 var regexp_val;
210 286
287 // Legacy implementation of RegExp.prototype.test
211 // Section 15.10.6.3 doesn't actually make sense, but the intention seems to be 288 // 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 289 // 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 290 // means the original value of String.prototype.exec, which is what everybody
214 // else implements. 291 // else implements.
215 function RegExpTest(string) { 292 function RegExpTest(string) {
216 if (!IS_REGEXP(this)) { 293 if (!IS_REGEXP(this)) {
217 throw MakeTypeError(kIncompatibleMethodReceiver, 294 throw MakeTypeError(kIncompatibleMethodReceiver,
218 'RegExp.prototype.test', this); 295 'RegExp.prototype.test', this);
219 } 296 }
220 string = TO_STRING(string); 297 string = TO_STRING(string);
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
254 // matchIndices is either null or the RegExpLastMatchInfo array. 331 // matchIndices is either null or the RegExpLastMatchInfo array.
255 var matchIndices = %_RegExpExec(regexp, string, 0, RegExpLastMatchInfo); 332 var matchIndices = %_RegExpExec(regexp, string, 0, RegExpLastMatchInfo);
256 if (IS_NULL(matchIndices)) { 333 if (IS_NULL(matchIndices)) {
257 this.lastIndex = 0; 334 this.lastIndex = 0;
258 return false; 335 return false;
259 } 336 }
260 return true; 337 return true;
261 } 338 }
262 } 339 }
263 340
341
342 // ES#sec-regexp.prototype.test RegExp.prototype.test ( S )
343 function RegExpSubclassTest(string) {
344 if (!IS_OBJECT(this)) {
345 throw MakeTypeError(kIncompatibleMethodReceiver,
346 'RegExp.prototype.test', this);
347 }
348 string = TO_STRING(string);
349 var match = RegExpSubclassExec(this, string);
350 return !IS_NULL(match);
351 }
352 %FunctionRemovePrototype(RegExpSubclassTest);
353
264 function TrimRegExp(regexp) { 354 function TrimRegExp(regexp) {
265 if (regexp_key !== regexp) { 355 if (regexp_key !== regexp) {
266 regexp_key = regexp; 356 regexp_key = regexp;
267 regexp_val = 357 regexp_val =
268 new GlobalRegExp( 358 new GlobalRegExp(
269 %_SubString(REGEXP_SOURCE(regexp), 2, REGEXP_SOURCE(regexp).length), 359 %_SubString(REGEXP_SOURCE(regexp), 2, REGEXP_SOURCE(regexp).length),
270 (REGEXP_IGNORE_CASE(regexp) ? REGEXP_MULTILINE(regexp) ? "im" : "i" 360 (REGEXP_IGNORE_CASE(regexp) ? REGEXP_MULTILINE(regexp) ? "im" : "i"
271 : REGEXP_MULTILINE(regexp) ? "m" : "")); 361 : REGEXP_MULTILINE(regexp) ? "m" : ""));
272 } 362 }
273 return regexp_val; 363 return regexp_val;
(...skipping 27 matching lines...) Expand all
301 391
302 function AtSurrogatePair(subject, index) { 392 function AtSurrogatePair(subject, index) {
303 if (index + 1 >= subject.length) return false; 393 if (index + 1 >= subject.length) return false;
304 var first = %_StringCharCodeAt(subject, index); 394 var first = %_StringCharCodeAt(subject, index);
305 if (first < 0xD800 || first > 0xDBFF) return false; 395 if (first < 0xD800 || first > 0xDBFF) return false;
306 var second = %_StringCharCodeAt(subject, index + 1); 396 var second = %_StringCharCodeAt(subject, index + 1);
307 return second >= 0xDC00 || second <= 0xDFFF; 397 return second >= 0xDC00 || second <= 0xDFFF;
308 } 398 }
309 399
310 400
311 // ES6 21.2.5.11. 401 // Legacy implementation of RegExp.prototype[Symbol.split] which
402 // doesn't properly call the underlying exec, @@species methods
312 function RegExpSplit(string, limit) { 403 function RegExpSplit(string, limit) {
313 // TODO(yangguo): allow non-regexp receivers. 404 // TODO(yangguo): allow non-regexp receivers.
314 if (!IS_REGEXP(this)) { 405 if (!IS_REGEXP(this)) {
315 throw MakeTypeError(kIncompatibleMethodReceiver, 406 throw MakeTypeError(kIncompatibleMethodReceiver,
316 "RegExp.prototype.@@split", this); 407 "RegExp.prototype.@@split", this);
317 } 408 }
318 var separator = this; 409 var separator = this;
319 var subject = TO_STRING(string); 410 var subject = TO_STRING(string);
320 411
321 limit = (IS_UNDEFINED(limit)) ? kMaxUint32 : TO_UINT32(limit); 412 limit = (IS_UNDEFINED(limit)) ? kMaxUint32 : TO_UINT32(limit);
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
375 466
376 startIndex = currentIndex = endIndex; 467 startIndex = currentIndex = endIndex;
377 } 468 }
378 469
379 var array_result = []; 470 var array_result = [];
380 %MoveArrayContents(result, array_result); 471 %MoveArrayContents(result, array_result);
381 return array_result; 472 return array_result;
382 } 473 }
383 474
384 475
385 // ES6 21.2.5.6. 476 // ES#sec-regexp.prototype-@@split
477 // RegExp.prototype [ @@split ] ( string, limit )
478 function RegExpSubclassSplit(string, limit) {
479 if (!IS_RECEIVER(this)) {
480 throw MakeTypeError(kIncompatibleMethodReceiver,
481 "RegExp.prototype.@@split", this);
482 }
483 string = TO_STRING(string);
484 var constructor = SpeciesConstructor(this, GlobalRegExp);
485 var flags = TO_STRING(this.flags);
486 var unicode = %StringIndexOf(flags, 'u', 0) >= 0;
487 var sticky = %StringIndexOf(flags, 'y', 0) >= 0;
488 var newFlags = sticky ? flags : flags + "y";
489 var splitter = new constructor(this, newFlags);
490 var array = new GlobalArray();
491 var arrayIndex = 0;
492 var lim = (IS_UNDEFINED(limit)) ? kMaxUint32 : TO_UINT32(limit);
493 var size = string.length;
494 var prevStringIndex = 0;
495 if (lim === 0) return array;
496 var result;
497 if (size === 0) {
498 result = RegExpSubclassExec(splitter, string);
499 if (IS_NULL(result)) AddIndexedProperty(array, 0, string);
500 return array;
501 }
502 var stringIndex = prevStringIndex;
503 while (stringIndex < size) {
504 splitter.lastIndex = stringIndex;
505 result = RegExpSubclassExec(splitter, string);
506 if (IS_NULL(result)) {
507 stringIndex += AdvanceStringIndex(string, stringIndex, unicode);
508 } else {
509 var end = MinSimple(TO_LENGTH(splitter.lastIndex), size);
510 if (end === stringIndex) {
511 stringIndex += AdvanceStringIndex(string, stringIndex, unicode);
512 } else {
513 AddIndexedProperty(
514 array, arrayIndex,
515 %_SubString(string, prevStringIndex, stringIndex));
516 arrayIndex++;
517 if (arrayIndex === lim) return array;
518 prevStringIndex = end;
519 var numberOfCaptures = MaxSimple(TO_LENGTH(result.length), 0);
520 for (var i = 1; i < numberOfCaptures; i++) {
521 AddIndexedProperty(array, arrayIndex, result[i]);
522 arrayIndex++;
523 if (arrayIndex === lim) return array;
524 }
525 stringIndex = prevStringIndex;
526 }
527 }
528 }
529 AddIndexedProperty(array, arrayIndex,
530 %_SubString(string, prevStringIndex, size));
531 return array;
532 }
533 %FunctionRemovePrototype(RegExpSubclassSplit);
534
535
536 // Legacy implementation of RegExp.prototype[Symbol.match] which
537 // doesn't properly call the underlying exec method
386 function RegExpMatch(string) { 538 function RegExpMatch(string) {
387 // TODO(yangguo): allow non-regexp receivers.
388 if (!IS_REGEXP(this)) { 539 if (!IS_REGEXP(this)) {
389 throw MakeTypeError(kIncompatibleMethodReceiver, 540 throw MakeTypeError(kIncompatibleMethodReceiver,
390 "RegExp.prototype.@@match", this); 541 "RegExp.prototype.@@match", this);
391 } 542 }
392 var subject = TO_STRING(string); 543 var subject = TO_STRING(string);
393 544
394 if (!REGEXP_GLOBAL(this)) return RegExpExecNoTests(this, subject, 0); 545 if (!REGEXP_GLOBAL(this)) return RegExpExecNoTests(this, subject, 0);
395 this.lastIndex = 0; 546 this.lastIndex = 0;
396 var result = %StringMatch(subject, this, RegExpLastMatchInfo); 547 var result = %StringMatch(subject, this, RegExpLastMatchInfo);
397 return result; 548 return result;
398 } 549 }
399 550
400 551
401 // ES6 21.2.5.8. 552 // ES#sec-regexp.prototype-@@match
553 // RegExp.prototype [ @@match ] ( string )
554 function RegExpSubclassMatch(string) {
555 if (!IS_OBJECT(this)) {
556 throw MakeTypeError(kIncompatibleMethodReceiver,
557 "RegExp.prototype.@@match", this);
558 }
559 string = TO_STRING(string);
560 var global = this.global;
561 if (!global) return RegExpSubclassExec(this, string);
562 var unicode = this.unicode;
563 this.lastIndex = 0;
564 var array = [];
565 var n = 0;
566 var result;
567 while (true) {
568 result = RegExpSubclassExec(this, string);
569 if (IS_NULL(result)) {
570 if (n === 0) return null;
571 return array;
572 }
573 var matchStr = TO_STRING(result[0]);
574 %AddElement(array, n, matchStr);
575 if (matchStr === "") SetAdvancedStringIndex(this, string, unicode);
576 n++;
577 }
578 }
579 %FunctionRemovePrototype(RegExpSubclassMatch);
580
581
582 // Legacy implementation of RegExp.prototype[Symbol.replace] which
583 // doesn't properly call the underlying exec method.
402 584
403 // TODO(lrn): This array will survive indefinitely if replace is never 585 // TODO(lrn): This array will survive indefinitely if replace is never
404 // called again. However, it will be empty, since the contents are cleared 586 // called again. However, it will be empty, since the contents are cleared
405 // in the finally block. 587 // in the finally block.
406 var reusableReplaceArray = new InternalArray(4); 588 var reusableReplaceArray = new InternalArray(4);
407 589
408 // Helper function for replacing regular expressions with the result of a 590 // Helper function for replacing regular expressions with the result of a
409 // function application in String.prototype.replace. 591 // function application in String.prototype.replace.
410 function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) { 592 function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) {
411 var resultArray = reusableReplaceArray; 593 var resultArray = reusableReplaceArray;
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after
518 } 700 }
519 701
520 result += replacement; // The add method converts to string if necessary. 702 result += replacement; // The add method converts to string if necessary.
521 // Can't use matchInfo any more from here, since the function could 703 // Can't use matchInfo any more from here, since the function could
522 // overwrite it. 704 // overwrite it.
523 return result + %_SubString(subject, endOfMatch, subject.length); 705 return result + %_SubString(subject, endOfMatch, subject.length);
524 } 706 }
525 707
526 708
527 function RegExpReplace(string, replace) { 709 function RegExpReplace(string, replace) {
528 // TODO(littledan): allow non-regexp receivers.
529 if (!IS_REGEXP(this)) { 710 if (!IS_REGEXP(this)) {
530 throw MakeTypeError(kIncompatibleMethodReceiver, 711 throw MakeTypeError(kIncompatibleMethodReceiver,
531 "RegExp.prototype.@@replace", this); 712 "RegExp.prototype.@@replace", this);
532 } 713 }
533 var subject = TO_STRING(string); 714 var subject = TO_STRING(string);
534 var search = this; 715 var search = this;
535 716
536 if (!IS_CALLABLE(replace)) { 717 if (!IS_CALLABLE(replace)) {
537 replace = TO_STRING(replace); 718 replace = TO_STRING(replace);
538 719
(...skipping 21 matching lines...) Expand all
560 741
561 if (REGEXP_GLOBAL(search)) { 742 if (REGEXP_GLOBAL(search)) {
562 // Global regexp search, function replace. 743 // Global regexp search, function replace.
563 return StringReplaceGlobalRegExpWithFunction(subject, search, replace); 744 return StringReplaceGlobalRegExpWithFunction(subject, search, replace);
564 } 745 }
565 // Non-global regexp search, function replace. 746 // Non-global regexp search, function replace.
566 return StringReplaceNonGlobalRegExpWithFunction(subject, search, replace); 747 return StringReplaceNonGlobalRegExpWithFunction(subject, search, replace);
567 } 748 }
568 749
569 750
570 // ES6 21.2.5.9. 751 // ES#sec-getsubstitution
752 // GetSubstitution(matched, str, position, captures, replacement)
753 // Expand the $-expressions in the string and return a new string with
754 // the result.
755 // TODO(littledan): Call this function from String.prototype.replace instead
756 // of the very similar ExpandReplacement in src/js/string.js
757 function GetSubstitution(matched, string, position, captures, replacement) {
758 var matchLength = matched.length;
759 var stringLength = string.length;
760 var capturesLength = captures.length;
761 var tailPos = position + matchLength;
762 var result = "";
763 var pos, expansion, peek, next, scaledIndex, advance, newScaledIndex;
764
765 var next = %StringIndexOf(replacement, '$', 0);
766 if (next < 0) {
767 result += replacement;
768 return result;
769 }
770
771 if (next > 0) result += %_SubString(replacement, 0, next);
772
773 while (true) {
774 expansion = '$';
775 pos = next + 1;
776 if (pos < replacement.length) {
777 peek = %_StringCharCodeAt(replacement, pos);
778 if (peek == 36) { // $$
779 ++pos;
780 result += '$';
781 } else if (peek == 38) { // $& - match
782 ++pos;
783 result += matched;
784 } else if (peek == 96) { // $` - prefix
785 ++pos;
786 result += %_SubString(string, 0, position);
787 } else if (peek == 39) { // $' - suffix
788 ++pos;
789 result += %_SubString(string, tailPos, stringLength);
790 } else if (peek >= 48 && peek <= 57) {
791 // Valid indices are $1 .. $9, $01 .. $09 and $10 .. $99
792 scaledIndex = (peek - 48);
793 advance = 1;
794 if (pos + 1 < replacement.length) {
795 next = %_StringCharCodeAt(replacement, pos + 1);
796 if (next >= 48 && next <= 57) {
797 newScaledIndex = scaledIndex * 10 + ((next - 48));
798 if (newScaledIndex < capturesLength) {
799 scaledIndex = newScaledIndex;
800 advance = 2;
801 }
802 }
803 }
804 if (scaledIndex != 0 && scaledIndex < capturesLength) {
805 var capture = captures[scaledIndex];
806 if (!IS_UNDEFINED(capture)) result += capture;
807 pos += advance;
808 } else {
809 result += '$';
810 }
811 } else {
812 result += '$';
813 }
814 } else {
815 result += '$';
816 }
817
818 // Go the the next $ in the replacement.
819 next = %StringIndexOf(replacement, '$', pos);
820
821 // Return if there are no more $ characters in the replacement. If we
822 // haven't reached the end, we need to append the suffix.
823 if (next < 0) {
824 if (pos < replacement.length) {
825 result += %_SubString(replacement, pos, replacement.length);
826 }
827 return result;
828 }
829
830 // Append substring between the previous and the next $ character.
831 if (next > pos) {
832 result += %_SubString(replacement, pos, next);
833 }
834 }
835 return result;
836 }
837
838
839 // ES#sec-advancestringindex
840 // AdvanceStringIndex ( S, index, unicode )
841 function AdvanceStringIndex(string, index, unicode) {
842 var increment = 1;
843 if (unicode) {
844 var first = %_StringCharCodeAt(string, index);
845 if (first >= 0xD800 && first <= 0xDBFF && string.length > index + 1) {
846 var second = %_StringCharCodeAt(string, index + 1);
847 if (second >= 0xDC00 && second <= 0xDFFF) {
848 increment = 2;
849 }
850 }
851 }
852 return increment;
853 }
854
855
856 function SetAdvancedStringIndex(regexp, string, unicode) {
857 var lastIndex = regexp.lastIndex;
858 regexp.lastIndex = lastIndex +
859 AdvanceStringIndex(string, lastIndex, unicode);
860 }
861
862
863 // ES#sec-regexp.prototype-@@replace
864 // RegExp.prototype [ @@replace ] ( string, replaceValue )
865 function RegExpSubclassReplace(string, replace) {
866 if (!IS_OBJECT(this)) {
867 throw MakeTypeError(kIncompatibleMethodReceiver,
868 "RegExp.prototype.@@replace", this);
869 }
870 string = TO_STRING(string);
871 var length = string.length;
872 var functionalReplace = IS_CALLABLE(replace);
873 if (!functionalReplace) replace = TO_STRING(replace);
874 var global = this.global;
875 if (global) {
876 var unicode = this.unicode;
877 this.lastIndex = 0;
878 }
879 var results = new InternalArray();
880 var result, replacement;
881 while (true) {
882 result = RegExpSubclassExec(this, string);
883 if (IS_NULL(result)) {
884 break;
885 } else {
886 results.push(result);
887 if (!global) break;
888 var matchStr = TO_STRING(result[0]);
889 if (matchStr === "") SetAdvancedStringIndex(this, string, unicode);
890 }
891 }
892 var accumulatedResult = "";
893 var nextSourcePosition = 0;
894 for (var i = 0; i < results.length; i++) {
895 result = results[i];
896 var capturesLength = MaxSimple(TO_LENGTH(result.length), 0);
897 var matched = TO_STRING(result[0]);
898 var matchedLength = matched.length;
899 var position = MaxSimple(MinSimple(TO_INTEGER(result.index), length), 0);
900 var captures = new InternalArray();
901 for (var n = 0; n < capturesLength; n++) {
902 var capture = result[n];
903 if (!IS_UNDEFINED(capture)) capture = TO_STRING(capture);
904 captures[n] = capture;
905 }
906 if (functionalReplace) {
907 var parameters = new InternalArray(capturesLength + 2);
908 for (var j = 0; j < capturesLength; j++) {
909 parameters[j] = captures[j];
910 }
911 parameters[j] = position;
912 parameters[j + 1] = string;
913 replacement = %reflect_apply(replace, UNDEFINED, parameters, 0,
914 parameters.length);
915 } else {
916 replacement = GetSubstitution(matched, string, position, captures,
917 replace);
918 }
919 if (position >= nextSourcePosition) {
920 accumulatedResult +=
921 %_SubString(string, nextSourcePosition, position) + replacement;
922 nextSourcePosition = position + matchedLength;
923 }
924 }
925 if (nextSourcePosition >= length) return accumulatedResult;
926 return accumulatedResult + %_SubString(string, nextSourcePosition, length);
927 }
928 %FunctionRemovePrototype(RegExpSubclassReplace);
929
930
931 // Legacy implementation of RegExp.prototype[Symbol.search] which
932 // doesn't properly use the overridden exec method
571 function RegExpSearch(string) { 933 function RegExpSearch(string) {
572 // TODO(yangguo): allow non-regexp receivers.
573 if (!IS_REGEXP(this)) { 934 if (!IS_REGEXP(this)) {
574 throw MakeTypeError(kIncompatibleMethodReceiver, 935 throw MakeTypeError(kIncompatibleMethodReceiver,
575 "RegExp.prototype.@@search", this); 936 "RegExp.prototype.@@search", this);
576 } 937 }
577 var match = DoRegExpExec(this, TO_STRING(string), 0); 938 var match = DoRegExpExec(this, TO_STRING(string), 0);
578 if (match) return match[CAPTURE0]; 939 if (match) return match[CAPTURE0];
579 return -1; 940 return -1;
580 } 941 }
581 942
582 943
944 // ES#sec-regexp.prototype-@@search
945 // RegExp.prototype [ @@search ] ( string )
946 function RegExpSubclassSearch(string) {
947 if (!IS_OBJECT(this)) {
948 throw MakeTypeError(kIncompatibleMethodReceiver,
949 "RegExp.prototype.@@search", this);
950 }
951 string = TO_STRING(string);
952 var previousLastIndex = this.lastIndex;
953 this.lastIndex = 0;
954 var result = RegExpSubclassExec(this, string);
955 this.lastIndex = previousLastIndex;
956 if (IS_NULL(result)) return -1;
957 return result.index;
958 }
959 %FunctionRemovePrototype(RegExpSubclassSearch);
960
961
583 // Getters for the static properties lastMatch, lastParen, leftContext, and 962 // Getters for the static properties lastMatch, lastParen, leftContext, and
584 // rightContext of the RegExp constructor. The properties are computed based 963 // rightContext of the RegExp constructor. The properties are computed based
585 // on the captures array of the last successful match and the subject string 964 // on the captures array of the last successful match and the subject string
586 // of the last successful match. 965 // of the last successful match.
587 function RegExpGetLastMatch() { 966 function RegExpGetLastMatch() {
588 var regExpSubject = LAST_SUBJECT(RegExpLastMatchInfo); 967 var regExpSubject = LAST_SUBJECT(RegExpLastMatchInfo);
589 return %_SubString(regExpSubject, 968 return %_SubString(regExpSubject,
590 RegExpLastMatchInfo[CAPTURE0], 969 RegExpLastMatchInfo[CAPTURE0],
591 RegExpLastMatchInfo[CAPTURE1]); 970 RegExpLastMatchInfo[CAPTURE1]);
592 } 971 }
(...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after
773 NoOpSetter, DONT_DELETE); 1152 NoOpSetter, DONT_DELETE);
774 } 1153 }
775 %ToFastProperties(GlobalRegExp); 1154 %ToFastProperties(GlobalRegExp);
776 1155
777 // ------------------------------------------------------------------- 1156 // -------------------------------------------------------------------
778 // Exports 1157 // Exports
779 1158
780 utils.Export(function(to) { 1159 utils.Export(function(to) {
781 to.RegExpExec = DoRegExpExec; 1160 to.RegExpExec = DoRegExpExec;
782 to.RegExpLastMatchInfo = RegExpLastMatchInfo; 1161 to.RegExpLastMatchInfo = RegExpLastMatchInfo;
1162 to.RegExpSubclassExecJS = RegExpSubclassExecJS;
1163 to.RegExpSubclassMatch = RegExpSubclassMatch;
1164 to.RegExpSubclassReplace = RegExpSubclassReplace;
1165 to.RegExpSubclassSearch = RegExpSubclassSearch;
1166 to.RegExpSubclassSplit = RegExpSubclassSplit;
1167 to.RegExpSubclassTest = RegExpSubclassTest;
783 to.RegExpTest = RegExpTest; 1168 to.RegExpTest = RegExpTest;
784 to.IsRegExp = IsRegExp; 1169 to.IsRegExp = IsRegExp;
785 }); 1170 });
786 1171
787 }) 1172 })
OLDNEW
« no previous file with comments | « src/js/prologue.js ('k') | src/messages.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698