Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: src/regexp.js

Issue 4343003: Version 2.5.4 (Closed)
Patch Set: Created 10 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/preparser.h ('k') | src/runtime.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2006-2009 the V8 project authors. All rights reserved. 1 // Copyright 2006-2009 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without 2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are 3 // modification, are permitted provided that the following conditions are
4 // met: 4 // met:
5 // 5 //
6 // * Redistributions of source code must retain the above copyright 6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer. 7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above 8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following 9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided 10 // disclaimer in the documentation and/or other materials provided
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
64 case 'm': 64 case 'm':
65 multiline = true; 65 multiline = true;
66 break; 66 break;
67 default: 67 default:
68 // Ignore flags that have no meaning to be consistent with 68 // Ignore flags that have no meaning to be consistent with
69 // JSC. 69 // JSC.
70 break; 70 break;
71 } 71 }
72 } 72 }
73 73
74 if (!isConstructorCall) {
75 regExpCache.type = 'none';
76 }
77 %RegExpInitializeObject(object, pattern, global, ignoreCase, multiline); 74 %RegExpInitializeObject(object, pattern, global, ignoreCase, multiline);
78 75
79 // Call internal function to compile the pattern. 76 // Call internal function to compile the pattern.
80 %RegExpCompile(object, pattern, flags); 77 %RegExpCompile(object, pattern, flags);
81 } 78 }
82 79
83 80
84 function RegExpConstructor(pattern, flags) { 81 function RegExpConstructor(pattern, flags) {
85 if (%_IsConstructCall()) { 82 if (%_IsConstructCall()) {
86 DoConstructRegExp(this, pattern, flags, true); 83 DoConstructRegExp(this, pattern, flags, true);
(...skipping 27 matching lines...) Expand all
114 } 111 }
115 112
116 113
117 function DoRegExpExec(regexp, string, index) { 114 function DoRegExpExec(regexp, string, index) {
118 var result = %_RegExpExec(regexp, string, index, lastMatchInfo); 115 var result = %_RegExpExec(regexp, string, index, lastMatchInfo);
119 if (result !== null) lastMatchInfoOverride = null; 116 if (result !== null) lastMatchInfoOverride = null;
120 return result; 117 return result;
121 } 118 }
122 119
123 120
124 function RegExpCache() {
125 this.type = 'none';
126 this.regExp = 0;
127 this.subject = 0;
128 this.replaceString = 0;
129 this.answer = 0;
130 // answerSaved marks whether the contents of answer is valid for a cache
131 // hit in RegExpExec, StringMatch and StringSplit.
132 this.answerSaved = false;
133 this.splitLimit = 0; // Used only when type is "split".
134 }
135
136
137 var regExpCache = new RegExpCache();
138
139
140 function BuildResultFromMatchInfo(lastMatchInfo, s) { 121 function BuildResultFromMatchInfo(lastMatchInfo, s) {
141 var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1; 122 var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1;
142 var result = %_RegExpConstructResult(numResults, lastMatchInfo[CAPTURE0], s); 123 var result = %_RegExpConstructResult(numResults, lastMatchInfo[CAPTURE0], s);
143 if (numResults === 1) { 124 if (numResults === 1) {
144 var matchStart = lastMatchInfo[CAPTURE(0)]; 125 var matchStart = lastMatchInfo[CAPTURE(0)];
145 var matchEnd = lastMatchInfo[CAPTURE(1)]; 126 var matchEnd = lastMatchInfo[CAPTURE(1)];
146 result[0] = SubString(s, matchStart, matchEnd); 127 result[0] = SubString(s, matchStart, matchEnd);
147 } else { 128 } else {
148 for (var i = 0; i < numResults; i++) { 129 for (var i = 0; i < numResults; i++) {
149 var matchStart = lastMatchInfo[CAPTURE(i << 1)]; 130 var matchStart = lastMatchInfo[CAPTURE(i << 1)];
(...skipping 21 matching lines...) Expand all
171 return result; 152 return result;
172 } 153 }
173 154
174 155
175 function RegExpExec(string) { 156 function RegExpExec(string) {
176 if (!IS_REGEXP(this)) { 157 if (!IS_REGEXP(this)) {
177 throw MakeTypeError('incompatible_method_receiver', 158 throw MakeTypeError('incompatible_method_receiver',
178 ['RegExp.prototype.exec', this]); 159 ['RegExp.prototype.exec', this]);
179 } 160 }
180 161
181 var cache = regExpCache;
182 var saveAnswer = false;
183
184 var lastIndex = this.lastIndex;
185
186 // Since cache.subject is always a string, a matching input can not
187 // cause visible side-effects when converted to a string, so we can omit
188 // the conversion required by the specification.
189 // Likewise, the regexp.lastIndex and regexp.global properties are value
190 // properties that are not configurable, so reading them can also not cause
191 // any side effects (converting lastIndex to a number can, though).
192 if (%_ObjectEquals(cache.type, 'exec') &&
193 %_ObjectEquals(0, lastIndex) &&
194 %_IsRegExpEquivalent(cache.regExp, this) &&
195 %_ObjectEquals(cache.subject, string)) {
196 if (cache.answerSaved) {
197 // The regexp.lastIndex value must be 0 for non-global RegExps, and for
198 // global RegExps we only cache negative results, which gives a lastIndex
199 // of zero as well.
200 this.lastIndex = 0;
201 return %_RegExpCloneResult(cache.answer);
202 } else {
203 saveAnswer = true;
204 }
205 }
206
207 if (%_ArgumentsLength() === 0) { 162 if (%_ArgumentsLength() === 0) {
208 var regExpInput = LAST_INPUT(lastMatchInfo); 163 var regExpInput = LAST_INPUT(lastMatchInfo);
209 if (IS_UNDEFINED(regExpInput)) { 164 if (IS_UNDEFINED(regExpInput)) {
210 throw MakeError('no_input_to_regexp', [this]); 165 throw MakeError('no_input_to_regexp', [this]);
211 } 166 }
212 string = regExpInput; 167 string = regExpInput;
213 } 168 }
214 var s; 169 var s;
215 if (IS_STRING(string)) { 170 if (IS_STRING(string)) {
216 s = string; 171 s = string;
217 } else { 172 } else {
218 s = ToString(string); 173 s = ToString(string);
219 } 174 }
220 var global = this.global; 175 var lastIndex = this.lastIndex;
221 176
222 // Conversion is required by the ES5 specification (RegExp.prototype.exec 177 // Conversion is required by the ES5 specification (RegExp.prototype.exec
223 // algorithm, step 5) even if the value is discarded for non-global RegExps. 178 // algorithm, step 5) even if the value is discarded for non-global RegExps.
224 var i = TO_INTEGER(lastIndex); 179 var i = TO_INTEGER(lastIndex);
180
181 var global = this.global;
225 if (global) { 182 if (global) {
226 if (i < 0 || i > s.length) { 183 if (i < 0 || i > s.length) {
227 this.lastIndex = 0; 184 this.lastIndex = 0;
228 return null; 185 return null;
229 } 186 }
230 } else { 187 } else {
231 i = 0; 188 i = 0;
232 } 189 }
233 190
234 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]); 191 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]);
235 // matchIndices is either null or the lastMatchInfo array. 192 // matchIndices is either null or the lastMatchInfo array.
236 var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo); 193 var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo);
237 194
238 if (matchIndices === null) { 195 if (matchIndices === null) {
239 if (global) { 196 if (global) {
240 // Cache negative result only if initial lastIndex was zero.
241 this.lastIndex = 0; 197 this.lastIndex = 0;
242 if (lastIndex !== 0) return matchIndices;
243 } 198 }
244 cache.regExp = this; 199 return null;
245 cache.subject = s; // Always a string.
246 cache.answer = null;
247 cache.answerSaved = true; // Safe since no cloning is needed.
248 cache.type = 'exec';
249 return matchIndices; // No match.
250 } 200 }
251 201
252 // Successful match. 202 // Successful match.
253 lastMatchInfoOverride = null; 203 lastMatchInfoOverride = null;
254 var result = BuildResultFromMatchInfo(matchIndices, s); 204 var result = BuildResultFromMatchInfo(matchIndices, s);
255 205
256 if (global) { 206 if (global) {
257 // Don't cache positive results for global regexps.
258 this.lastIndex = lastMatchInfo[CAPTURE1]; 207 this.lastIndex = lastMatchInfo[CAPTURE1];
259 } else {
260 cache.regExp = this;
261 cache.subject = s;
262 if (saveAnswer) cache.answer = %_RegExpCloneResult(result);
263 cache.answerSaved = saveAnswer;
264 cache.type = 'exec';
265 } 208 }
266 return result; 209 return result;
267
268 } 210 }
269 211
270 212
271 // One-element cache for the simplified test regexp. 213 // One-element cache for the simplified test regexp.
272 var regexp_key; 214 var regexp_key;
273 var regexp_val; 215 var regexp_val;
274 216
275 // Section 15.10.6.3 doesn't actually make sense, but the intention seems to be 217 // Section 15.10.6.3 doesn't actually make sense, but the intention seems to be
276 // that test is defined in terms of String.prototype.exec. However, it probably 218 // that test is defined in terms of String.prototype.exec. However, it probably
277 // means the original value of String.prototype.exec, which is what everybody 219 // means the original value of String.prototype.exec, which is what everybody
278 // else implements. 220 // else implements.
279 function RegExpTest(string) { 221 function RegExpTest(string) {
280 if (!IS_REGEXP(this)) { 222 if (!IS_REGEXP(this)) {
281 throw MakeTypeError('incompatible_method_receiver', 223 throw MakeTypeError('incompatible_method_receiver',
282 ['RegExp.prototype.test', this]); 224 ['RegExp.prototype.test', this]);
283 } 225 }
284 if (%_ArgumentsLength() == 0) { 226 if (%_ArgumentsLength() == 0) {
285 var regExpInput = LAST_INPUT(lastMatchInfo); 227 var regExpInput = LAST_INPUT(lastMatchInfo);
286 if (IS_UNDEFINED(regExpInput)) { 228 if (IS_UNDEFINED(regExpInput)) {
287 throw MakeError('no_input_to_regexp', [this]); 229 throw MakeError('no_input_to_regexp', [this]);
288 } 230 }
289 string = regExpInput; 231 string = regExpInput;
290 } 232 }
291 233
292 var lastIndex = this.lastIndex;
293
294 var cache = regExpCache;
295 if (%_ObjectEquals(cache.type, 'test') &&
296 %_IsRegExpEquivalent(cache.regExp, this) &&
297 %_ObjectEquals(cache.subject, string) &&
298 %_ObjectEquals(0, lastIndex)) {
299 // The regexp.lastIndex value must be 0 for non-global RegExps, and for
300 // global RegExps we only cache negative results, which gives a resulting
301 // lastIndex of zero as well.
302 if (global) this.lastIndex = 0;
303 return cache.answer;
304 }
305
306 var s; 234 var s;
307 if (IS_STRING(string)) { 235 if (IS_STRING(string)) {
308 s = string; 236 s = string;
309 } else { 237 } else {
310 s = ToString(string); 238 s = ToString(string);
311 } 239 }
312 var length = s.length; 240 var length = s.length;
313 241
242 var lastIndex = this.lastIndex;
243
314 // Conversion is required by the ES5 specification (RegExp.prototype.exec 244 // Conversion is required by the ES5 specification (RegExp.prototype.exec
315 // algorithm, step 5) even if the value is discarded for non-global RegExps. 245 // algorithm, step 5) even if the value is discarded for non-global RegExps.
316 var i = TO_INTEGER(lastIndex); 246 var i = TO_INTEGER(lastIndex);
247
248 var global = this.global;
317 if (global) { 249 if (global) {
318 if (i < 0 || i > length) { 250 if (i < 0 || i > length) {
319 this.lastIndex = 0; 251 this.lastIndex = 0;
320 return false; 252 return false;
321 } 253 }
322 } else { 254 } else {
323 i = 0; 255 i = 0;
324 } 256 }
325 257
326 var global = this.global;
327
328 // Remove irrelevant preceeding '.*' in a test regexp. The expression 258 // Remove irrelevant preceeding '.*' in a test regexp. The expression
329 // checks whether this.source starts with '.*' and that the third 259 // checks whether this.source starts with '.*' and that the third
330 // char is not a '?' 260 // char is not a '?'
331 if (%_StringCharCodeAt(this.source, 0) == 46 && // '.' 261 if (%_StringCharCodeAt(this.source, 0) == 46 && // '.'
332 %_StringCharCodeAt(this.source, 1) == 42 && // '*' 262 %_StringCharCodeAt(this.source, 1) == 42 && // '*'
333 %_StringCharCodeAt(this.source, 2) != 63) { // '?' 263 %_StringCharCodeAt(this.source, 2) != 63) { // '?'
334 if (!%_ObjectEquals(regexp_key, this)) { 264 if (!%_ObjectEquals(regexp_key, this)) {
335 regexp_key = this; 265 regexp_key = this;
336 regexp_val = new $RegExp(this.source.substring(2, this.source.length), 266 regexp_val = new $RegExp(this.source.substring(2, this.source.length),
337 (this.global ? 'g' : '') 267 (global ? 'g' : '')
338 + (this.ignoreCase ? 'i' : '') 268 + (this.ignoreCase ? 'i' : '')
339 + (this.multiline ? 'm' : '')); 269 + (this.multiline ? 'm' : ''));
340 } 270 }
341 if (!regexp_val.test(s)) return false; 271 if (!regexp_val.test(s)) return false;
342 } 272 }
343 273
274 var length = s.length;
275
276 if (i < 0 || i > length) {
277 this.lastIndex = 0;
278 return false;
279 }
280
344 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]); 281 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]);
345 // matchIndices is either null or the lastMatchInfo array. 282 // matchIndices is either null or the lastMatchInfo array.
346 var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo); 283 var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo);
347 284
348 var result = (matchIndices !== null); 285 if (matchIndices === null) {
349 if (result) { 286 if (global) this.lastIndex = 0;
350 lastMatchInfoOverride = null; 287 return false;
351 } 288 }
352 if (global) { 289 lastMatchInfoOverride = null;
353 if (result) { 290 if (global) this.lastIndex = lastMatchInfo[CAPTURE1];
354 this.lastIndex = lastMatchInfo[CAPTURE1]; 291 return true;
355 return true;
356 } else {
357 this.lastIndex = 0;
358 if (lastIndex !== 0) return false;
359 }
360 }
361 cache.type = 'test';
362 cache.regExp = this;
363 cache.subject = s;
364 cache.answer = result;
365 return result;
366 } 292 }
367 293
368 294
369 function RegExpToString() { 295 function RegExpToString() {
370 // If this.source is an empty string, output /(?:)/. 296 // If this.source is an empty string, output /(?:)/.
371 // http://bugzilla.mozilla.org/show_bug.cgi?id=225550 297 // http://bugzilla.mozilla.org/show_bug.cgi?id=225550
372 // ecma_2/RegExp/properties-001.js. 298 // ecma_2/RegExp/properties-001.js.
373 var src = this.source ? this.source : '(?:)'; 299 var src = this.source ? this.source : '(?:)';
374 var result = '/' + src + '/'; 300 var result = '/' + src + '/';
375 if (this.global) result += 'g'; 301 if (this.global) result += 'g';
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after
503 %FunctionSetLength($RegExp.prototype.compile, 1); 429 %FunctionSetLength($RegExp.prototype.compile, 1);
504 430
505 // The properties input, $input, and $_ are aliases for each other. When this 431 // The properties input, $input, and $_ are aliases for each other. When this
506 // value is set the value it is set to is coerced to a string. 432 // value is set the value it is set to is coerced to a string.
507 // Getter and setter for the input. 433 // Getter and setter for the input.
508 function RegExpGetInput() { 434 function RegExpGetInput() {
509 var regExpInput = LAST_INPUT(lastMatchInfo); 435 var regExpInput = LAST_INPUT(lastMatchInfo);
510 return IS_UNDEFINED(regExpInput) ? "" : regExpInput; 436 return IS_UNDEFINED(regExpInput) ? "" : regExpInput;
511 } 437 }
512 function RegExpSetInput(string) { 438 function RegExpSetInput(string) {
513 regExpCache.type = 'none';
514 LAST_INPUT(lastMatchInfo) = ToString(string); 439 LAST_INPUT(lastMatchInfo) = ToString(string);
515 }; 440 };
516 441
517 %DefineAccessor($RegExp, 'input', GETTER, RegExpGetInput, DONT_DELETE); 442 %DefineAccessor($RegExp, 'input', GETTER, RegExpGetInput, DONT_DELETE);
518 %DefineAccessor($RegExp, 'input', SETTER, RegExpSetInput, DONT_DELETE); 443 %DefineAccessor($RegExp, 'input', SETTER, RegExpSetInput, DONT_DELETE);
519 %DefineAccessor($RegExp, '$_', GETTER, RegExpGetInput, DONT_ENUM | DONT_DELETE ); 444 %DefineAccessor($RegExp, '$_', GETTER, RegExpGetInput, DONT_ENUM | DONT_DELETE );
520 %DefineAccessor($RegExp, '$_', SETTER, RegExpSetInput, DONT_ENUM | DONT_DELETE ); 445 %DefineAccessor($RegExp, '$_', SETTER, RegExpSetInput, DONT_ENUM | DONT_DELETE );
521 %DefineAccessor($RegExp, '$input', GETTER, RegExpGetInput, DONT_ENUM | DONT_DE LETE); 446 %DefineAccessor($RegExp, '$input', GETTER, RegExpGetInput, DONT_ENUM | DONT_DE LETE);
522 %DefineAccessor($RegExp, '$input', SETTER, RegExpSetInput, DONT_ENUM | DONT_DE LETE); 447 %DefineAccessor($RegExp, '$input', SETTER, RegExpSetInput, DONT_ENUM | DONT_DE LETE);
523 448
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
561 %DefineAccessor($RegExp, "$'", SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE); 486 %DefineAccessor($RegExp, "$'", SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE);
562 487
563 for (var i = 1; i < 10; ++i) { 488 for (var i = 1; i < 10; ++i) {
564 %DefineAccessor($RegExp, '$' + i, GETTER, RegExpMakeCaptureGetter(i), DONT_D ELETE); 489 %DefineAccessor($RegExp, '$' + i, GETTER, RegExpMakeCaptureGetter(i), DONT_D ELETE);
565 %DefineAccessor($RegExp, '$' + i, SETTER, NoOpSetter, DONT_DELETE); 490 %DefineAccessor($RegExp, '$' + i, SETTER, NoOpSetter, DONT_DELETE);
566 } 491 }
567 } 492 }
568 493
569 494
570 SetupRegExp(); 495 SetupRegExp();
OLDNEW
« no previous file with comments | « src/preparser.h ('k') | src/runtime.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698