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

Side by Side Diff: src/regexp-delay.js

Issue 995005: Fix a bug in the regexp caching. Also add a few more places to... (Closed) Base URL: http://v8.googlecode.com/svn/branches/bleeding_edge/
Patch Set: Created 10 years, 9 months 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | src/string.js » ('j') | test/mjsunit/regexp-cache-replace.js » ('J')
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 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
88 DONT_DELETE | READ_ONLY | DONT_ENUM); 88 DONT_DELETE | READ_ONLY | DONT_ENUM);
89 89
90 // ECMA-262, section 15.10.7.5. 90 // ECMA-262, section 15.10.7.5.
91 %SetProperty(object, 'lastIndex', 0, DONT_DELETE | DONT_ENUM); 91 %SetProperty(object, 'lastIndex', 0, DONT_DELETE | DONT_ENUM);
92 } else { // RegExp is being recompiled via RegExp.prototype.compile. 92 } else { // RegExp is being recompiled via RegExp.prototype.compile.
93 %IgnoreAttributesAndSetProperty(object, 'source', pattern); 93 %IgnoreAttributesAndSetProperty(object, 'source', pattern);
94 %IgnoreAttributesAndSetProperty(object, 'global', global); 94 %IgnoreAttributesAndSetProperty(object, 'global', global);
95 %IgnoreAttributesAndSetProperty(object, 'ignoreCase', ignoreCase); 95 %IgnoreAttributesAndSetProperty(object, 'ignoreCase', ignoreCase);
96 %IgnoreAttributesAndSetProperty(object, 'multiline', multiline); 96 %IgnoreAttributesAndSetProperty(object, 'multiline', multiline);
97 %IgnoreAttributesAndSetProperty(object, 'lastIndex', 0); 97 %IgnoreAttributesAndSetProperty(object, 'lastIndex', 0);
98 // Clear the regexp result cache. 98 regExpCache.type = 'none';
99 cachedRegexp = 0;
100 cachedSubject = 0;
101 cachedLastIndex = 0;
102 cachedAnswer = 0;
103 // These are from string.js.
104 cachedReplaceSubject = 0;
105 cachedReplaceRegexp = 0;
106 cachedReplaceReplacement = 0;
107 cachedReplaceAnswer = 0;
108 } 99 }
109 100
110 // Call internal function to compile the pattern. 101 // Call internal function to compile the pattern.
111 %RegExpCompile(object, pattern, flags); 102 %RegExpCompile(object, pattern, flags);
112 } 103 }
113 104
114 105
115 function RegExpConstructor(pattern, flags) { 106 function RegExpConstructor(pattern, flags) {
116 if (%_IsConstructCall()) { 107 if (%_IsConstructCall()) {
117 DoConstructRegExp(this, pattern, flags, true); 108 DoConstructRegExp(this, pattern, flags, true);
(...skipping 25 matching lines...) Expand all
143 DoConstructRegExp(this, pattern, flags, false); 134 DoConstructRegExp(this, pattern, flags, false);
144 } 135 }
145 } 136 }
146 137
147 138
148 function DoRegExpExec(regexp, string, index) { 139 function DoRegExpExec(regexp, string, index) {
149 return %_RegExpExec(regexp, string, index, lastMatchInfo); 140 return %_RegExpExec(regexp, string, index, lastMatchInfo);
150 } 141 }
151 142
152 143
153 var cachedRegexp; 144 function RegExpCache() {
154 var cachedSubject; 145 this.type = 'none';
155 var cachedLastIndex; 146 this.regExp = 0;
156 var cachedAnswer; 147 this.subject = 0;
148 this.replaceString = 0;
149 this.lastIndex = 0;
150 this.answer = 0;
151 }
152
153
154 var regExpCache = new RegExpCache();
157 155
158 156
159 function CloneRegexpAnswer(array) { 157 function CloneRegexpAnswer(array) {
160 var len = array.length; 158 var len = array.length;
161 var answer = new $Array(len); 159 var answer = new $Array(len);
162 for (var i = 0; i < len; i++) { 160 for (var i = 0; i < len; i++) {
163 answer[i] = array[i]; 161 answer[i] = array[i];
164 } 162 }
165 answer.index = array.index; 163 answer.index = array.index;
166 answer.input = array.input; 164 answer.input = array.input;
167 return answer; 165 return answer;
168 } 166 }
169 167
170 168
171 function RegExpExec(string) { 169 function RegExpExec(string) {
172 if (%_ObjectEquals(cachedLastIndex, this.lastIndex) && 170 if (!IS_REGEXP(this)) {
173 %_ObjectEquals(cachedRegexp, this) && 171 throw MakeTypeError('incompatible_method_receiver',
174 %_ObjectEquals(cachedSubject, string)) { 172 ['RegExp.prototype.exec', this]);
175 var last = cachedAnswer; 173 }
174
175 var cache = regExpCache;
176
177 if (%_ObjectEquals(cache.type, 'exec') &&
178 %_ObjectEquals(cache.lastIndex, this.lastIndex) &&
179 %_ObjectEquals(cache.regExp, this) &&
180 %_ObjectEquals(cache.subject, string)) {
181 var last = cache.answer;
176 if (last == null) { 182 if (last == null) {
177 return last; 183 return last;
178 } else { 184 } else {
179 return CloneRegexpAnswer(last); 185 return CloneRegexpAnswer(last);
180 } 186 }
181 } 187 }
182 188
183 if (!IS_REGEXP(this)) {
184 throw MakeTypeError('incompatible_method_receiver',
185 ['RegExp.prototype.exec', this]);
186 }
187 if (%_ArgumentsLength() == 0) { 189 if (%_ArgumentsLength() == 0) {
188 var regExpInput = LAST_INPUT(lastMatchInfo); 190 var regExpInput = LAST_INPUT(lastMatchInfo);
189 if (IS_UNDEFINED(regExpInput)) { 191 if (IS_UNDEFINED(regExpInput)) {
190 throw MakeError('no_input_to_regexp', [this]); 192 throw MakeError('no_input_to_regexp', [this]);
191 } 193 }
192 string = regExpInput; 194 string = regExpInput;
193 } 195 }
194 var s; 196 var s;
195 if (IS_STRING(string)) { 197 if (IS_STRING(string)) {
196 s = string; 198 s = string;
197 } else { 199 } else {
198 s = ToString(string); 200 s = ToString(string);
199 } 201 }
200 var lastIndex = this.lastIndex; 202 var lastIndex = this.lastIndex;
201 203
202 var i = this.global ? TO_INTEGER(lastIndex) : 0; 204 var i = this.global ? TO_INTEGER(lastIndex) : 0;
203 205
204 if (i < 0 || i > s.length) { 206 if (i < 0 || i > s.length) {
205 this.lastIndex = 0; 207 this.lastIndex = 0;
206 return null; 208 return null;
207 } 209 }
208 210
209 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]); 211 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]);
210 // matchIndices is either null or the lastMatchInfo array. 212 // matchIndices is either null or the lastMatchInfo array.
211 var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo); 213 var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo);
212 214
213 if (matchIndices == null) { 215 if (matchIndices == null) {
214 if (this.global) this.lastIndex = 0; 216 if (this.global) this.lastIndex = 0;
215 cachedLastIndex = lastIndex; 217 cache.lastIndex = lastIndex;
216 cachedRegexp = this; 218 cache.regExp = this;
217 cachedSubject = s; 219 cache.subject = s;
218 cachedAnswer = matchIndices; // Null. 220 cache.answer = matchIndices; // Null.
221 cache.type = 'exec';
219 return matchIndices; // No match. 222 return matchIndices; // No match.
220 } 223 }
221 224
222 var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1; 225 var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1;
223 var result; 226 var result;
224 if (numResults === 1) { 227 if (numResults === 1) {
225 var matchStart = lastMatchInfo[CAPTURE(0)]; 228 var matchStart = lastMatchInfo[CAPTURE(0)];
226 var matchEnd = lastMatchInfo[CAPTURE(1)]; 229 var matchEnd = lastMatchInfo[CAPTURE(1)];
227 result = [SubString(s, matchStart, matchEnd)]; 230 result = [SubString(s, matchStart, matchEnd)];
228 } else { 231 } else {
(...skipping 10 matching lines...) Expand all
239 } 242 }
240 } 243 }
241 } 244 }
242 245
243 result.index = lastMatchInfo[CAPTURE0]; 246 result.index = lastMatchInfo[CAPTURE0];
244 result.input = s; 247 result.input = s;
245 if (this.global) { 248 if (this.global) {
246 this.lastIndex = lastMatchInfo[CAPTURE1]; 249 this.lastIndex = lastMatchInfo[CAPTURE1];
247 return result; 250 return result;
248 } else { 251 } else {
249 cachedRegexp = this; 252 cache.regExp = this;
250 cachedSubject = s; 253 cache.subject = s;
251 cachedLastIndex = lastIndex; 254 cache.lastIndex = lastIndex;
252 cachedAnswer = result; 255 cache.answer = result;
256 cache.type = 'exec';
253 return CloneRegexpAnswer(result); 257 return CloneRegexpAnswer(result);
254 } 258 }
255 } 259 }
256 260
257 261
258 // Section 15.10.6.3 doesn't actually make sense, but the intention seems to be 262 // Section 15.10.6.3 doesn't actually make sense, but the intention seems to be
259 // that test is defined in terms of String.prototype.exec. However, it probably 263 // that test is defined in terms of String.prototype.exec. However, it probably
260 // means the original value of String.prototype.exec, which is what everybody 264 // means the original value of String.prototype.exec, which is what everybody
261 // else implements. 265 // else implements.
262 function RegExpTest(string) { 266 function RegExpTest(string) {
263 if (!IS_REGEXP(this)) { 267 if (!IS_REGEXP(this)) {
264 throw MakeTypeError('incompatible_method_receiver', 268 throw MakeTypeError('incompatible_method_receiver',
265 ['RegExp.prototype.test', this]); 269 ['RegExp.prototype.test', this]);
266 } 270 }
267 if (%_ArgumentsLength() == 0) { 271 if (%_ArgumentsLength() == 0) {
268 var regExpInput = LAST_INPUT(lastMatchInfo); 272 var regExpInput = LAST_INPUT(lastMatchInfo);
269 if (IS_UNDEFINED(regExpInput)) { 273 if (IS_UNDEFINED(regExpInput)) {
270 throw MakeError('no_input_to_regexp', [this]); 274 throw MakeError('no_input_to_regexp', [this]);
271 } 275 }
272 string = regExpInput; 276 string = regExpInput;
273 } 277 }
274 var s = ToString(string); 278 var s;
279 if (IS_STRING(string)) {
280 s = string;
281 } else {
282 s = ToString(string);
283 }
284
285 var lastIndex = this.lastIndex;
286
287 var cache = regExpCache;
288
289 if (%_ObjectEquals(cache.type, 'test') &&
290 %_ObjectEquals(cache.regExp, this) &&
291 %_ObjectEquals(cache.subject, string) &&
292 %_ObjectEquals(cache.lastIndex, lastIndex)) {
293 return cache.answer;
294 }
295
275 var length = s.length; 296 var length = s.length;
276 var lastIndex = this.lastIndex;
277 var i = this.global ? TO_INTEGER(lastIndex) : 0; 297 var i = this.global ? TO_INTEGER(lastIndex) : 0;
278 298
299 cache.type = 'test';
300 cache.regExp = this;
301 cache.subject = s;
302 cache.lastIndex = i;
303
279 if (i < 0 || i > s.length) { 304 if (i < 0 || i > s.length) {
280 this.lastIndex = 0; 305 this.lastIndex = 0;
306 cache.answer = false;
281 return false; 307 return false;
282 } 308 }
283 309
284 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]); 310 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]);
285 // matchIndices is either null or the lastMatchInfo array. 311 // matchIndices is either null or the lastMatchInfo array.
286 var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo); 312 var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo);
287 313
288 if (matchIndices == null) { 314 if (matchIndices == null) {
289 if (this.global) this.lastIndex = 0; 315 if (this.global) this.lastIndex = 0;
316 cache.answer = false;
290 return false; 317 return false;
291 } 318 }
292 319
293 if (this.global) this.lastIndex = lastMatchInfo[CAPTURE1]; 320 if (this.global) this.lastIndex = lastMatchInfo[CAPTURE1];
321 cache.answer = true;
294 return true; 322 return true;
295 } 323 }
296 324
297 325
298 function RegExpToString() { 326 function RegExpToString() {
299 // If this.source is an empty string, output /(?:)/. 327 // If this.source is an empty string, output /(?:)/.
300 // http://bugzilla.mozilla.org/show_bug.cgi?id=225550 328 // http://bugzilla.mozilla.org/show_bug.cgi?id=225550
301 // ecma_2/RegExp/properties-001.js. 329 // ecma_2/RegExp/properties-001.js.
302 var src = this.source ? this.source : '(?:)'; 330 var src = this.source ? this.source : '(?:)';
303 var result = '/' + src + '/'; 331 var result = '/' + src + '/';
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
402 %FunctionSetLength($RegExp.prototype.compile, 1); 430 %FunctionSetLength($RegExp.prototype.compile, 1);
403 431
404 // The properties input, $input, and $_ are aliases for each other. When this 432 // The properties input, $input, and $_ are aliases for each other. When this
405 // value is set the value it is set to is coerced to a string. 433 // value is set the value it is set to is coerced to a string.
406 // Getter and setter for the input. 434 // Getter and setter for the input.
407 function RegExpGetInput() { 435 function RegExpGetInput() {
408 var regExpInput = LAST_INPUT(lastMatchInfo); 436 var regExpInput = LAST_INPUT(lastMatchInfo);
409 return IS_UNDEFINED(regExpInput) ? "" : regExpInput; 437 return IS_UNDEFINED(regExpInput) ? "" : regExpInput;
410 } 438 }
411 function RegExpSetInput(string) { 439 function RegExpSetInput(string) {
440 regExpCache.type = 'none';
412 LAST_INPUT(lastMatchInfo) = ToString(string); 441 LAST_INPUT(lastMatchInfo) = ToString(string);
413 }; 442 };
414 443
415 %DefineAccessor($RegExp, 'input', GETTER, RegExpGetInput, DONT_DELETE); 444 %DefineAccessor($RegExp, 'input', GETTER, RegExpGetInput, DONT_DELETE);
416 %DefineAccessor($RegExp, 'input', SETTER, RegExpSetInput, DONT_DELETE); 445 %DefineAccessor($RegExp, 'input', SETTER, RegExpSetInput, DONT_DELETE);
417 %DefineAccessor($RegExp, '$_', GETTER, RegExpGetInput, DONT_ENUM | DONT_DELETE ); 446 %DefineAccessor($RegExp, '$_', GETTER, RegExpGetInput, DONT_ENUM | DONT_DELETE );
418 %DefineAccessor($RegExp, '$_', SETTER, RegExpSetInput, DONT_ENUM | DONT_DELETE ); 447 %DefineAccessor($RegExp, '$_', SETTER, RegExpSetInput, DONT_ENUM | DONT_DELETE );
419 %DefineAccessor($RegExp, '$input', GETTER, RegExpGetInput, DONT_ENUM | DONT_DE LETE); 448 %DefineAccessor($RegExp, '$input', GETTER, RegExpGetInput, DONT_ENUM | DONT_DE LETE);
420 %DefineAccessor($RegExp, '$input', SETTER, RegExpSetInput, DONT_ENUM | DONT_DE LETE); 449 %DefineAccessor($RegExp, '$input', SETTER, RegExpSetInput, DONT_ENUM | DONT_DE LETE);
421 450
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
459 %DefineAccessor($RegExp, "$'", SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE); 488 %DefineAccessor($RegExp, "$'", SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE);
460 489
461 for (var i = 1; i < 10; ++i) { 490 for (var i = 1; i < 10; ++i) {
462 %DefineAccessor($RegExp, '$' + i, GETTER, RegExpMakeCaptureGetter(i), DONT_D ELETE); 491 %DefineAccessor($RegExp, '$' + i, GETTER, RegExpMakeCaptureGetter(i), DONT_D ELETE);
463 %DefineAccessor($RegExp, '$' + i, SETTER, NoOpSetter, DONT_DELETE); 492 %DefineAccessor($RegExp, '$' + i, SETTER, NoOpSetter, DONT_DELETE);
464 } 493 }
465 } 494 }
466 495
467 496
468 SetupRegExp(); 497 SetupRegExp();
OLDNEW
« no previous file with comments | « no previous file | src/string.js » ('j') | test/mjsunit/regexp-cache-replace.js » ('J')

Powered by Google App Engine
This is Rietveld 408576698