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