blob: 7b851a35744ac2cc67a52effd91c5f7c72b2bff2 [file] [log] [blame]
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001// Copyright 2006-2009 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28// Expect $Object = global.Object;
29// Expect $Array = global.Array;
30
31const $RegExp = global.RegExp;
32
33// A recursive descent parser for Patterns according to the grammar of
34// ECMA-262 15.10.1, with deviations noted below.
whesse@chromium.org023421e2010-12-21 12:19:12 +000035function DoConstructRegExp(object, pattern, flags) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000036 // RegExp : Called as constructor; see ECMA-262, section 15.10.4.
37 if (IS_REGEXP(pattern)) {
38 if (!IS_UNDEFINED(flags)) {
39 throw MakeTypeError('regexp_flags', []);
40 }
41 flags = (pattern.global ? 'g' : '')
42 + (pattern.ignoreCase ? 'i' : '')
43 + (pattern.multiline ? 'm' : '');
44 pattern = pattern.source;
45 }
46
47 pattern = IS_UNDEFINED(pattern) ? '' : ToString(pattern);
48 flags = IS_UNDEFINED(flags) ? '' : ToString(flags);
49
50 var global = false;
51 var ignoreCase = false;
52 var multiline = false;
53
54 for (var i = 0; i < flags.length; i++) {
whesse@chromium.org7a392b32011-01-31 11:30:36 +000055 var c = %_CallFunction(flags, i, StringCharAt);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000056 switch (c) {
57 case 'g':
iposva@chromium.org245aa852009-02-10 00:49:54 +000058 // Allow duplicate flags to be consistent with JSC and others.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000059 global = true;
60 break;
61 case 'i':
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000062 ignoreCase = true;
63 break;
64 case 'm':
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000065 multiline = true;
66 break;
67 default:
68 // Ignore flags that have no meaning to be consistent with
iposva@chromium.org245aa852009-02-10 00:49:54 +000069 // JSC.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000070 break;
71 }
72 }
73
lrn@chromium.org25156de2010-04-06 13:10:27 +000074 %RegExpInitializeObject(object, pattern, global, ignoreCase, multiline);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000075
76 // Call internal function to compile the pattern.
77 %RegExpCompile(object, pattern, flags);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000078}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000079
80
81function RegExpConstructor(pattern, flags) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000082 if (%_IsConstructCall()) {
whesse@chromium.org023421e2010-12-21 12:19:12 +000083 DoConstructRegExp(this, pattern, flags);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000084 } else {
85 // RegExp : Called as function; see ECMA-262, section 15.10.3.1.
86 if (IS_REGEXP(pattern) && IS_UNDEFINED(flags)) {
87 return pattern;
88 }
89 return new $RegExp(pattern, flags);
90 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000091}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000092
93
94// Deprecated RegExp.prototype.compile method. We behave like the constructor
95// were called again. In SpiderMonkey, this method returns the regexp object.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000096// In JSC, it returns undefined. For compatibility with JSC, we match their
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000097// behavior.
98function CompileRegExp(pattern, flags) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000099 // Both JSC and SpiderMonkey treat a missing pattern argument as the
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000100 // empty subject string, and an actual undefined value passed as the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000101 // pattern as the string 'undefined'. Note that JSC is inconsistent
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000102 // here, treating undefined values differently in
103 // RegExp.prototype.compile and in the constructor, where they are
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000104 // the empty string. For compatibility with JSC, we match their
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000105 // behavior.
106 if (IS_UNDEFINED(pattern) && %_ArgumentsLength() != 0) {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000107 DoConstructRegExp(this, 'undefined', flags);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000108 } else {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000109 DoConstructRegExp(this, pattern, flags);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000110 }
111}
112
113
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000114function DoRegExpExec(regexp, string, index) {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000115 var result = %_RegExpExec(regexp, string, index, lastMatchInfo);
116 if (result !== null) lastMatchInfoOverride = null;
117 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000118}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000119
120
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000121function BuildResultFromMatchInfo(lastMatchInfo, s) {
122 var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1;
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000123 var start = lastMatchInfo[CAPTURE0];
124 var end = lastMatchInfo[CAPTURE1];
125 var result = %_RegExpConstructResult(numResults, start, s);
126 if (start + 1 == end) {
127 result[0] = %_StringCharAt(s, start);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000128 } else {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000129 result[0] = %_SubString(s, start, end);
130 }
131 var j = REGEXP_FIRST_CAPTURE + 2;
132 for (var i = 1; i < numResults; i++) {
133 start = lastMatchInfo[j++];
134 end = lastMatchInfo[j++];
135 if (end != -1) {
136 if (start + 1 == end) {
137 result[i] = %_StringCharAt(s, start);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000138 } else {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000139 result[i] = %_SubString(s, start, end);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000140 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000141 } else {
142 // Make sure the element is present. Avoid reading the undefined
143 // property from the global object since this may change.
144 result[i] = void 0;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000145 }
146 }
147 return result;
148}
149
150
151function RegExpExecNoTests(regexp, string, start) {
152 // Must be called with RegExp, string and positive integer as arguments.
whesse@chromium.org023421e2010-12-21 12:19:12 +0000153 var matchInfo = %_RegExpExec(regexp, string, start, lastMatchInfo);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000154 if (matchInfo !== null) {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000155 lastMatchInfoOverride = null;
156 return BuildResultFromMatchInfo(matchInfo, string);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000157 }
whesse@chromium.org023421e2010-12-21 12:19:12 +0000158 return null;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000159}
160
161
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000162function RegExpExec(string) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000163 if (!IS_REGEXP(this)) {
164 throw MakeTypeError('incompatible_method_receiver',
165 ['RegExp.prototype.exec', this]);
166 }
167
vegorov@chromium.org42841962010-10-18 11:18:59 +0000168 if (%_ArgumentsLength() === 0) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000169 var regExpInput = LAST_INPUT(lastMatchInfo);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000170 if (IS_UNDEFINED(regExpInput)) {
171 throw MakeError('no_input_to_regexp', [this]);
172 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000173 string = regExpInput;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000174 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000175 string = TO_STRING_INLINE(string);
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000176 var lastIndex = this.lastIndex;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000177
vegorov@chromium.org42841962010-10-18 11:18:59 +0000178 // Conversion is required by the ES5 specification (RegExp.prototype.exec
179 // algorithm, step 5) even if the value is discarded for non-global RegExps.
180 var i = TO_INTEGER(lastIndex);
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000181
182 var global = this.global;
vegorov@chromium.org42841962010-10-18 11:18:59 +0000183 if (global) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000184 if (i < 0 || i > string.length) {
vegorov@chromium.org42841962010-10-18 11:18:59 +0000185 this.lastIndex = 0;
186 return null;
187 }
188 } else {
189 i = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000190 }
191
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000192 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, string, lastIndex]);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000193 // matchIndices is either null or the lastMatchInfo array.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000194 var matchIndices = %_RegExpExec(this, string, i, lastMatchInfo);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000195
vegorov@chromium.org42841962010-10-18 11:18:59 +0000196 if (matchIndices === null) {
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000197 if (global) this.lastIndex = 0;
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000198 return null;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000199 }
vegorov@chromium.org42841962010-10-18 11:18:59 +0000200
201 // Successful match.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000202 lastMatchInfoOverride = null;
vegorov@chromium.org42841962010-10-18 11:18:59 +0000203 if (global) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000204 this.lastIndex = lastMatchInfo[CAPTURE1];
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000205 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000206 return BuildResultFromMatchInfo(matchIndices, string);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000207}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000208
209
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000210// One-element cache for the simplified test regexp.
211var regexp_key;
212var regexp_val;
213
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000214// Section 15.10.6.3 doesn't actually make sense, but the intention seems to be
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000215// that test is defined in terms of String.prototype.exec. However, it probably
216// means the original value of String.prototype.exec, which is what everybody
217// else implements.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000218function RegExpTest(string) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000219 if (!IS_REGEXP(this)) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000220 throw MakeTypeError('incompatible_method_receiver',
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000221 ['RegExp.prototype.test', this]);
222 }
223 if (%_ArgumentsLength() == 0) {
224 var regExpInput = LAST_INPUT(lastMatchInfo);
225 if (IS_UNDEFINED(regExpInput)) {
226 throw MakeError('no_input_to_regexp', [this]);
227 }
228 string = regExpInput;
229 }
vegorov@chromium.org42841962010-10-18 11:18:59 +0000230
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000231 string = TO_STRING_INLINE(string);
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000232
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000233 var lastIndex = this.lastIndex;
234
vegorov@chromium.org42841962010-10-18 11:18:59 +0000235 // Conversion is required by the ES5 specification (RegExp.prototype.exec
236 // algorithm, step 5) even if the value is discarded for non-global RegExps.
237 var i = TO_INTEGER(lastIndex);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000238
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000239 if (this.global) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000240 if (i < 0 || i > string.length) {
vegorov@chromium.org42841962010-10-18 11:18:59 +0000241 this.lastIndex = 0;
242 return false;
243 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000244 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, string, lastIndex]);
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000245 // matchIndices is either null or the lastMatchInfo array.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000246 var matchIndices = %_RegExpExec(this, string, i, lastMatchInfo);
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000247 if (matchIndices === null) {
248 this.lastIndex = 0;
249 return false;
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000250 }
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000251 lastMatchInfoOverride = null;
252 this.lastIndex = lastMatchInfo[CAPTURE1];
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000253 return true;
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000254 } else {
255 // Non-global regexp.
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000256 // Remove irrelevant preceeding '.*' in a non-global test regexp.
257 // The expression checks whether this.source starts with '.*' and
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000258 // that the third char is not a '?'.
259 if (%_StringCharCodeAt(this.source, 0) == 46 && // '.'
260 %_StringCharCodeAt(this.source, 1) == 42 && // '*'
261 %_StringCharCodeAt(this.source, 2) != 63) { // '?'
262 if (!%_ObjectEquals(regexp_key, this)) {
263 regexp_key = this;
whesse@chromium.org023421e2010-12-21 12:19:12 +0000264 regexp_val = new $RegExp(SubString(this.source, 2, this.source.length),
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000265 (!this.ignoreCase
whesse@chromium.org023421e2010-12-21 12:19:12 +0000266 ? !this.multiline ? "" : "m"
267 : !this.multiline ? "i" : "im"));
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000268 }
whesse@chromium.org023421e2010-12-21 12:19:12 +0000269 if (%_RegExpExec(regexp_val, string, 0, lastMatchInfo) === null) {
270 return false;
271 }
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000272 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000273 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, string, lastIndex]);
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000274 // matchIndices is either null or the lastMatchInfo array.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000275 var matchIndices = %_RegExpExec(this, string, 0, lastMatchInfo);
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000276 if (matchIndices === null) return false;
277 lastMatchInfoOverride = null;
278 return true;
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000279 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000280}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000281
282
283function RegExpToString() {
284 // If this.source is an empty string, output /(?:)/.
285 // http://bugzilla.mozilla.org/show_bug.cgi?id=225550
286 // ecma_2/RegExp/properties-001.js.
287 var src = this.source ? this.source : '(?:)';
288 var result = '/' + src + '/';
vegorov@chromium.org42841962010-10-18 11:18:59 +0000289 if (this.global) result += 'g';
290 if (this.ignoreCase) result += 'i';
291 if (this.multiline) result += 'm';
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000292 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000293}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000294
295
296// Getters for the static properties lastMatch, lastParen, leftContext, and
297// rightContext of the RegExp constructor. The properties are computed based
298// on the captures array of the last successful match and the subject string
299// of the last successful match.
300function RegExpGetLastMatch() {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000301 if (lastMatchInfoOverride !== null) {
302 return lastMatchInfoOverride[0];
303 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000304 var regExpSubject = LAST_SUBJECT(lastMatchInfo);
305 return SubString(regExpSubject,
306 lastMatchInfo[CAPTURE0],
307 lastMatchInfo[CAPTURE1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000308}
309
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000310
311function RegExpGetLastParen() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000312 if (lastMatchInfoOverride) {
313 var override = lastMatchInfoOverride;
314 if (override.length <= 3) return '';
315 return override[override.length - 3];
316 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000317 var length = NUMBER_OF_CAPTURES(lastMatchInfo);
318 if (length <= 2) return ''; // There were no captures.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000319 // We match the SpiderMonkey behavior: return the substring defined by the
320 // last pair (after the first pair) of elements of the capture array even if
321 // it is empty.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000322 var regExpSubject = LAST_SUBJECT(lastMatchInfo);
323 var start = lastMatchInfo[CAPTURE(length - 2)];
324 var end = lastMatchInfo[CAPTURE(length - 1)];
325 if (start != -1 && end != -1) {
326 return SubString(regExpSubject, start, end);
327 }
328 return "";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000329}
330
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000331
332function RegExpGetLeftContext() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000333 var start_index;
334 var subject;
335 if (!lastMatchInfoOverride) {
336 start_index = lastMatchInfo[CAPTURE0];
337 subject = LAST_SUBJECT(lastMatchInfo);
338 } else {
339 var override = lastMatchInfoOverride;
340 start_index = override[override.length - 2];
341 subject = override[override.length - 1];
342 }
343 return SubString(subject, 0, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000344}
345
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000346
347function RegExpGetRightContext() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000348 var start_index;
349 var subject;
350 if (!lastMatchInfoOverride) {
351 start_index = lastMatchInfo[CAPTURE1];
352 subject = LAST_SUBJECT(lastMatchInfo);
353 } else {
354 var override = lastMatchInfoOverride;
355 subject = override[override.length - 1];
356 start_index = override[override.length - 2] + subject.length;
357 }
358 return SubString(subject, start_index, subject.length);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000359}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000360
361
362// The properties $1..$9 are the first nine capturing substrings of the last
363// successful match, or ''. The function RegExpMakeCaptureGetter will be
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000364// called with indices from 1 to 9.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000365function RegExpMakeCaptureGetter(n) {
366 return function() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000367 if (lastMatchInfoOverride) {
368 if (n < lastMatchInfoOverride.length - 2) return lastMatchInfoOverride[n];
369 return '';
370 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000371 var index = n * 2;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000372 if (index >= NUMBER_OF_CAPTURES(lastMatchInfo)) return '';
373 var matchStart = lastMatchInfo[CAPTURE(index)];
374 var matchEnd = lastMatchInfo[CAPTURE(index + 1)];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000375 if (matchStart == -1 || matchEnd == -1) return '';
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000376 return SubString(LAST_SUBJECT(lastMatchInfo), matchStart, matchEnd);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000377 };
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000378}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000379
380
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000381// Property of the builtins object for recording the result of the last
382// regexp match. The property lastMatchInfo includes the matchIndices
383// array of the last successful regexp match (an array of start/end index
384// pairs for the match and all the captured substrings), the invariant is
385// that there are at least two capture indeces. The array also contains
386// the subject string for the last successful match.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000387var lastMatchInfo = new InternalArray(
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000388 2, // REGEXP_NUMBER_OF_CAPTURES
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000389 "", // Last subject.
390 void 0, // Last input - settable with RegExpSetInput.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000391 0, // REGEXP_FIRST_CAPTURE + 0
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000392 0 // REGEXP_FIRST_CAPTURE + 1
393);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000394
lrn@chromium.org25156de2010-04-06 13:10:27 +0000395// Override last match info with an array of actual substrings.
396// Used internally by replace regexp with function.
397// The array has the format of an "apply" argument for a replacement
398// function.
399var lastMatchInfoOverride = null;
400
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000401// -------------------------------------------------------------------
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000402
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000403function SetupRegExp() {
404 %FunctionSetInstanceClassName($RegExp, 'RegExp');
405 %FunctionSetPrototype($RegExp, new $Object());
406 %SetProperty($RegExp.prototype, 'constructor', $RegExp, DONT_ENUM);
407 %SetCode($RegExp, RegExpConstructor);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000408
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000409 InstallFunctions($RegExp.prototype, DONT_ENUM, $Array(
410 "exec", RegExpExec,
411 "test", RegExpTest,
412 "toString", RegExpToString,
413 "compile", CompileRegExp
414 ));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000415
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000416 // The length of compile is 1 in SpiderMonkey.
417 %FunctionSetLength($RegExp.prototype.compile, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000418
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000419 // The properties input, $input, and $_ are aliases for each other. When this
lrn@chromium.org25156de2010-04-06 13:10:27 +0000420 // value is set the value it is set to is coerced to a string.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000421 // Getter and setter for the input.
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000422 function RegExpGetInput() {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000423 var regExpInput = LAST_INPUT(lastMatchInfo);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000424 return IS_UNDEFINED(regExpInput) ? "" : regExpInput;
425 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000426 function RegExpSetInput(string) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000427 LAST_INPUT(lastMatchInfo) = ToString(string);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000428 };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000429
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000430 %DefineAccessor($RegExp, 'input', GETTER, RegExpGetInput, DONT_DELETE);
431 %DefineAccessor($RegExp, 'input', SETTER, RegExpSetInput, DONT_DELETE);
432 %DefineAccessor($RegExp, '$_', GETTER, RegExpGetInput, DONT_ENUM | DONT_DELETE);
433 %DefineAccessor($RegExp, '$_', SETTER, RegExpSetInput, DONT_ENUM | DONT_DELETE);
434 %DefineAccessor($RegExp, '$input', GETTER, RegExpGetInput, DONT_ENUM | DONT_DELETE);
435 %DefineAccessor($RegExp, '$input', SETTER, RegExpSetInput, DONT_ENUM | DONT_DELETE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000436
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000437 // The properties multiline and $* are aliases for each other. When this
438 // value is set in SpiderMonkey, the value it is set to is coerced to a
439 // boolean. We mimic that behavior with a slight difference: in SpiderMonkey
440 // the value of the expression 'RegExp.multiline = null' (for instance) is the
441 // boolean false (ie, the value after coercion), while in V8 it is the value
442 // null (ie, the value before coercion).
443
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000444 // Getter and setter for multiline.
445 var multiline = false;
446 function RegExpGetMultiline() { return multiline; };
447 function RegExpSetMultiline(flag) { multiline = flag ? true : false; };
448
449 %DefineAccessor($RegExp, 'multiline', GETTER, RegExpGetMultiline, DONT_DELETE);
450 %DefineAccessor($RegExp, 'multiline', SETTER, RegExpSetMultiline, DONT_DELETE);
451 %DefineAccessor($RegExp, '$*', GETTER, RegExpGetMultiline, DONT_ENUM | DONT_DELETE);
452 %DefineAccessor($RegExp, '$*', SETTER, RegExpSetMultiline, DONT_ENUM | DONT_DELETE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000453
454
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000455 function NoOpSetter(ignored) {}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000456
457
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000458 // Static properties set by a successful match.
459 %DefineAccessor($RegExp, 'lastMatch', GETTER, RegExpGetLastMatch, DONT_DELETE);
460 %DefineAccessor($RegExp, 'lastMatch', SETTER, NoOpSetter, DONT_DELETE);
461 %DefineAccessor($RegExp, '$&', GETTER, RegExpGetLastMatch, DONT_ENUM | DONT_DELETE);
462 %DefineAccessor($RegExp, '$&', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE);
463 %DefineAccessor($RegExp, 'lastParen', GETTER, RegExpGetLastParen, DONT_DELETE);
464 %DefineAccessor($RegExp, 'lastParen', SETTER, NoOpSetter, DONT_DELETE);
465 %DefineAccessor($RegExp, '$+', GETTER, RegExpGetLastParen, DONT_ENUM | DONT_DELETE);
466 %DefineAccessor($RegExp, '$+', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE);
467 %DefineAccessor($RegExp, 'leftContext', GETTER, RegExpGetLeftContext, DONT_DELETE);
468 %DefineAccessor($RegExp, 'leftContext', SETTER, NoOpSetter, DONT_DELETE);
469 %DefineAccessor($RegExp, '$`', GETTER, RegExpGetLeftContext, DONT_ENUM | DONT_DELETE);
470 %DefineAccessor($RegExp, '$`', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE);
471 %DefineAccessor($RegExp, 'rightContext', GETTER, RegExpGetRightContext, DONT_DELETE);
472 %DefineAccessor($RegExp, 'rightContext', SETTER, NoOpSetter, DONT_DELETE);
473 %DefineAccessor($RegExp, "$'", GETTER, RegExpGetRightContext, DONT_ENUM | DONT_DELETE);
474 %DefineAccessor($RegExp, "$'", SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000475
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000476 for (var i = 1; i < 10; ++i) {
477 %DefineAccessor($RegExp, '$' + i, GETTER, RegExpMakeCaptureGetter(i), DONT_DELETE);
478 %DefineAccessor($RegExp, '$' + i, SETTER, NoOpSetter, DONT_DELETE);
479 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000480}
481
482
483SetupRegExp();