| 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 // This file relies on the fact that the following declaration has been made | 5 var $regexpExec; |
| 6 // in runtime.js: | 6 var $regexpExecNoTests; |
| 7 // var $Object = global.Object; | 7 var $regexpLastMatchInfo; |
| 8 // var $Array = global.Array; | 8 var $regexpLastMatchInfoOverride; |
| 9 | 9 |
| 10 var $RegExp = global.RegExp; | 10 (function() { |
| 11 |
| 12 %CheckIsBootstrapping(); |
| 13 |
| 14 var GlobalRegExp = global.RegExp; |
| 15 var GlobalArray = global.Array; |
| 16 |
| 17 // Property of the builtins object for recording the result of the last |
| 18 // regexp match. The property $regexpLastMatchInfo includes the matchIndices |
| 19 // array of the last successful regexp match (an array of start/end index |
| 20 // pairs for the match and all the captured substrings), the invariant is |
| 21 // that there are at least two capture indeces. The array also contains |
| 22 // the subject string for the last successful match. |
| 23 $regexpLastMatchInfo = new InternalPackedArray( |
| 24 2, // REGEXP_NUMBER_OF_CAPTURES |
| 25 "", // Last subject. |
| 26 UNDEFINED, // Last input - settable with RegExpSetInput. |
| 27 0, // REGEXP_FIRST_CAPTURE + 0 |
| 28 0 // REGEXP_FIRST_CAPTURE + 1 |
| 29 ); |
| 30 |
| 31 // Override last match info with an array of actual substrings. |
| 32 // Used internally by replace regexp with function. |
| 33 // The array has the format of an "apply" argument for a replacement |
| 34 // function. |
| 35 $regexpLastMatchInfoOverride = null; |
| 11 | 36 |
| 12 // ------------------------------------------------------------------- | 37 // ------------------------------------------------------------------- |
| 13 | 38 |
| 14 // A recursive descent parser for Patterns according to the grammar of | 39 // A recursive descent parser for Patterns according to the grammar of |
| 15 // ECMA-262 15.10.1, with deviations noted below. | 40 // ECMA-262 15.10.1, with deviations noted below. |
| 16 function DoConstructRegExp(object, pattern, flags) { | 41 function DoConstructRegExp(object, pattern, flags) { |
| 17 // RegExp : Called as constructor; see ECMA-262, section 15.10.4. | 42 // RegExp : Called as constructor; see ECMA-262, section 15.10.4. |
| 18 if (IS_REGEXP(pattern)) { | 43 if (IS_REGEXP(pattern)) { |
| 19 if (!IS_UNDEFINED(flags)) { | 44 if (!IS_UNDEFINED(flags)) { |
| 20 throw MakeTypeError('regexp_flags', []); | 45 throw MakeTypeError('regexp_flags', []); |
| (...skipping 16 matching lines...) Expand all Loading... |
| 37 | 62 |
| 38 | 63 |
| 39 function RegExpConstructor(pattern, flags) { | 64 function RegExpConstructor(pattern, flags) { |
| 40 if (%_IsConstructCall()) { | 65 if (%_IsConstructCall()) { |
| 41 DoConstructRegExp(this, pattern, flags); | 66 DoConstructRegExp(this, pattern, flags); |
| 42 } else { | 67 } else { |
| 43 // RegExp : Called as function; see ECMA-262, section 15.10.3.1. | 68 // RegExp : Called as function; see ECMA-262, section 15.10.3.1. |
| 44 if (IS_REGEXP(pattern) && IS_UNDEFINED(flags)) { | 69 if (IS_REGEXP(pattern) && IS_UNDEFINED(flags)) { |
| 45 return pattern; | 70 return pattern; |
| 46 } | 71 } |
| 47 return new $RegExp(pattern, flags); | 72 return new GlobalRegExp(pattern, flags); |
| 48 } | 73 } |
| 49 } | 74 } |
| 50 | 75 |
| 51 // Deprecated RegExp.prototype.compile method. We behave like the constructor | 76 // Deprecated RegExp.prototype.compile method. We behave like the constructor |
| 52 // were called again. In SpiderMonkey, this method returns the regexp object. | 77 // were called again. In SpiderMonkey, this method returns the regexp object. |
| 53 // In JSC, it returns undefined. For compatibility with JSC, we match their | 78 // In JSC, it returns undefined. For compatibility with JSC, we match their |
| 54 // behavior. | 79 // behavior. |
| 55 function RegExpCompileJS(pattern, flags) { | 80 function RegExpCompileJS(pattern, flags) { |
| 56 // Both JSC and SpiderMonkey treat a missing pattern argument as the | 81 // Both JSC and SpiderMonkey treat a missing pattern argument as the |
| 57 // empty subject string, and an actual undefined value passed as the | 82 // empty subject string, and an actual undefined value passed as the |
| 58 // pattern as the string 'undefined'. Note that JSC is inconsistent | 83 // pattern as the string 'undefined'. Note that JSC is inconsistent |
| 59 // here, treating undefined values differently in | 84 // here, treating undefined values differently in |
| 60 // RegExp.prototype.compile and in the constructor, where they are | 85 // RegExp.prototype.compile and in the constructor, where they are |
| 61 // the empty string. For compatibility with JSC, we match their | 86 // the empty string. For compatibility with JSC, we match their |
| 62 // behavior. | 87 // behavior. |
| 63 if (this == $RegExp.prototype) { | 88 if (this == GlobalRegExp.prototype) { |
| 64 // We don't allow recompiling RegExp.prototype. | 89 // We don't allow recompiling RegExp.prototype. |
| 65 throw MakeTypeError('incompatible_method_receiver', | 90 throw MakeTypeError('incompatible_method_receiver', |
| 66 ['RegExp.prototype.compile', this]); | 91 ['RegExp.prototype.compile', this]); |
| 67 } | 92 } |
| 68 if (IS_UNDEFINED(pattern) && %_ArgumentsLength() != 0) { | 93 if (IS_UNDEFINED(pattern) && %_ArgumentsLength() != 0) { |
| 69 DoConstructRegExp(this, 'undefined', flags); | 94 DoConstructRegExp(this, 'undefined', flags); |
| 70 } else { | 95 } else { |
| 71 DoConstructRegExp(this, pattern, flags); | 96 DoConstructRegExp(this, pattern, flags); |
| 72 } | 97 } |
| 73 } | 98 } |
| 74 | 99 |
| 75 | 100 |
| 76 function DoRegExpExec(regexp, string, index) { | 101 function DoRegExpExec(regexp, string, index) { |
| 77 var result = %_RegExpExec(regexp, string, index, lastMatchInfo); | 102 var result = %_RegExpExec(regexp, string, index, $regexpLastMatchInfo); |
| 78 if (result !== null) lastMatchInfoOverride = null; | 103 if (result !== null) $regexpLastMatchInfoOverride = null; |
| 79 return result; | 104 return result; |
| 80 } | 105 } |
| 81 | 106 |
| 82 | 107 |
| 83 // This is kind of performance sensitive, so we want to avoid unnecessary | 108 // This is kind of performance sensitive, so we want to avoid unnecessary |
| 84 // type checks on inputs. But we also don't want to inline it several times | 109 // type checks on inputs. But we also don't want to inline it several times |
| 85 // manually, so we use a macro :-) | 110 // manually, so we use a macro :-) |
| 86 macro RETURN_NEW_RESULT_FROM_MATCH_INFO(MATCHINFO, STRING) | 111 macro RETURN_NEW_RESULT_FROM_MATCH_INFO(MATCHINFO, STRING) |
| 87 var numResults = NUMBER_OF_CAPTURES(MATCHINFO) >> 1; | 112 var numResults = NUMBER_OF_CAPTURES(MATCHINFO) >> 1; |
| 88 var start = MATCHINFO[CAPTURE0]; | 113 var start = MATCHINFO[CAPTURE0]; |
| (...skipping 12 matching lines...) Expand all Loading... |
| 101 result[i] = %_SubString(STRING, start, end); | 126 result[i] = %_SubString(STRING, start, end); |
| 102 } | 127 } |
| 103 j++; | 128 j++; |
| 104 } | 129 } |
| 105 return result; | 130 return result; |
| 106 endmacro | 131 endmacro |
| 107 | 132 |
| 108 | 133 |
| 109 function RegExpExecNoTests(regexp, string, start) { | 134 function RegExpExecNoTests(regexp, string, start) { |
| 110 // Must be called with RegExp, string and positive integer as arguments. | 135 // Must be called with RegExp, string and positive integer as arguments. |
| 111 var matchInfo = %_RegExpExec(regexp, string, start, lastMatchInfo); | 136 var matchInfo = %_RegExpExec(regexp, string, start, $regexpLastMatchInfo); |
| 112 if (matchInfo !== null) { | 137 if (matchInfo !== null) { |
| 113 lastMatchInfoOverride = null; | 138 $regexpLastMatchInfoOverride = null; |
| 114 RETURN_NEW_RESULT_FROM_MATCH_INFO(matchInfo, string); | 139 RETURN_NEW_RESULT_FROM_MATCH_INFO(matchInfo, string); |
| 115 } | 140 } |
| 116 regexp.lastIndex = 0; | 141 regexp.lastIndex = 0; |
| 117 return null; | 142 return null; |
| 118 } | 143 } |
| 119 | 144 |
| 120 | 145 |
| 121 function RegExpExec(string) { | 146 function RegExpExec(string) { |
| 122 if (!IS_REGEXP(this)) { | 147 if (!IS_REGEXP(this)) { |
| 123 throw MakeTypeError('incompatible_method_receiver', | 148 throw MakeTypeError('incompatible_method_receiver', |
| (...skipping 10 matching lines...) Expand all Loading... |
| 134 var updateLastIndex = this.global || (harmony_regexps && this.sticky); | 159 var updateLastIndex = this.global || (harmony_regexps && this.sticky); |
| 135 if (updateLastIndex) { | 160 if (updateLastIndex) { |
| 136 if (i < 0 || i > string.length) { | 161 if (i < 0 || i > string.length) { |
| 137 this.lastIndex = 0; | 162 this.lastIndex = 0; |
| 138 return null; | 163 return null; |
| 139 } | 164 } |
| 140 } else { | 165 } else { |
| 141 i = 0; | 166 i = 0; |
| 142 } | 167 } |
| 143 | 168 |
| 144 // matchIndices is either null or the lastMatchInfo array. | 169 // matchIndices is either null or the $regexpLastMatchInfo array. |
| 145 var matchIndices = %_RegExpExec(this, string, i, lastMatchInfo); | 170 var matchIndices = %_RegExpExec(this, string, i, $regexpLastMatchInfo); |
| 146 | 171 |
| 147 if (IS_NULL(matchIndices)) { | 172 if (IS_NULL(matchIndices)) { |
| 148 this.lastIndex = 0; | 173 this.lastIndex = 0; |
| 149 return null; | 174 return null; |
| 150 } | 175 } |
| 151 | 176 |
| 152 // Successful match. | 177 // Successful match. |
| 153 lastMatchInfoOverride = null; | 178 $regexpLastMatchInfoOverride = null; |
| 154 if (updateLastIndex) { | 179 if (updateLastIndex) { |
| 155 this.lastIndex = lastMatchInfo[CAPTURE1]; | 180 this.lastIndex = $regexpLastMatchInfo[CAPTURE1]; |
| 156 } | 181 } |
| 157 RETURN_NEW_RESULT_FROM_MATCH_INFO(matchIndices, string); | 182 RETURN_NEW_RESULT_FROM_MATCH_INFO(matchIndices, string); |
| 158 } | 183 } |
| 159 | 184 |
| 160 | 185 |
| 161 // One-element cache for the simplified test regexp. | 186 // One-element cache for the simplified test regexp. |
| 162 var regexp_key; | 187 var regexp_key; |
| 163 var regexp_val; | 188 var regexp_val; |
| 164 | 189 |
| 165 // Section 15.10.6.3 doesn't actually make sense, but the intention seems to be | 190 // Section 15.10.6.3 doesn't actually make sense, but the intention seems to be |
| (...skipping 11 matching lines...) Expand all Loading... |
| 177 | 202 |
| 178 // Conversion is required by the ES5 specification (RegExp.prototype.exec | 203 // Conversion is required by the ES5 specification (RegExp.prototype.exec |
| 179 // algorithm, step 5) even if the value is discarded for non-global RegExps. | 204 // algorithm, step 5) even if the value is discarded for non-global RegExps. |
| 180 var i = TO_INTEGER(lastIndex); | 205 var i = TO_INTEGER(lastIndex); |
| 181 | 206 |
| 182 if (this.global || (harmony_regexps && this.sticky)) { | 207 if (this.global || (harmony_regexps && this.sticky)) { |
| 183 if (i < 0 || i > string.length) { | 208 if (i < 0 || i > string.length) { |
| 184 this.lastIndex = 0; | 209 this.lastIndex = 0; |
| 185 return false; | 210 return false; |
| 186 } | 211 } |
| 187 // matchIndices is either null or the lastMatchInfo array. | 212 // matchIndices is either null or the $regexpLastMatchInfo array. |
| 188 var matchIndices = %_RegExpExec(this, string, i, lastMatchInfo); | 213 var matchIndices = %_RegExpExec(this, string, i, $regexpLastMatchInfo); |
| 189 if (IS_NULL(matchIndices)) { | 214 if (IS_NULL(matchIndices)) { |
| 190 this.lastIndex = 0; | 215 this.lastIndex = 0; |
| 191 return false; | 216 return false; |
| 192 } | 217 } |
| 193 lastMatchInfoOverride = null; | 218 $regexpLastMatchInfoOverride = null; |
| 194 this.lastIndex = lastMatchInfo[CAPTURE1]; | 219 this.lastIndex = $regexpLastMatchInfo[CAPTURE1]; |
| 195 return true; | 220 return true; |
| 196 } else { | 221 } else { |
| 197 // Non-global, non-sticky regexp. | 222 // Non-global, non-sticky regexp. |
| 198 // Remove irrelevant preceeding '.*' in a test regexp. The expression | 223 // Remove irrelevant preceeding '.*' in a test regexp. The expression |
| 199 // checks whether this.source starts with '.*' and that the third char is | 224 // checks whether this.source starts with '.*' and that the third char is |
| 200 // not a '?'. But see https://code.google.com/p/v8/issues/detail?id=3560 | 225 // not a '?'. But see https://code.google.com/p/v8/issues/detail?id=3560 |
| 201 var regexp = this; | 226 var regexp = this; |
| 202 if (regexp.source.length >= 3 && | 227 if (regexp.source.length >= 3 && |
| 203 %_StringCharCodeAt(regexp.source, 0) == 46 && // '.' | 228 %_StringCharCodeAt(regexp.source, 0) == 46 && // '.' |
| 204 %_StringCharCodeAt(regexp.source, 1) == 42 && // '*' | 229 %_StringCharCodeAt(regexp.source, 1) == 42 && // '*' |
| 205 %_StringCharCodeAt(regexp.source, 2) != 63) { // '?' | 230 %_StringCharCodeAt(regexp.source, 2) != 63) { // '?' |
| 206 regexp = TrimRegExp(regexp); | 231 regexp = TrimRegExp(regexp); |
| 207 } | 232 } |
| 208 // matchIndices is either null or the lastMatchInfo array. | 233 // matchIndices is either null or the $regexpLastMatchInfo array. |
| 209 var matchIndices = %_RegExpExec(regexp, string, 0, lastMatchInfo); | 234 var matchIndices = %_RegExpExec(regexp, string, 0, $regexpLastMatchInfo); |
| 210 if (IS_NULL(matchIndices)) { | 235 if (IS_NULL(matchIndices)) { |
| 211 this.lastIndex = 0; | 236 this.lastIndex = 0; |
| 212 return false; | 237 return false; |
| 213 } | 238 } |
| 214 lastMatchInfoOverride = null; | 239 $regexpLastMatchInfoOverride = null; |
| 215 return true; | 240 return true; |
| 216 } | 241 } |
| 217 } | 242 } |
| 218 | 243 |
| 219 function TrimRegExp(regexp) { | 244 function TrimRegExp(regexp) { |
| 220 if (!%_ObjectEquals(regexp_key, regexp)) { | 245 if (!%_ObjectEquals(regexp_key, regexp)) { |
| 221 regexp_key = regexp; | 246 regexp_key = regexp; |
| 222 regexp_val = | 247 regexp_val = |
| 223 new $RegExp(%_SubString(regexp.source, 2, regexp.source.length), | 248 new GlobalRegExp(%_SubString(regexp.source, 2, regexp.source.length), |
| 224 (regexp.ignoreCase ? regexp.multiline ? "im" : "i" | 249 (regexp.ignoreCase ? regexp.multiline ? "im" : "i" |
| 225 : regexp.multiline ? "m" : "")); | 250 : regexp.multiline ? "m" : "")); |
| 226 } | 251 } |
| 227 return regexp_val; | 252 return regexp_val; |
| 228 } | 253 } |
| 229 | 254 |
| 230 | 255 |
| 231 function RegExpToString() { | 256 function RegExpToString() { |
| 232 if (!IS_REGEXP(this)) { | 257 if (!IS_REGEXP(this)) { |
| 233 throw MakeTypeError('incompatible_method_receiver', | 258 throw MakeTypeError('incompatible_method_receiver', |
| 234 ['RegExp.prototype.toString', this]); | 259 ['RegExp.prototype.toString', this]); |
| 235 } | 260 } |
| 236 var result = '/' + this.source + '/'; | 261 var result = '/' + this.source + '/'; |
| 237 if (this.global) result += 'g'; | 262 if (this.global) result += 'g'; |
| 238 if (this.ignoreCase) result += 'i'; | 263 if (this.ignoreCase) result += 'i'; |
| 239 if (this.multiline) result += 'm'; | 264 if (this.multiline) result += 'm'; |
| 240 if (harmony_unicode_regexps && this.unicode) result += 'u'; | 265 if (harmony_unicode_regexps && this.unicode) result += 'u'; |
| 241 if (harmony_regexps && this.sticky) result += 'y'; | 266 if (harmony_regexps && this.sticky) result += 'y'; |
| 242 return result; | 267 return result; |
| 243 } | 268 } |
| 244 | 269 |
| 245 | 270 |
| 246 // Getters for the static properties lastMatch, lastParen, leftContext, and | 271 // Getters for the static properties lastMatch, lastParen, leftContext, and |
| 247 // rightContext of the RegExp constructor. The properties are computed based | 272 // rightContext of the RegExp constructor. The properties are computed based |
| 248 // on the captures array of the last successful match and the subject string | 273 // on the captures array of the last successful match and the subject string |
| 249 // of the last successful match. | 274 // of the last successful match. |
| 250 function RegExpGetLastMatch() { | 275 function RegExpGetLastMatch() { |
| 251 if (lastMatchInfoOverride !== null) { | 276 if ($regexpLastMatchInfoOverride !== null) { |
| 252 return OVERRIDE_MATCH(lastMatchInfoOverride); | 277 return OVERRIDE_MATCH($regexpLastMatchInfoOverride); |
| 253 } | 278 } |
| 254 var regExpSubject = LAST_SUBJECT(lastMatchInfo); | 279 var regExpSubject = LAST_SUBJECT($regexpLastMatchInfo); |
| 255 return %_SubString(regExpSubject, | 280 return %_SubString(regExpSubject, |
| 256 lastMatchInfo[CAPTURE0], | 281 $regexpLastMatchInfo[CAPTURE0], |
| 257 lastMatchInfo[CAPTURE1]); | 282 $regexpLastMatchInfo[CAPTURE1]); |
| 258 } | 283 } |
| 259 | 284 |
| 260 | 285 |
| 261 function RegExpGetLastParen() { | 286 function RegExpGetLastParen() { |
| 262 if (lastMatchInfoOverride) { | 287 if ($regexpLastMatchInfoOverride) { |
| 263 var override = lastMatchInfoOverride; | 288 var override = $regexpLastMatchInfoOverride; |
| 264 if (override.length <= 3) return ''; | 289 if (override.length <= 3) return ''; |
| 265 return override[override.length - 3]; | 290 return override[override.length - 3]; |
| 266 } | 291 } |
| 267 var length = NUMBER_OF_CAPTURES(lastMatchInfo); | 292 var length = NUMBER_OF_CAPTURES($regexpLastMatchInfo); |
| 268 if (length <= 2) return ''; // There were no captures. | 293 if (length <= 2) return ''; // There were no captures. |
| 269 // We match the SpiderMonkey behavior: return the substring defined by the | 294 // We match the SpiderMonkey behavior: return the substring defined by the |
| 270 // last pair (after the first pair) of elements of the capture array even if | 295 // last pair (after the first pair) of elements of the capture array even if |
| 271 // it is empty. | 296 // it is empty. |
| 272 var regExpSubject = LAST_SUBJECT(lastMatchInfo); | 297 var regExpSubject = LAST_SUBJECT($regexpLastMatchInfo); |
| 273 var start = lastMatchInfo[CAPTURE(length - 2)]; | 298 var start = $regexpLastMatchInfo[CAPTURE(length - 2)]; |
| 274 var end = lastMatchInfo[CAPTURE(length - 1)]; | 299 var end = $regexpLastMatchInfo[CAPTURE(length - 1)]; |
| 275 if (start != -1 && end != -1) { | 300 if (start != -1 && end != -1) { |
| 276 return %_SubString(regExpSubject, start, end); | 301 return %_SubString(regExpSubject, start, end); |
| 277 } | 302 } |
| 278 return ""; | 303 return ""; |
| 279 } | 304 } |
| 280 | 305 |
| 281 | 306 |
| 282 function RegExpGetLeftContext() { | 307 function RegExpGetLeftContext() { |
| 283 var start_index; | 308 var start_index; |
| 284 var subject; | 309 var subject; |
| 285 if (!lastMatchInfoOverride) { | 310 if (!$regexpLastMatchInfoOverride) { |
| 286 start_index = lastMatchInfo[CAPTURE0]; | 311 start_index = $regexpLastMatchInfo[CAPTURE0]; |
| 287 subject = LAST_SUBJECT(lastMatchInfo); | 312 subject = LAST_SUBJECT($regexpLastMatchInfo); |
| 288 } else { | 313 } else { |
| 289 var override = lastMatchInfoOverride; | 314 var override = $regexpLastMatchInfoOverride; |
| 290 start_index = OVERRIDE_POS(override); | 315 start_index = OVERRIDE_POS(override); |
| 291 subject = OVERRIDE_SUBJECT(override); | 316 subject = OVERRIDE_SUBJECT(override); |
| 292 } | 317 } |
| 293 return %_SubString(subject, 0, start_index); | 318 return %_SubString(subject, 0, start_index); |
| 294 } | 319 } |
| 295 | 320 |
| 296 | 321 |
| 297 function RegExpGetRightContext() { | 322 function RegExpGetRightContext() { |
| 298 var start_index; | 323 var start_index; |
| 299 var subject; | 324 var subject; |
| 300 if (!lastMatchInfoOverride) { | 325 if (!$regexpLastMatchInfoOverride) { |
| 301 start_index = lastMatchInfo[CAPTURE1]; | 326 start_index = $regexpLastMatchInfo[CAPTURE1]; |
| 302 subject = LAST_SUBJECT(lastMatchInfo); | 327 subject = LAST_SUBJECT($regexpLastMatchInfo); |
| 303 } else { | 328 } else { |
| 304 var override = lastMatchInfoOverride; | 329 var override = $regexpLastMatchInfoOverride; |
| 305 subject = OVERRIDE_SUBJECT(override); | 330 subject = OVERRIDE_SUBJECT(override); |
| 306 var match = OVERRIDE_MATCH(override); | 331 var match = OVERRIDE_MATCH(override); |
| 307 start_index = OVERRIDE_POS(override) + match.length; | 332 start_index = OVERRIDE_POS(override) + match.length; |
| 308 } | 333 } |
| 309 return %_SubString(subject, start_index, subject.length); | 334 return %_SubString(subject, start_index, subject.length); |
| 310 } | 335 } |
| 311 | 336 |
| 312 | 337 |
| 313 // The properties $1..$9 are the first nine capturing substrings of the last | 338 // The properties $1..$9 are the first nine capturing substrings of the last |
| 314 // successful match, or ''. The function RegExpMakeCaptureGetter will be | 339 // successful match, or ''. The function RegExpMakeCaptureGetter will be |
| 315 // called with indices from 1 to 9. | 340 // called with indices from 1 to 9. |
| 316 function RegExpMakeCaptureGetter(n) { | 341 function RegExpMakeCaptureGetter(n) { |
| 317 return function() { | 342 return function foo() { |
| 318 if (lastMatchInfoOverride) { | 343 if ($regexpLastMatchInfoOverride) { |
| 319 if (n < lastMatchInfoOverride.length - 2) { | 344 if (n < $regexpLastMatchInfoOverride.length - 2) { |
| 320 return OVERRIDE_CAPTURE(lastMatchInfoOverride, n); | 345 return OVERRIDE_CAPTURE($regexpLastMatchInfoOverride, n); |
| 321 } | 346 } |
| 322 return ''; | 347 return ''; |
| 323 } | 348 } |
| 324 var index = n * 2; | 349 var index = n * 2; |
| 325 if (index >= NUMBER_OF_CAPTURES(lastMatchInfo)) return ''; | 350 if (index >= NUMBER_OF_CAPTURES($regexpLastMatchInfo)) return ''; |
| 326 var matchStart = lastMatchInfo[CAPTURE(index)]; | 351 var matchStart = $regexpLastMatchInfo[CAPTURE(index)]; |
| 327 var matchEnd = lastMatchInfo[CAPTURE(index + 1)]; | 352 var matchEnd = $regexpLastMatchInfo[CAPTURE(index + 1)]; |
| 328 if (matchStart == -1 || matchEnd == -1) return ''; | 353 if (matchStart == -1 || matchEnd == -1) return ''; |
| 329 return %_SubString(LAST_SUBJECT(lastMatchInfo), matchStart, matchEnd); | 354 return %_SubString(LAST_SUBJECT($regexpLastMatchInfo), matchStart, matchEnd)
; |
| 330 }; | 355 }; |
| 331 } | 356 } |
| 332 | 357 |
| 333 | |
| 334 // Property of the builtins object for recording the result of the last | |
| 335 // regexp match. The property lastMatchInfo includes the matchIndices | |
| 336 // array of the last successful regexp match (an array of start/end index | |
| 337 // pairs for the match and all the captured substrings), the invariant is | |
| 338 // that there are at least two capture indeces. The array also contains | |
| 339 // the subject string for the last successful match. | |
| 340 var lastMatchInfo = new InternalPackedArray( | |
| 341 2, // REGEXP_NUMBER_OF_CAPTURES | |
| 342 "", // Last subject. | |
| 343 UNDEFINED, // Last input - settable with RegExpSetInput. | |
| 344 0, // REGEXP_FIRST_CAPTURE + 0 | |
| 345 0 // REGEXP_FIRST_CAPTURE + 1 | |
| 346 ); | |
| 347 | |
| 348 // Override last match info with an array of actual substrings. | |
| 349 // Used internally by replace regexp with function. | |
| 350 // The array has the format of an "apply" argument for a replacement | |
| 351 // function. | |
| 352 var lastMatchInfoOverride = null; | |
| 353 | |
| 354 // ------------------------------------------------------------------- | 358 // ------------------------------------------------------------------- |
| 355 | 359 |
| 356 function SetUpRegExp() { | 360 %FunctionSetInstanceClassName(GlobalRegExp, 'RegExp'); |
| 357 %CheckIsBootstrapping(); | 361 %AddNamedProperty( |
| 358 %FunctionSetInstanceClassName($RegExp, 'RegExp'); | 362 GlobalRegExp.prototype, 'constructor', GlobalRegExp, DONT_ENUM); |
| 359 %AddNamedProperty($RegExp.prototype, 'constructor', $RegExp, DONT_ENUM); | 363 %SetCode(GlobalRegExp, RegExpConstructor); |
| 360 %SetCode($RegExp, RegExpConstructor); | |
| 361 | 364 |
| 362 InstallFunctions($RegExp.prototype, DONT_ENUM, $Array( | 365 InstallFunctions(GlobalRegExp.prototype, DONT_ENUM, GlobalArray( |
| 363 "exec", RegExpExec, | 366 "exec", RegExpExec, |
| 364 "test", RegExpTest, | 367 "test", RegExpTest, |
| 365 "toString", RegExpToString, | 368 "toString", RegExpToString, |
| 366 "compile", RegExpCompileJS | 369 "compile", RegExpCompileJS |
| 367 )); | 370 )); |
| 368 | 371 |
| 369 // The length of compile is 1 in SpiderMonkey. | 372 // The length of compile is 1 in SpiderMonkey. |
| 370 %FunctionSetLength($RegExp.prototype.compile, 1); | 373 %FunctionSetLength(GlobalRegExp.prototype.compile, 1); |
| 371 | 374 |
| 372 // The properties `input` and `$_` are aliases for each other. When this | 375 // The properties `input` and `$_` are aliases for each other. When this |
| 373 // value is set the value it is set to is coerced to a string. | 376 // value is set the value it is set to is coerced to a string. |
| 374 // Getter and setter for the input. | 377 // Getter and setter for the input. |
| 375 var RegExpGetInput = function() { | 378 var RegExpGetInput = function() { |
| 376 var regExpInput = LAST_INPUT(lastMatchInfo); | 379 var regExpInput = LAST_INPUT($regexpLastMatchInfo); |
| 377 return IS_UNDEFINED(regExpInput) ? "" : regExpInput; | 380 return IS_UNDEFINED(regExpInput) ? "" : regExpInput; |
| 378 }; | 381 }; |
| 379 var RegExpSetInput = function(string) { | 382 var RegExpSetInput = function(string) { |
| 380 LAST_INPUT(lastMatchInfo) = ToString(string); | 383 LAST_INPUT($regexpLastMatchInfo) = ToString(string); |
| 381 }; | 384 }; |
| 382 | 385 |
| 383 %OptimizeObjectForAddingMultipleProperties($RegExp, 22); | 386 %OptimizeObjectForAddingMultipleProperties(GlobalRegExp, 22); |
| 384 %DefineAccessorPropertyUnchecked($RegExp, 'input', RegExpGetInput, | 387 %DefineAccessorPropertyUnchecked(GlobalRegExp, 'input', RegExpGetInput, |
| 385 RegExpSetInput, DONT_DELETE); | 388 RegExpSetInput, DONT_DELETE); |
| 386 %DefineAccessorPropertyUnchecked($RegExp, '$_', RegExpGetInput, | 389 %DefineAccessorPropertyUnchecked(GlobalRegExp, '$_', RegExpGetInput, |
| 387 RegExpSetInput, DONT_ENUM | DONT_DELETE); | 390 RegExpSetInput, DONT_ENUM | DONT_DELETE); |
| 388 | 391 |
| 389 // The properties multiline and $* are aliases for each other. When this | 392 // The properties multiline and $* are aliases for each other. When this |
| 390 // value is set in SpiderMonkey, the value it is set to is coerced to a | 393 // value is set in SpiderMonkey, the value it is set to is coerced to a |
| 391 // boolean. We mimic that behavior with a slight difference: in SpiderMonkey | 394 // boolean. We mimic that behavior with a slight difference: in SpiderMonkey |
| 392 // the value of the expression 'RegExp.multiline = null' (for instance) is the | 395 // the value of the expression 'RegExp.multiline = null' (for instance) is the |
| 393 // boolean false (i.e., the value after coercion), while in V8 it is the value | 396 // boolean false (i.e., the value after coercion), while in V8 it is the value |
| 394 // null (i.e., the value before coercion). | 397 // null (i.e., the value before coercion). |
| 395 | 398 |
| 396 // Getter and setter for multiline. | 399 // Getter and setter for multiline. |
| 397 var multiline = false; | 400 var multiline = false; |
| 398 var RegExpGetMultiline = function() { return multiline; }; | 401 var RegExpGetMultiline = function() { return multiline; }; |
| 399 var RegExpSetMultiline = function(flag) { multiline = flag ? true : false; }; | 402 var RegExpSetMultiline = function(flag) { multiline = flag ? true : false; }; |
| 400 | 403 |
| 401 %DefineAccessorPropertyUnchecked($RegExp, 'multiline', RegExpGetMultiline, | 404 %DefineAccessorPropertyUnchecked(GlobalRegExp, 'multiline', RegExpGetMultiline, |
| 402 RegExpSetMultiline, DONT_DELETE); | 405 RegExpSetMultiline, DONT_DELETE); |
| 403 %DefineAccessorPropertyUnchecked($RegExp, '$*', RegExpGetMultiline, | 406 %DefineAccessorPropertyUnchecked(GlobalRegExp, '$*', RegExpGetMultiline, |
| 404 RegExpSetMultiline, | 407 RegExpSetMultiline, |
| 405 DONT_ENUM | DONT_DELETE); | 408 DONT_ENUM | DONT_DELETE); |
| 406 | 409 |
| 407 | 410 |
| 408 var NoOpSetter = function(ignored) {}; | 411 var NoOpSetter = function(ignored) {}; |
| 409 | 412 |
| 410 | 413 |
| 411 // Static properties set by a successful match. | 414 // Static properties set by a successful match. |
| 412 %DefineAccessorPropertyUnchecked($RegExp, 'lastMatch', RegExpGetLastMatch, | 415 %DefineAccessorPropertyUnchecked(GlobalRegExp, 'lastMatch', RegExpGetLastMatch, |
| 413 NoOpSetter, DONT_DELETE); | 416 NoOpSetter, DONT_DELETE); |
| 414 %DefineAccessorPropertyUnchecked($RegExp, '$&', RegExpGetLastMatch, | 417 %DefineAccessorPropertyUnchecked(GlobalRegExp, '$&', RegExpGetLastMatch, |
| 415 NoOpSetter, DONT_ENUM | DONT_DELETE); | 418 NoOpSetter, DONT_ENUM | DONT_DELETE); |
| 416 %DefineAccessorPropertyUnchecked($RegExp, 'lastParen', RegExpGetLastParen, | 419 %DefineAccessorPropertyUnchecked(GlobalRegExp, 'lastParen', RegExpGetLastParen, |
| 417 NoOpSetter, DONT_DELETE); | 420 NoOpSetter, DONT_DELETE); |
| 418 %DefineAccessorPropertyUnchecked($RegExp, '$+', RegExpGetLastParen, | 421 %DefineAccessorPropertyUnchecked(GlobalRegExp, '$+', RegExpGetLastParen, |
| 419 NoOpSetter, DONT_ENUM | DONT_DELETE); | 422 NoOpSetter, DONT_ENUM | DONT_DELETE); |
| 420 %DefineAccessorPropertyUnchecked($RegExp, 'leftContext', | 423 %DefineAccessorPropertyUnchecked(GlobalRegExp, 'leftContext', |
| 421 RegExpGetLeftContext, NoOpSetter, | 424 RegExpGetLeftContext, NoOpSetter, |
| 425 DONT_DELETE); |
| 426 %DefineAccessorPropertyUnchecked(GlobalRegExp, '$`', RegExpGetLeftContext, |
| 427 NoOpSetter, DONT_ENUM | DONT_DELETE); |
| 428 %DefineAccessorPropertyUnchecked(GlobalRegExp, 'rightContext', |
| 429 RegExpGetRightContext, NoOpSetter, |
| 430 DONT_DELETE); |
| 431 %DefineAccessorPropertyUnchecked(GlobalRegExp, "$'", RegExpGetRightContext, |
| 432 NoOpSetter, DONT_ENUM | DONT_DELETE); |
| 433 |
| 434 for (var i = 1; i < 10; ++i) { |
| 435 %DefineAccessorPropertyUnchecked(GlobalRegExp, '$' + i, |
| 436 RegExpMakeCaptureGetter(i), NoOpSetter, |
| 422 DONT_DELETE); | 437 DONT_DELETE); |
| 423 %DefineAccessorPropertyUnchecked($RegExp, '$`', RegExpGetLeftContext, | 438 } |
| 424 NoOpSetter, DONT_ENUM | DONT_DELETE); | 439 %ToFastProperties(GlobalRegExp); |
| 425 %DefineAccessorPropertyUnchecked($RegExp, 'rightContext', | |
| 426 RegExpGetRightContext, NoOpSetter, | |
| 427 DONT_DELETE); | |
| 428 %DefineAccessorPropertyUnchecked($RegExp, "$'", RegExpGetRightContext, | |
| 429 NoOpSetter, DONT_ENUM | DONT_DELETE); | |
| 430 | 440 |
| 431 for (var i = 1; i < 10; ++i) { | 441 $regexpExecNoTests = RegExpExecNoTests; |
| 432 %DefineAccessorPropertyUnchecked($RegExp, '$' + i, | 442 $regexpExec = DoRegExpExec; |
| 433 RegExpMakeCaptureGetter(i), NoOpSetter, | |
| 434 DONT_DELETE); | |
| 435 } | |
| 436 %ToFastProperties($RegExp); | |
| 437 } | |
| 438 | 443 |
| 439 SetUpRegExp(); | 444 })(); |
| OLD | NEW |