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 var $regexpLastMatchInfoOverride; | |
6 | |
7 (function(global, utils) { | 5 (function(global, utils) { |
8 | 6 |
9 %CheckIsBootstrapping(); | 7 %CheckIsBootstrapping(); |
10 | 8 |
11 // ------------------------------------------------------------------- | 9 // ------------------------------------------------------------------- |
12 // Imports | 10 // Imports |
13 | 11 |
14 var FLAG_harmony_regexps; | 12 var FLAG_harmony_regexps; |
15 var FLAG_harmony_tolength; | 13 var FLAG_harmony_tolength; |
16 var FLAG_harmony_unicode_regexps; | 14 var FLAG_harmony_unicode_regexps; |
(...skipping 20 matching lines...) Expand all Loading... |
37 // that there are at least two capture indeces. The array also contains | 35 // that there are at least two capture indeces. The array also contains |
38 // the subject string for the last successful match. | 36 // the subject string for the last successful match. |
39 var RegExpLastMatchInfo = new InternalPackedArray( | 37 var RegExpLastMatchInfo = new InternalPackedArray( |
40 2, // REGEXP_NUMBER_OF_CAPTURES | 38 2, // REGEXP_NUMBER_OF_CAPTURES |
41 "", // Last subject. | 39 "", // Last subject. |
42 UNDEFINED, // Last input - settable with RegExpSetInput. | 40 UNDEFINED, // Last input - settable with RegExpSetInput. |
43 0, // REGEXP_FIRST_CAPTURE + 0 | 41 0, // REGEXP_FIRST_CAPTURE + 0 |
44 0 // REGEXP_FIRST_CAPTURE + 1 | 42 0 // REGEXP_FIRST_CAPTURE + 1 |
45 ); | 43 ); |
46 | 44 |
47 // Override last match info with an array of actual substrings. | |
48 // Used internally by replace regexp with function. | |
49 // The array has the format of an "apply" argument for a replacement | |
50 // function. | |
51 $regexpLastMatchInfoOverride = null; | |
52 | |
53 // ------------------------------------------------------------------- | 45 // ------------------------------------------------------------------- |
54 | 46 |
55 // A recursive descent parser for Patterns according to the grammar of | 47 // A recursive descent parser for Patterns according to the grammar of |
56 // ECMA-262 15.10.1, with deviations noted below. | 48 // ECMA-262 15.10.1, with deviations noted below. |
57 function DoConstructRegExp(object, pattern, flags) { | 49 function DoConstructRegExp(object, pattern, flags) { |
58 // RegExp : Called as constructor; see ECMA-262, section 15.10.4. | 50 // RegExp : Called as constructor; see ECMA-262, section 15.10.4. |
59 if (IS_REGEXP(pattern)) { | 51 if (IS_REGEXP(pattern)) { |
60 if (!IS_UNDEFINED(flags)) throw MakeTypeError(kRegExpFlags); | 52 if (!IS_UNDEFINED(flags)) throw MakeTypeError(kRegExpFlags); |
61 flags = (pattern.global ? 'g' : '') | 53 flags = (pattern.global ? 'g' : '') |
62 + (pattern.ignoreCase ? 'i' : '') | 54 + (pattern.ignoreCase ? 'i' : '') |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
107 if (IS_UNDEFINED(pattern) && %_ArgumentsLength() != 0) { | 99 if (IS_UNDEFINED(pattern) && %_ArgumentsLength() != 0) { |
108 DoConstructRegExp(this, 'undefined', flags); | 100 DoConstructRegExp(this, 'undefined', flags); |
109 } else { | 101 } else { |
110 DoConstructRegExp(this, pattern, flags); | 102 DoConstructRegExp(this, pattern, flags); |
111 } | 103 } |
112 } | 104 } |
113 | 105 |
114 | 106 |
115 function DoRegExpExec(regexp, string, index) { | 107 function DoRegExpExec(regexp, string, index) { |
116 var result = %_RegExpExec(regexp, string, index, RegExpLastMatchInfo); | 108 var result = %_RegExpExec(regexp, string, index, RegExpLastMatchInfo); |
117 if (result !== null) $regexpLastMatchInfoOverride = null; | |
118 return result; | 109 return result; |
119 } | 110 } |
120 | 111 |
121 | 112 |
122 // This is kind of performance sensitive, so we want to avoid unnecessary | 113 // This is kind of performance sensitive, so we want to avoid unnecessary |
123 // type checks on inputs. But we also don't want to inline it several times | 114 // type checks on inputs. But we also don't want to inline it several times |
124 // manually, so we use a macro :-) | 115 // manually, so we use a macro :-) |
125 macro RETURN_NEW_RESULT_FROM_MATCH_INFO(MATCHINFO, STRING) | 116 macro RETURN_NEW_RESULT_FROM_MATCH_INFO(MATCHINFO, STRING) |
126 var numResults = NUMBER_OF_CAPTURES(MATCHINFO) >> 1; | 117 var numResults = NUMBER_OF_CAPTURES(MATCHINFO) >> 1; |
127 var start = MATCHINFO[CAPTURE0]; | 118 var start = MATCHINFO[CAPTURE0]; |
(...skipping 14 matching lines...) Expand all Loading... |
142 j++; | 133 j++; |
143 } | 134 } |
144 return result; | 135 return result; |
145 endmacro | 136 endmacro |
146 | 137 |
147 | 138 |
148 function RegExpExecNoTests(regexp, string, start) { | 139 function RegExpExecNoTests(regexp, string, start) { |
149 // Must be called with RegExp, string and positive integer as arguments. | 140 // Must be called with RegExp, string and positive integer as arguments. |
150 var matchInfo = %_RegExpExec(regexp, string, start, RegExpLastMatchInfo); | 141 var matchInfo = %_RegExpExec(regexp, string, start, RegExpLastMatchInfo); |
151 if (matchInfo !== null) { | 142 if (matchInfo !== null) { |
152 $regexpLastMatchInfoOverride = null; | |
153 // ES6 21.2.5.2.2 step 18. | 143 // ES6 21.2.5.2.2 step 18. |
154 if (FLAG_harmony_regexps && regexp.sticky) { | 144 if (FLAG_harmony_regexps && regexp.sticky) { |
155 regexp.lastIndex = matchInfo[CAPTURE1]; | 145 regexp.lastIndex = matchInfo[CAPTURE1]; |
156 } | 146 } |
157 RETURN_NEW_RESULT_FROM_MATCH_INFO(matchInfo, string); | 147 RETURN_NEW_RESULT_FROM_MATCH_INFO(matchInfo, string); |
158 } | 148 } |
159 regexp.lastIndex = 0; | 149 regexp.lastIndex = 0; |
160 return null; | 150 return null; |
161 } | 151 } |
162 | 152 |
(...skipping 23 matching lines...) Expand all Loading... |
186 | 176 |
187 // matchIndices is either null or the RegExpLastMatchInfo array. | 177 // matchIndices is either null or the RegExpLastMatchInfo array. |
188 var matchIndices = %_RegExpExec(this, string, i, RegExpLastMatchInfo); | 178 var matchIndices = %_RegExpExec(this, string, i, RegExpLastMatchInfo); |
189 | 179 |
190 if (IS_NULL(matchIndices)) { | 180 if (IS_NULL(matchIndices)) { |
191 this.lastIndex = 0; | 181 this.lastIndex = 0; |
192 return null; | 182 return null; |
193 } | 183 } |
194 | 184 |
195 // Successful match. | 185 // Successful match. |
196 $regexpLastMatchInfoOverride = null; | |
197 if (updateLastIndex) { | 186 if (updateLastIndex) { |
198 this.lastIndex = RegExpLastMatchInfo[CAPTURE1]; | 187 this.lastIndex = RegExpLastMatchInfo[CAPTURE1]; |
199 } | 188 } |
200 RETURN_NEW_RESULT_FROM_MATCH_INFO(matchIndices, string); | 189 RETURN_NEW_RESULT_FROM_MATCH_INFO(matchIndices, string); |
201 } | 190 } |
202 | 191 |
203 | 192 |
204 // One-element cache for the simplified test regexp. | 193 // One-element cache for the simplified test regexp. |
205 var regexp_key; | 194 var regexp_key; |
206 var regexp_val; | 195 var regexp_val; |
(...skipping 19 matching lines...) Expand all Loading... |
226 if (i < 0 || i > string.length) { | 215 if (i < 0 || i > string.length) { |
227 this.lastIndex = 0; | 216 this.lastIndex = 0; |
228 return false; | 217 return false; |
229 } | 218 } |
230 // matchIndices is either null or the RegExpLastMatchInfo array. | 219 // matchIndices is either null or the RegExpLastMatchInfo array. |
231 var matchIndices = %_RegExpExec(this, string, i, RegExpLastMatchInfo); | 220 var matchIndices = %_RegExpExec(this, string, i, RegExpLastMatchInfo); |
232 if (IS_NULL(matchIndices)) { | 221 if (IS_NULL(matchIndices)) { |
233 this.lastIndex = 0; | 222 this.lastIndex = 0; |
234 return false; | 223 return false; |
235 } | 224 } |
236 $regexpLastMatchInfoOverride = null; | |
237 this.lastIndex = RegExpLastMatchInfo[CAPTURE1]; | 225 this.lastIndex = RegExpLastMatchInfo[CAPTURE1]; |
238 return true; | 226 return true; |
239 } else { | 227 } else { |
240 // Non-global, non-sticky regexp. | 228 // Non-global, non-sticky regexp. |
241 // Remove irrelevant preceeding '.*' in a test regexp. The expression | 229 // Remove irrelevant preceeding '.*' in a test regexp. The expression |
242 // checks whether this.source starts with '.*' and that the third char is | 230 // checks whether this.source starts with '.*' and that the third char is |
243 // not a '?'. But see https://code.google.com/p/v8/issues/detail?id=3560 | 231 // not a '?'. But see https://code.google.com/p/v8/issues/detail?id=3560 |
244 var regexp = this; | 232 var regexp = this; |
245 if (regexp.source.length >= 3 && | 233 if (regexp.source.length >= 3 && |
246 %_StringCharCodeAt(regexp.source, 0) == 46 && // '.' | 234 %_StringCharCodeAt(regexp.source, 0) == 46 && // '.' |
247 %_StringCharCodeAt(regexp.source, 1) == 42 && // '*' | 235 %_StringCharCodeAt(regexp.source, 1) == 42 && // '*' |
248 %_StringCharCodeAt(regexp.source, 2) != 63) { // '?' | 236 %_StringCharCodeAt(regexp.source, 2) != 63) { // '?' |
249 regexp = TrimRegExp(regexp); | 237 regexp = TrimRegExp(regexp); |
250 } | 238 } |
251 // matchIndices is either null or the RegExpLastMatchInfo array. | 239 // matchIndices is either null or the RegExpLastMatchInfo array. |
252 var matchIndices = %_RegExpExec(regexp, string, 0, RegExpLastMatchInfo); | 240 var matchIndices = %_RegExpExec(regexp, string, 0, RegExpLastMatchInfo); |
253 if (IS_NULL(matchIndices)) { | 241 if (IS_NULL(matchIndices)) { |
254 this.lastIndex = 0; | 242 this.lastIndex = 0; |
255 return false; | 243 return false; |
256 } | 244 } |
257 $regexpLastMatchInfoOverride = null; | |
258 return true; | 245 return true; |
259 } | 246 } |
260 } | 247 } |
261 | 248 |
262 function TrimRegExp(regexp) { | 249 function TrimRegExp(regexp) { |
263 if (!%_ObjectEquals(regexp_key, regexp)) { | 250 if (!%_ObjectEquals(regexp_key, regexp)) { |
264 regexp_key = regexp; | 251 regexp_key = regexp; |
265 regexp_val = | 252 regexp_val = |
266 new GlobalRegExp(%_SubString(regexp.source, 2, regexp.source.length), | 253 new GlobalRegExp(%_SubString(regexp.source, 2, regexp.source.length), |
267 (regexp.ignoreCase ? regexp.multiline ? "im" : "i" | 254 (regexp.ignoreCase ? regexp.multiline ? "im" : "i" |
(...skipping 16 matching lines...) Expand all Loading... |
284 if (FLAG_harmony_regexps && this.sticky) result += 'y'; | 271 if (FLAG_harmony_regexps && this.sticky) result += 'y'; |
285 return result; | 272 return result; |
286 } | 273 } |
287 | 274 |
288 | 275 |
289 // Getters for the static properties lastMatch, lastParen, leftContext, and | 276 // Getters for the static properties lastMatch, lastParen, leftContext, and |
290 // rightContext of the RegExp constructor. The properties are computed based | 277 // rightContext of the RegExp constructor. The properties are computed based |
291 // on the captures array of the last successful match and the subject string | 278 // on the captures array of the last successful match and the subject string |
292 // of the last successful match. | 279 // of the last successful match. |
293 function RegExpGetLastMatch() { | 280 function RegExpGetLastMatch() { |
294 if ($regexpLastMatchInfoOverride !== null) { | |
295 return OVERRIDE_MATCH($regexpLastMatchInfoOverride); | |
296 } | |
297 var regExpSubject = LAST_SUBJECT(RegExpLastMatchInfo); | 281 var regExpSubject = LAST_SUBJECT(RegExpLastMatchInfo); |
298 return %_SubString(regExpSubject, | 282 return %_SubString(regExpSubject, |
299 RegExpLastMatchInfo[CAPTURE0], | 283 RegExpLastMatchInfo[CAPTURE0], |
300 RegExpLastMatchInfo[CAPTURE1]); | 284 RegExpLastMatchInfo[CAPTURE1]); |
301 } | 285 } |
302 | 286 |
303 | 287 |
304 function RegExpGetLastParen() { | 288 function RegExpGetLastParen() { |
305 if ($regexpLastMatchInfoOverride) { | |
306 var override = $regexpLastMatchInfoOverride; | |
307 if (override.length <= 3) return ''; | |
308 return override[override.length - 3]; | |
309 } | |
310 var length = NUMBER_OF_CAPTURES(RegExpLastMatchInfo); | 289 var length = NUMBER_OF_CAPTURES(RegExpLastMatchInfo); |
311 if (length <= 2) return ''; // There were no captures. | 290 if (length <= 2) return ''; // There were no captures. |
312 // We match the SpiderMonkey behavior: return the substring defined by the | 291 // We match the SpiderMonkey behavior: return the substring defined by the |
313 // last pair (after the first pair) of elements of the capture array even if | 292 // last pair (after the first pair) of elements of the capture array even if |
314 // it is empty. | 293 // it is empty. |
315 var regExpSubject = LAST_SUBJECT(RegExpLastMatchInfo); | 294 var regExpSubject = LAST_SUBJECT(RegExpLastMatchInfo); |
316 var start = RegExpLastMatchInfo[CAPTURE(length - 2)]; | 295 var start = RegExpLastMatchInfo[CAPTURE(length - 2)]; |
317 var end = RegExpLastMatchInfo[CAPTURE(length - 1)]; | 296 var end = RegExpLastMatchInfo[CAPTURE(length - 1)]; |
318 if (start != -1 && end != -1) { | 297 if (start != -1 && end != -1) { |
319 return %_SubString(regExpSubject, start, end); | 298 return %_SubString(regExpSubject, start, end); |
320 } | 299 } |
321 return ""; | 300 return ""; |
322 } | 301 } |
323 | 302 |
324 | 303 |
325 function RegExpGetLeftContext() { | 304 function RegExpGetLeftContext() { |
326 var start_index; | 305 var start_index; |
327 var subject; | 306 var subject; |
328 if (!$regexpLastMatchInfoOverride) { | 307 start_index = RegExpLastMatchInfo[CAPTURE0]; |
329 start_index = RegExpLastMatchInfo[CAPTURE0]; | 308 subject = LAST_SUBJECT(RegExpLastMatchInfo); |
330 subject = LAST_SUBJECT(RegExpLastMatchInfo); | |
331 } else { | |
332 var override = $regexpLastMatchInfoOverride; | |
333 start_index = OVERRIDE_POS(override); | |
334 subject = OVERRIDE_SUBJECT(override); | |
335 } | |
336 return %_SubString(subject, 0, start_index); | 309 return %_SubString(subject, 0, start_index); |
337 } | 310 } |
338 | 311 |
339 | 312 |
340 function RegExpGetRightContext() { | 313 function RegExpGetRightContext() { |
341 var start_index; | 314 var start_index; |
342 var subject; | 315 var subject; |
343 if (!$regexpLastMatchInfoOverride) { | 316 start_index = RegExpLastMatchInfo[CAPTURE1]; |
344 start_index = RegExpLastMatchInfo[CAPTURE1]; | 317 subject = LAST_SUBJECT(RegExpLastMatchInfo); |
345 subject = LAST_SUBJECT(RegExpLastMatchInfo); | |
346 } else { | |
347 var override = $regexpLastMatchInfoOverride; | |
348 subject = OVERRIDE_SUBJECT(override); | |
349 var match = OVERRIDE_MATCH(override); | |
350 start_index = OVERRIDE_POS(override) + match.length; | |
351 } | |
352 return %_SubString(subject, start_index, subject.length); | 318 return %_SubString(subject, start_index, subject.length); |
353 } | 319 } |
354 | 320 |
355 | 321 |
356 // The properties $1..$9 are the first nine capturing substrings of the last | 322 // The properties $1..$9 are the first nine capturing substrings of the last |
357 // successful match, or ''. The function RegExpMakeCaptureGetter will be | 323 // successful match, or ''. The function RegExpMakeCaptureGetter will be |
358 // called with indices from 1 to 9. | 324 // called with indices from 1 to 9. |
359 function RegExpMakeCaptureGetter(n) { | 325 function RegExpMakeCaptureGetter(n) { |
360 return function foo() { | 326 return function foo() { |
361 if ($regexpLastMatchInfoOverride) { | |
362 if (n < $regexpLastMatchInfoOverride.length - 2) { | |
363 return OVERRIDE_CAPTURE($regexpLastMatchInfoOverride, n); | |
364 } | |
365 return ''; | |
366 } | |
367 var index = n * 2; | 327 var index = n * 2; |
368 if (index >= NUMBER_OF_CAPTURES(RegExpLastMatchInfo)) return ''; | 328 if (index >= NUMBER_OF_CAPTURES(RegExpLastMatchInfo)) return ''; |
369 var matchStart = RegExpLastMatchInfo[CAPTURE(index)]; | 329 var matchStart = RegExpLastMatchInfo[CAPTURE(index)]; |
370 var matchEnd = RegExpLastMatchInfo[CAPTURE(index + 1)]; | 330 var matchEnd = RegExpLastMatchInfo[CAPTURE(index + 1)]; |
371 if (matchStart == -1 || matchEnd == -1) return ''; | 331 if (matchStart == -1 || matchEnd == -1) return ''; |
372 return %_SubString(LAST_SUBJECT(RegExpLastMatchInfo), matchStart, matchEnd); | 332 return %_SubString(LAST_SUBJECT(RegExpLastMatchInfo), matchStart, matchEnd); |
373 }; | 333 }; |
374 } | 334 } |
375 | 335 |
376 // ------------------------------------------------------------------- | 336 // ------------------------------------------------------------------- |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
460 // Exports | 420 // Exports |
461 | 421 |
462 utils.Export(function(to) { | 422 utils.Export(function(to) { |
463 to.RegExpExec = DoRegExpExec; | 423 to.RegExpExec = DoRegExpExec; |
464 to.RegExpExecNoTests = RegExpExecNoTests; | 424 to.RegExpExecNoTests = RegExpExecNoTests; |
465 to.RegExpLastMatchInfo = RegExpLastMatchInfo; | 425 to.RegExpLastMatchInfo = RegExpLastMatchInfo; |
466 to.RegExpTest = RegExpTest; | 426 to.RegExpTest = RegExpTest; |
467 }); | 427 }); |
468 | 428 |
469 }) | 429 }) |
OLD | NEW |