OLD | NEW |
1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 (function(global, utils) { | 5 (function(global, utils) { |
6 | 6 |
7 %CheckIsBootstrapping(); | 7 %CheckIsBootstrapping(); |
8 | 8 |
9 // ------------------------------------------------------------------- | 9 // ------------------------------------------------------------------- |
10 // Imports | 10 // Imports |
11 | 11 |
12 var FLAG_harmony_regexps; | |
13 var FLAG_harmony_tolength; | 12 var FLAG_harmony_tolength; |
14 var FLAG_harmony_unicode_regexps; | |
15 var GlobalObject = global.Object; | 13 var GlobalObject = global.Object; |
16 var GlobalRegExp = global.RegExp; | 14 var GlobalRegExp = global.RegExp; |
17 var InternalPackedArray = utils.InternalPackedArray; | 15 var InternalPackedArray = utils.InternalPackedArray; |
18 var MakeTypeError; | 16 var MakeTypeError; |
| 17 var regExpFlagsSymbol = utils.ImportNow("regexp_flags_symbol"); |
| 18 var regExpSourceSymbol = utils.ImportNow("regexp_source_symbol"); |
19 | 19 |
20 utils.ImportFromExperimental(function(from) { | 20 utils.ImportFromExperimental(function(from) { |
21 FLAG_harmony_regexps = from.FLAG_harmony_regexps; | |
22 FLAG_harmony_tolength = from.FLAG_harmony_tolength; | 21 FLAG_harmony_tolength = from.FLAG_harmony_tolength; |
23 FLAG_harmony_unicode_regexps = from.FLAG_harmony_unicode_regexps; | |
24 }); | 22 }); |
25 | 23 |
26 utils.Import(function(from) { | 24 utils.Import(function(from) { |
27 MakeTypeError = from.MakeTypeError; | 25 MakeTypeError = from.MakeTypeError; |
28 }); | 26 }); |
29 | 27 |
30 // ------------------------------------------------------------------- | 28 // ------------------------------------------------------------------- |
31 | 29 |
32 // Property of the builtins object for recording the result of the last | 30 // Property of the builtins object for recording the result of the last |
33 // regexp match. The property RegExpLastMatchInfo includes the matchIndices | 31 // regexp match. The property RegExpLastMatchInfo includes the matchIndices |
(...skipping 10 matching lines...) Expand all Loading... |
44 ); | 42 ); |
45 | 43 |
46 // ------------------------------------------------------------------- | 44 // ------------------------------------------------------------------- |
47 | 45 |
48 // A recursive descent parser for Patterns according to the grammar of | 46 // A recursive descent parser for Patterns according to the grammar of |
49 // ECMA-262 15.10.1, with deviations noted below. | 47 // ECMA-262 15.10.1, with deviations noted below. |
50 function DoConstructRegExp(object, pattern, flags) { | 48 function DoConstructRegExp(object, pattern, flags) { |
51 // RegExp : Called as constructor; see ECMA-262, section 15.10.4. | 49 // RegExp : Called as constructor; see ECMA-262, section 15.10.4. |
52 if (IS_REGEXP(pattern)) { | 50 if (IS_REGEXP(pattern)) { |
53 if (!IS_UNDEFINED(flags)) throw MakeTypeError(kRegExpFlags); | 51 if (!IS_UNDEFINED(flags)) throw MakeTypeError(kRegExpFlags); |
54 flags = (pattern.global ? 'g' : '') | 52 flags = (REGEXP_GLOBAL(pattern) ? 'g' : '') |
55 + (pattern.ignoreCase ? 'i' : '') | 53 + (REGEXP_IGNORE_CASE(pattern) ? 'i' : '') |
56 + (pattern.multiline ? 'm' : ''); | 54 + (REGEXP_MULTILINE(pattern) ? 'm' : '') |
57 if (FLAG_harmony_unicode_regexps) | 55 + (REGEXP_UNICODE(pattern) ? 'u' : '') |
58 flags += (pattern.unicode ? 'u' : ''); | 56 + (REGEXP_STICKY(pattern) ? 'y' : ''); |
59 if (FLAG_harmony_regexps) | 57 pattern = REGEXP_SOURCE(pattern); |
60 flags += (pattern.sticky ? 'y' : ''); | |
61 pattern = pattern.source; | |
62 } | 58 } |
63 | 59 |
64 pattern = IS_UNDEFINED(pattern) ? '' : TO_STRING(pattern); | 60 pattern = IS_UNDEFINED(pattern) ? '' : TO_STRING(pattern); |
65 flags = IS_UNDEFINED(flags) ? '' : TO_STRING(flags); | 61 flags = IS_UNDEFINED(flags) ? '' : TO_STRING(flags); |
66 | 62 |
67 %RegExpInitializeAndCompile(object, pattern, flags); | 63 %RegExpInitializeAndCompile(object, pattern, flags); |
68 } | 64 } |
69 | 65 |
70 | 66 |
71 function RegExpConstructor(pattern, flags) { | 67 function RegExpConstructor(pattern, flags) { |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
135 } | 131 } |
136 return result; | 132 return result; |
137 endmacro | 133 endmacro |
138 | 134 |
139 | 135 |
140 function RegExpExecNoTests(regexp, string, start) { | 136 function RegExpExecNoTests(regexp, string, start) { |
141 // Must be called with RegExp, string and positive integer as arguments. | 137 // Must be called with RegExp, string and positive integer as arguments. |
142 var matchInfo = %_RegExpExec(regexp, string, start, RegExpLastMatchInfo); | 138 var matchInfo = %_RegExpExec(regexp, string, start, RegExpLastMatchInfo); |
143 if (matchInfo !== null) { | 139 if (matchInfo !== null) { |
144 // ES6 21.2.5.2.2 step 18. | 140 // ES6 21.2.5.2.2 step 18. |
145 if (FLAG_harmony_regexps && regexp.sticky) { | 141 if (REGEXP_STICKY(regexp)) regexp.lastIndex = matchInfo[CAPTURE1]; |
146 regexp.lastIndex = matchInfo[CAPTURE1]; | |
147 } | |
148 RETURN_NEW_RESULT_FROM_MATCH_INFO(matchInfo, string); | 142 RETURN_NEW_RESULT_FROM_MATCH_INFO(matchInfo, string); |
149 } | 143 } |
150 regexp.lastIndex = 0; | 144 regexp.lastIndex = 0; |
151 return null; | 145 return null; |
152 } | 146 } |
153 | 147 |
154 | 148 |
155 function RegExpExecJS(string) { | 149 function RegExpExecJS(string) { |
156 if (!IS_REGEXP(this)) { | 150 if (!IS_REGEXP(this)) { |
157 throw MakeTypeError(kIncompatibleMethodReceiver, | 151 throw MakeTypeError(kIncompatibleMethodReceiver, |
158 'RegExp.prototype.exec', this); | 152 'RegExp.prototype.exec', this); |
159 } | 153 } |
160 | 154 |
161 string = TO_STRING(string); | 155 string = TO_STRING(string); |
162 var lastIndex = this.lastIndex; | 156 var lastIndex = this.lastIndex; |
163 | 157 |
164 // Conversion is required by the ES2015 specification (RegExpBuiltinExec | 158 // Conversion is required by the ES2015 specification (RegExpBuiltinExec |
165 // algorithm, step 4) even if the value is discarded for non-global RegExps. | 159 // algorithm, step 4) even if the value is discarded for non-global RegExps. |
166 var i = TO_LENGTH_OR_INTEGER(lastIndex); | 160 var i = TO_LENGTH_OR_INTEGER(lastIndex); |
167 | 161 |
168 var updateLastIndex = this.global || (FLAG_harmony_regexps && this.sticky); | 162 var updateLastIndex = REGEXP_GLOBAL(this) || REGEXP_STICKY(this); |
169 if (updateLastIndex) { | 163 if (updateLastIndex) { |
170 if (i < 0 || i > string.length) { | 164 if (i < 0 || i > string.length) { |
171 this.lastIndex = 0; | 165 this.lastIndex = 0; |
172 return null; | 166 return null; |
173 } | 167 } |
174 } else { | 168 } else { |
175 i = 0; | 169 i = 0; |
176 } | 170 } |
177 | 171 |
178 // matchIndices is either null or the RegExpLastMatchInfo array. | 172 // matchIndices is either null or the RegExpLastMatchInfo array. |
(...skipping 26 matching lines...) Expand all Loading... |
205 'RegExp.prototype.test', this); | 199 'RegExp.prototype.test', this); |
206 } | 200 } |
207 string = TO_STRING(string); | 201 string = TO_STRING(string); |
208 | 202 |
209 var lastIndex = this.lastIndex; | 203 var lastIndex = this.lastIndex; |
210 | 204 |
211 // Conversion is required by the ES2015 specification (RegExpBuiltinExec | 205 // Conversion is required by the ES2015 specification (RegExpBuiltinExec |
212 // algorithm, step 4) even if the value is discarded for non-global RegExps. | 206 // algorithm, step 4) even if the value is discarded for non-global RegExps. |
213 var i = TO_LENGTH_OR_INTEGER(lastIndex); | 207 var i = TO_LENGTH_OR_INTEGER(lastIndex); |
214 | 208 |
215 if (this.global || (FLAG_harmony_regexps && this.sticky)) { | 209 if (REGEXP_GLOBAL(this) || REGEXP_STICKY(this)) { |
216 if (i < 0 || i > string.length) { | 210 if (i < 0 || i > string.length) { |
217 this.lastIndex = 0; | 211 this.lastIndex = 0; |
218 return false; | 212 return false; |
219 } | 213 } |
220 // matchIndices is either null or the RegExpLastMatchInfo array. | 214 // matchIndices is either null or the RegExpLastMatchInfo array. |
221 var matchIndices = %_RegExpExec(this, string, i, RegExpLastMatchInfo); | 215 var matchIndices = %_RegExpExec(this, string, i, RegExpLastMatchInfo); |
222 if (IS_NULL(matchIndices)) { | 216 if (IS_NULL(matchIndices)) { |
223 this.lastIndex = 0; | 217 this.lastIndex = 0; |
224 return false; | 218 return false; |
225 } | 219 } |
226 this.lastIndex = RegExpLastMatchInfo[CAPTURE1]; | 220 this.lastIndex = RegExpLastMatchInfo[CAPTURE1]; |
227 return true; | 221 return true; |
228 } else { | 222 } else { |
229 // Non-global, non-sticky regexp. | 223 // Non-global, non-sticky regexp. |
230 // Remove irrelevant preceeding '.*' in a test regexp. The expression | 224 // Remove irrelevant preceeding '.*' in a test regexp. The expression |
231 // checks whether this.source starts with '.*' and that the third char is | 225 // checks whether this.source starts with '.*' and that the third char is |
232 // not a '?'. But see https://code.google.com/p/v8/issues/detail?id=3560 | 226 // not a '?'. But see https://code.google.com/p/v8/issues/detail?id=3560 |
233 var regexp = this; | 227 var regexp = this; |
234 if (regexp.source.length >= 3 && | 228 var source = REGEXP_SOURCE(regexp); |
235 %_StringCharCodeAt(regexp.source, 0) == 46 && // '.' | 229 if (regexp.length >= 3 && |
236 %_StringCharCodeAt(regexp.source, 1) == 42 && // '*' | 230 %_StringCharCodeAt(regexp, 0) == 46 && // '.' |
237 %_StringCharCodeAt(regexp.source, 2) != 63) { // '?' | 231 %_StringCharCodeAt(regexp, 1) == 42 && // '*' |
| 232 %_StringCharCodeAt(regexp, 2) != 63) { // '?' |
238 regexp = TrimRegExp(regexp); | 233 regexp = TrimRegExp(regexp); |
239 } | 234 } |
240 // matchIndices is either null or the RegExpLastMatchInfo array. | 235 // matchIndices is either null or the RegExpLastMatchInfo array. |
241 var matchIndices = %_RegExpExec(regexp, string, 0, RegExpLastMatchInfo); | 236 var matchIndices = %_RegExpExec(regexp, string, 0, RegExpLastMatchInfo); |
242 if (IS_NULL(matchIndices)) { | 237 if (IS_NULL(matchIndices)) { |
243 this.lastIndex = 0; | 238 this.lastIndex = 0; |
244 return false; | 239 return false; |
245 } | 240 } |
246 return true; | 241 return true; |
247 } | 242 } |
248 } | 243 } |
249 | 244 |
250 function TrimRegExp(regexp) { | 245 function TrimRegExp(regexp) { |
251 if (!%_ObjectEquals(regexp_key, regexp)) { | 246 if (!%_ObjectEquals(regexp_key, regexp)) { |
252 regexp_key = regexp; | 247 regexp_key = regexp; |
253 regexp_val = | 248 regexp_val = |
254 new GlobalRegExp(%_SubString(regexp.source, 2, regexp.source.length), | 249 new GlobalRegExp( |
255 (regexp.ignoreCase ? regexp.multiline ? "im" : "i" | 250 %_SubString(REGEXP_SOURCE(regexp), 2, REGEXP_SOURCE(regexp).length), |
256 : regexp.multiline ? "m" : "")); | 251 (REGEXP_IGNORE_CASE(regexp) ? REGEXP_MULTILINE(regexp) ? "im" : "i" |
| 252 : REGEXP_MULTILINE(regexp) ? "m" : "")); |
257 } | 253 } |
258 return regexp_val; | 254 return regexp_val; |
259 } | 255 } |
260 | 256 |
261 | 257 |
262 function RegExpToString() { | 258 function RegExpToString() { |
263 if (!IS_REGEXP(this)) { | 259 if (!IS_REGEXP(this)) { |
264 throw MakeTypeError(kIncompatibleMethodReceiver, | 260 throw MakeTypeError(kIncompatibleMethodReceiver, |
265 'RegExp.prototype.toString', this); | 261 'RegExp.prototype.toString', this); |
266 } | 262 } |
267 var result = '/' + this.source + '/'; | 263 var result = '/' + REGEXP_SOURCE(this) + '/'; |
268 if (this.global) result += 'g'; | 264 if (REGEXP_GLOBAL(this)) result += 'g'; |
269 if (this.ignoreCase) result += 'i'; | 265 if (REGEXP_IGNORE_CASE(this)) result += 'i'; |
270 if (this.multiline) result += 'm'; | 266 if (REGEXP_MULTILINE(this)) result += 'm'; |
271 if (FLAG_harmony_unicode_regexps && this.unicode) result += 'u'; | 267 if (REGEXP_UNICODE(this)) result += 'u'; |
272 if (FLAG_harmony_regexps && this.sticky) result += 'y'; | 268 if (REGEXP_STICKY(this)) result += 'y'; |
273 return result; | 269 return result; |
274 } | 270 } |
275 | 271 |
276 | 272 |
277 // Getters for the static properties lastMatch, lastParen, leftContext, and | 273 // Getters for the static properties lastMatch, lastParen, leftContext, and |
278 // rightContext of the RegExp constructor. The properties are computed based | 274 // rightContext of the RegExp constructor. The properties are computed based |
279 // on the captures array of the last successful match and the subject string | 275 // on the captures array of the last successful match and the subject string |
280 // of the last successful match. | 276 // of the last successful match. |
281 function RegExpGetLastMatch() { | 277 function RegExpGetLastMatch() { |
282 var regExpSubject = LAST_SUBJECT(RegExpLastMatchInfo); | 278 var regExpSubject = LAST_SUBJECT(RegExpLastMatchInfo); |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
327 return function foo() { | 323 return function foo() { |
328 var index = n * 2; | 324 var index = n * 2; |
329 if (index >= NUMBER_OF_CAPTURES(RegExpLastMatchInfo)) return ''; | 325 if (index >= NUMBER_OF_CAPTURES(RegExpLastMatchInfo)) return ''; |
330 var matchStart = RegExpLastMatchInfo[CAPTURE(index)]; | 326 var matchStart = RegExpLastMatchInfo[CAPTURE(index)]; |
331 var matchEnd = RegExpLastMatchInfo[CAPTURE(index + 1)]; | 327 var matchEnd = RegExpLastMatchInfo[CAPTURE(index + 1)]; |
332 if (matchStart == -1 || matchEnd == -1) return ''; | 328 if (matchStart == -1 || matchEnd == -1) return ''; |
333 return %_SubString(LAST_SUBJECT(RegExpLastMatchInfo), matchStart, matchEnd); | 329 return %_SubString(LAST_SUBJECT(RegExpLastMatchInfo), matchStart, matchEnd); |
334 }; | 330 }; |
335 } | 331 } |
336 | 332 |
| 333 |
| 334 // ES6 21.2.5.4. |
| 335 function RegExpGetGlobal() { |
| 336 if (!IS_REGEXP(this)) { |
| 337 throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.global"); |
| 338 } |
| 339 return !!REGEXP_GLOBAL(this); |
| 340 } |
| 341 %FunctionSetName(RegExpGetGlobal, "RegExp.prototype.global"); |
| 342 %SetNativeFlag(RegExpGetGlobal); |
| 343 |
| 344 |
| 345 // ES6 21.2.5.5. |
| 346 function RegExpGetIgnoreCase() { |
| 347 if (!IS_REGEXP(this)) { |
| 348 throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.ignoreCase"); |
| 349 } |
| 350 return !!REGEXP_IGNORE_CASE(this); |
| 351 } |
| 352 %FunctionSetName(RegExpGetIgnoreCase, "RegExp.prototype.ignoreCase"); |
| 353 %SetNativeFlag(RegExpGetIgnoreCase); |
| 354 |
| 355 |
| 356 // ES6 21.2.5.7. |
| 357 function RegExpGetMultiline() { |
| 358 if (!IS_REGEXP(this)) { |
| 359 throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.multiline"); |
| 360 } |
| 361 return !!REGEXP_MULTILINE(this); |
| 362 } |
| 363 %FunctionSetName(RegExpGetMultiline, "RegExp.prototype.multiline"); |
| 364 %SetNativeFlag(RegExpGetMultiline); |
| 365 |
| 366 |
| 367 // ES6 21.2.5.10. |
| 368 function RegExpGetSource() { |
| 369 if (!IS_REGEXP(this)) { |
| 370 throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.source"); |
| 371 } |
| 372 return REGEXP_SOURCE(this); |
| 373 } |
| 374 %FunctionSetName(RegExpGetSource, "RegExp.prototype.source"); |
| 375 %SetNativeFlag(RegExpGetSource); |
| 376 |
337 // ------------------------------------------------------------------- | 377 // ------------------------------------------------------------------- |
338 | 378 |
339 %FunctionSetInstanceClassName(GlobalRegExp, 'RegExp'); | 379 %FunctionSetInstanceClassName(GlobalRegExp, 'RegExp'); |
340 %FunctionSetPrototype(GlobalRegExp, new GlobalObject()); | 380 %FunctionSetPrototype(GlobalRegExp, new GlobalObject()); |
341 %AddNamedProperty( | 381 %AddNamedProperty( |
342 GlobalRegExp.prototype, 'constructor', GlobalRegExp, DONT_ENUM); | 382 GlobalRegExp.prototype, 'constructor', GlobalRegExp, DONT_ENUM); |
343 %SetCode(GlobalRegExp, RegExpConstructor); | 383 %SetCode(GlobalRegExp, RegExpConstructor); |
344 | 384 |
345 utils.InstallFunctions(GlobalRegExp.prototype, DONT_ENUM, [ | 385 utils.InstallFunctions(GlobalRegExp.prototype, DONT_ENUM, [ |
346 "exec", RegExpExecJS, | 386 "exec", RegExpExecJS, |
347 "test", RegExpTest, | 387 "test", RegExpTest, |
348 "toString", RegExpToString, | 388 "toString", RegExpToString, |
349 "compile", RegExpCompileJS | 389 "compile", RegExpCompileJS |
350 ]); | 390 ]); |
351 | 391 |
| 392 %DefineGetterPropertyUnchecked(GlobalRegExp.prototype, "global", |
| 393 RegExpGetGlobal, DONT_ENUM); |
| 394 %DefineGetterPropertyUnchecked(GlobalRegExp.prototype, "ignoreCase", |
| 395 RegExpGetIgnoreCase, DONT_ENUM); |
| 396 %DefineGetterPropertyUnchecked(GlobalRegExp.prototype, "multiline", |
| 397 RegExpGetMultiline, DONT_ENUM); |
| 398 %DefineGetterPropertyUnchecked(GlobalRegExp.prototype, "source", |
| 399 RegExpGetSource, DONT_ENUM); |
| 400 |
352 // The length of compile is 1 in SpiderMonkey. | 401 // The length of compile is 1 in SpiderMonkey. |
353 %FunctionSetLength(GlobalRegExp.prototype.compile, 1); | 402 %FunctionSetLength(GlobalRegExp.prototype.compile, 1); |
354 | 403 |
355 // The properties `input` and `$_` are aliases for each other. When this | 404 // The properties `input` and `$_` are aliases for each other. When this |
356 // value is set the value it is set to is coerced to a string. | 405 // value is set the value it is set to is coerced to a string. |
357 // Getter and setter for the input. | 406 // Getter and setter for the input. |
358 var RegExpGetInput = function() { | 407 var RegExpGetInput = function() { |
359 var regExpInput = LAST_INPUT(RegExpLastMatchInfo); | 408 var regExpInput = LAST_INPUT(RegExpLastMatchInfo); |
360 return IS_UNDEFINED(regExpInput) ? "" : regExpInput; | 409 return IS_UNDEFINED(regExpInput) ? "" : regExpInput; |
361 }; | 410 }; |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
422 // Exports | 471 // Exports |
423 | 472 |
424 utils.Export(function(to) { | 473 utils.Export(function(to) { |
425 to.RegExpExec = DoRegExpExec; | 474 to.RegExpExec = DoRegExpExec; |
426 to.RegExpExecNoTests = RegExpExecNoTests; | 475 to.RegExpExecNoTests = RegExpExecNoTests; |
427 to.RegExpLastMatchInfo = RegExpLastMatchInfo; | 476 to.RegExpLastMatchInfo = RegExpLastMatchInfo; |
428 to.RegExpTest = RegExpTest; | 477 to.RegExpTest = RegExpTest; |
429 }); | 478 }); |
430 | 479 |
431 }) | 480 }) |
OLD | NEW |