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