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

Side by Side Diff: src/regexp.js

Issue 580383003: Reland sticky regexps https://codereview.chromium.org/567313003/ (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 6 years, 3 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 | Annotate | Revision Log
« no previous file with comments | « src/objects.h ('k') | src/runtime.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 // This file relies on the fact that the following declaration has been made 5 // This file relies on the fact that the following declaration has been made
6 // in runtime.js: 6 // in runtime.js:
7 // var $Object = global.Object; 7 // var $Object = global.Object;
8 // var $Array = global.Array; 8 // var $Array = global.Array;
9 9
10 var $RegExp = global.RegExp; 10 var $RegExp = global.RegExp;
11 11
12 // ------------------------------------------------------------------- 12 // -------------------------------------------------------------------
13 13
14 // A recursive descent parser for Patterns according to the grammar of 14 // A recursive descent parser for Patterns according to the grammar of
15 // ECMA-262 15.10.1, with deviations noted below. 15 // ECMA-262 15.10.1, with deviations noted below.
16 function DoConstructRegExp(object, pattern, flags) { 16 function DoConstructRegExp(object, pattern, flags) {
17 // RegExp : Called as constructor; see ECMA-262, section 15.10.4. 17 // RegExp : Called as constructor; see ECMA-262, section 15.10.4.
18 if (IS_REGEXP(pattern)) { 18 if (IS_REGEXP(pattern)) {
19 if (!IS_UNDEFINED(flags)) { 19 if (!IS_UNDEFINED(flags)) {
20 throw MakeTypeError('regexp_flags', []); 20 throw MakeTypeError('regexp_flags', []);
21 } 21 }
22 flags = (pattern.global ? 'g' : '') 22 flags = (pattern.global ? 'g' : '')
23 + (pattern.ignoreCase ? 'i' : '') 23 + (pattern.ignoreCase ? 'i' : '')
24 + (pattern.multiline ? 'm' : ''); 24 + (pattern.multiline ? 'm' : '');
25 if (harmony_regexps)
26 flags += (pattern.sticky ? 'y' : '');
25 pattern = pattern.source; 27 pattern = pattern.source;
26 } 28 }
27 29
28 pattern = IS_UNDEFINED(pattern) ? '' : ToString(pattern); 30 pattern = IS_UNDEFINED(pattern) ? '' : ToString(pattern);
29 flags = IS_UNDEFINED(flags) ? '' : ToString(flags); 31 flags = IS_UNDEFINED(flags) ? '' : ToString(flags);
30 32
31 var global = false; 33 var global = false;
32 var ignoreCase = false; 34 var ignoreCase = false;
33 var multiline = false; 35 var multiline = false;
36 var sticky = false;
34 for (var i = 0; i < flags.length; i++) { 37 for (var i = 0; i < flags.length; i++) {
35 var c = %_CallFunction(flags, i, StringCharAt); 38 var c = %_CallFunction(flags, i, StringCharAt);
36 switch (c) { 39 switch (c) {
37 case 'g': 40 case 'g':
38 if (global) { 41 if (global) {
39 throw MakeSyntaxError("invalid_regexp_flags", [flags]); 42 throw MakeSyntaxError("invalid_regexp_flags", [flags]);
40 } 43 }
41 global = true; 44 global = true;
42 break; 45 break;
43 case 'i': 46 case 'i':
44 if (ignoreCase) { 47 if (ignoreCase) {
45 throw MakeSyntaxError("invalid_regexp_flags", [flags]); 48 throw MakeSyntaxError("invalid_regexp_flags", [flags]);
46 } 49 }
47 ignoreCase = true; 50 ignoreCase = true;
48 break; 51 break;
49 case 'm': 52 case 'm':
50 if (multiline) { 53 if (multiline) {
51 throw MakeSyntaxError("invalid_regexp_flags", [flags]); 54 throw MakeSyntaxError("invalid_regexp_flags", [flags]);
52 } 55 }
53 multiline = true; 56 multiline = true;
54 break; 57 break;
58 case 'y':
59 if (!harmony_regexps || sticky) {
60 throw MakeSyntaxError("invalid_regexp_flags", [flags]);
61 }
62 sticky = true;
63 break;
55 default: 64 default:
56 throw MakeSyntaxError("invalid_regexp_flags", [flags]); 65 throw MakeSyntaxError("invalid_regexp_flags", [flags]);
57 } 66 }
58 } 67 }
59 68
60 %RegExpInitializeObject(object, pattern, global, ignoreCase, multiline); 69 %RegExpInitializeObject(object, pattern, global, ignoreCase, multiline, sticky );
61 70
62 // Call internal function to compile the pattern. 71 // Call internal function to compile the pattern.
63 %RegExpCompile(object, pattern, flags); 72 %RegExpCompile(object, pattern, flags);
64 } 73 }
65 74
66 75
67 function RegExpConstructor(pattern, flags) { 76 function RegExpConstructor(pattern, flags) {
68 if (%_IsConstructCall()) { 77 if (%_IsConstructCall()) {
69 DoConstructRegExp(this, pattern, flags); 78 DoConstructRegExp(this, pattern, flags);
70 } else { 79 } else {
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
152 ['RegExp.prototype.exec', this]); 161 ['RegExp.prototype.exec', this]);
153 } 162 }
154 163
155 string = TO_STRING_INLINE(string); 164 string = TO_STRING_INLINE(string);
156 var lastIndex = this.lastIndex; 165 var lastIndex = this.lastIndex;
157 166
158 // Conversion is required by the ES5 specification (RegExp.prototype.exec 167 // Conversion is required by the ES5 specification (RegExp.prototype.exec
159 // algorithm, step 5) even if the value is discarded for non-global RegExps. 168 // algorithm, step 5) even if the value is discarded for non-global RegExps.
160 var i = TO_INTEGER(lastIndex); 169 var i = TO_INTEGER(lastIndex);
161 170
162 var global = this.global; 171 var updateLastIndex = this.global || (harmony_regexps && this.sticky);
163 if (global) { 172 if (updateLastIndex) {
164 if (i < 0 || i > string.length) { 173 if (i < 0 || i > string.length) {
165 this.lastIndex = 0; 174 this.lastIndex = 0;
166 return null; 175 return null;
167 } 176 }
168 } else { 177 } else {
169 i = 0; 178 i = 0;
170 } 179 }
171 180
172 // matchIndices is either null or the lastMatchInfo array. 181 // matchIndices is either null or the lastMatchInfo array.
173 var matchIndices = %_RegExpExec(this, string, i, lastMatchInfo); 182 var matchIndices = %_RegExpExec(this, string, i, lastMatchInfo);
174 183
175 if (IS_NULL(matchIndices)) { 184 if (IS_NULL(matchIndices)) {
176 this.lastIndex = 0; 185 this.lastIndex = 0;
177 return null; 186 return null;
178 } 187 }
179 188
180 // Successful match. 189 // Successful match.
181 lastMatchInfoOverride = null; 190 lastMatchInfoOverride = null;
182 if (global) { 191 if (updateLastIndex) {
183 this.lastIndex = lastMatchInfo[CAPTURE1]; 192 this.lastIndex = lastMatchInfo[CAPTURE1];
184 } 193 }
185 RETURN_NEW_RESULT_FROM_MATCH_INFO(matchIndices, string); 194 RETURN_NEW_RESULT_FROM_MATCH_INFO(matchIndices, string);
186 } 195 }
187 196
188 197
189 // One-element cache for the simplified test regexp. 198 // One-element cache for the simplified test regexp.
190 var regexp_key; 199 var regexp_key;
191 var regexp_val; 200 var regexp_val;
192 201
193 // Section 15.10.6.3 doesn't actually make sense, but the intention seems to be 202 // Section 15.10.6.3 doesn't actually make sense, but the intention seems to be
194 // that test is defined in terms of String.prototype.exec. However, it probably 203 // that test is defined in terms of String.prototype.exec. However, it probably
195 // means the original value of String.prototype.exec, which is what everybody 204 // means the original value of String.prototype.exec, which is what everybody
196 // else implements. 205 // else implements.
197 function RegExpTest(string) { 206 function RegExpTest(string) {
198 if (!IS_REGEXP(this)) { 207 if (!IS_REGEXP(this)) {
199 throw MakeTypeError('incompatible_method_receiver', 208 throw MakeTypeError('incompatible_method_receiver',
200 ['RegExp.prototype.test', this]); 209 ['RegExp.prototype.test', this]);
201 } 210 }
202 string = TO_STRING_INLINE(string); 211 string = TO_STRING_INLINE(string);
203 212
204 var lastIndex = this.lastIndex; 213 var lastIndex = this.lastIndex;
205 214
206 // Conversion is required by the ES5 specification (RegExp.prototype.exec 215 // Conversion is required by the ES5 specification (RegExp.prototype.exec
207 // algorithm, step 5) even if the value is discarded for non-global RegExps. 216 // algorithm, step 5) even if the value is discarded for non-global RegExps.
208 var i = TO_INTEGER(lastIndex); 217 var i = TO_INTEGER(lastIndex);
209 218
210 if (this.global) { 219 if (this.global || (harmony_regexps && this.sticky)) {
211 if (i < 0 || i > string.length) { 220 if (i < 0 || i > string.length) {
212 this.lastIndex = 0; 221 this.lastIndex = 0;
213 return false; 222 return false;
214 } 223 }
215 // matchIndices is either null or the lastMatchInfo array. 224 // matchIndices is either null or the lastMatchInfo array.
216 var matchIndices = %_RegExpExec(this, string, i, lastMatchInfo); 225 var matchIndices = %_RegExpExec(this, string, i, lastMatchInfo);
217 if (IS_NULL(matchIndices)) { 226 if (IS_NULL(matchIndices)) {
218 this.lastIndex = 0; 227 this.lastIndex = 0;
219 return false; 228 return false;
220 } 229 }
221 lastMatchInfoOverride = null; 230 lastMatchInfoOverride = null;
222 this.lastIndex = lastMatchInfo[CAPTURE1]; 231 this.lastIndex = lastMatchInfo[CAPTURE1];
223 return true; 232 return true;
224 } else { 233 } else {
225 // Non-global regexp. 234 // Non-global, non-sticky regexp.
226 // Remove irrelevant preceeding '.*' in a non-global test regexp. 235 // Remove irrelevant preceeding '.*' in a test regexp. The expression
227 // The expression checks whether this.source starts with '.*' and 236 // checks whether this.source starts with '.*' and that the third char is
228 // that the third char is not a '?'. 237 // not a '?'. But see https://code.google.com/p/v8/issues/detail?id=3560
229 var regexp = this; 238 var regexp = this;
230 if (%_StringCharCodeAt(regexp.source, 0) == 46 && // '.' 239 if (regexp.source.length >= 3 &&
240 %_StringCharCodeAt(regexp.source, 0) == 46 && // '.'
231 %_StringCharCodeAt(regexp.source, 1) == 42 && // '*' 241 %_StringCharCodeAt(regexp.source, 1) == 42 && // '*'
232 %_StringCharCodeAt(regexp.source, 2) != 63) { // '?' 242 %_StringCharCodeAt(regexp.source, 2) != 63) { // '?'
233 regexp = TrimRegExp(regexp); 243 regexp = TrimRegExp(regexp);
234 } 244 }
235 // matchIndices is either null or the lastMatchInfo array. 245 // matchIndices is either null or the lastMatchInfo array.
236 var matchIndices = %_RegExpExec(regexp, string, 0, lastMatchInfo); 246 var matchIndices = %_RegExpExec(regexp, string, 0, lastMatchInfo);
237 if (IS_NULL(matchIndices)) { 247 if (IS_NULL(matchIndices)) {
238 this.lastIndex = 0; 248 this.lastIndex = 0;
239 return false; 249 return false;
240 } 250 }
(...skipping 16 matching lines...) Expand all
257 267
258 function RegExpToString() { 268 function RegExpToString() {
259 if (!IS_REGEXP(this)) { 269 if (!IS_REGEXP(this)) {
260 throw MakeTypeError('incompatible_method_receiver', 270 throw MakeTypeError('incompatible_method_receiver',
261 ['RegExp.prototype.toString', this]); 271 ['RegExp.prototype.toString', this]);
262 } 272 }
263 var result = '/' + this.source + '/'; 273 var result = '/' + this.source + '/';
264 if (this.global) result += 'g'; 274 if (this.global) result += 'g';
265 if (this.ignoreCase) result += 'i'; 275 if (this.ignoreCase) result += 'i';
266 if (this.multiline) result += 'm'; 276 if (this.multiline) result += 'm';
277 if (harmony_regexps && this.sticky) result += 'y';
267 return result; 278 return result;
268 } 279 }
269 280
270 281
271 // Getters for the static properties lastMatch, lastParen, leftContext, and 282 // Getters for the static properties lastMatch, lastParen, leftContext, and
272 // rightContext of the RegExp constructor. The properties are computed based 283 // rightContext of the RegExp constructor. The properties are computed based
273 // on the captures array of the last successful match and the subject string 284 // on the captures array of the last successful match and the subject string
274 // of the last successful match. 285 // of the last successful match.
275 function RegExpGetLastMatch() { 286 function RegExpGetLastMatch() {
276 if (lastMatchInfoOverride !== null) { 287 if (lastMatchInfoOverride !== null) {
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after
455 466
456 for (var i = 1; i < 10; ++i) { 467 for (var i = 1; i < 10; ++i) {
457 %DefineAccessorPropertyUnchecked($RegExp, '$' + i, 468 %DefineAccessorPropertyUnchecked($RegExp, '$' + i,
458 RegExpMakeCaptureGetter(i), NoOpSetter, 469 RegExpMakeCaptureGetter(i), NoOpSetter,
459 DONT_DELETE); 470 DONT_DELETE);
460 } 471 }
461 %ToFastProperties($RegExp); 472 %ToFastProperties($RegExp);
462 } 473 }
463 474
464 SetUpRegExp(); 475 SetUpRegExp();
OLDNEW
« no previous file with comments | « src/objects.h ('k') | src/runtime.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698