blob: 38d4496153909f41d98ada01cd18ee5fa367fe63 [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;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000053 for (var i = 0; i < flags.length; i++) {
whesse@chromium.org7a392b32011-01-31 11:30:36 +000054 var c = %_CallFunction(flags, i, StringCharAt);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000055 switch (c) {
56 case 'g':
rossberg@chromium.org28a37082011-08-22 11:03:23 +000057 if (global) {
58 throw MakeSyntaxError("invalid_regexp_flags", [flags]);
59 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000060 global = true;
61 break;
62 case 'i':
rossberg@chromium.org28a37082011-08-22 11:03:23 +000063 if (ignoreCase) {
64 throw MakeSyntaxError("invalid_regexp_flags", [flags]);
65 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000066 ignoreCase = true;
67 break;
68 case 'm':
rossberg@chromium.org28a37082011-08-22 11:03:23 +000069 if (multiline) {
70 throw MakeSyntaxError("invalid_regexp_flags", [flags]);
71 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000072 multiline = true;
73 break;
74 default:
rossberg@chromium.org28a37082011-08-22 11:03:23 +000075 throw MakeSyntaxError("invalid_regexp_flags", [flags]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000076 }
77 }
78
lrn@chromium.org25156de2010-04-06 13:10:27 +000079 %RegExpInitializeObject(object, pattern, global, ignoreCase, multiline);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000080
81 // Call internal function to compile the pattern.
82 %RegExpCompile(object, pattern, flags);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000083}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000084
85
86function RegExpConstructor(pattern, flags) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000087 if (%_IsConstructCall()) {
whesse@chromium.org023421e2010-12-21 12:19:12 +000088 DoConstructRegExp(this, pattern, flags);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000089 } else {
90 // RegExp : Called as function; see ECMA-262, section 15.10.3.1.
91 if (IS_REGEXP(pattern) && IS_UNDEFINED(flags)) {
92 return pattern;
93 }
94 return new $RegExp(pattern, flags);
95 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000096}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000097
98
99// Deprecated RegExp.prototype.compile method. We behave like the constructor
100// were called again. In SpiderMonkey, this method returns the regexp object.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000101// In JSC, it returns undefined. For compatibility with JSC, we match their
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000102// behavior.
103function CompileRegExp(pattern, flags) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000104 // Both JSC and SpiderMonkey treat a missing pattern argument as the
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000105 // empty subject string, and an actual undefined value passed as the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000106 // pattern as the string 'undefined'. Note that JSC is inconsistent
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000107 // here, treating undefined values differently in
108 // RegExp.prototype.compile and in the constructor, where they are
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000109 // the empty string. For compatibility with JSC, we match their
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000110 // behavior.
111 if (IS_UNDEFINED(pattern) && %_ArgumentsLength() != 0) {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000112 DoConstructRegExp(this, 'undefined', flags);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000113 } else {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000114 DoConstructRegExp(this, pattern, flags);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000115 }
116}
117
118
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000119function DoRegExpExec(regexp, string, index) {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000120 var result = %_RegExpExec(regexp, string, index, lastMatchInfo);
121 if (result !== null) lastMatchInfoOverride = null;
122 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000123}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000124
125
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000126function BuildResultFromMatchInfo(lastMatchInfo, s) {
127 var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1;
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000128 var start = lastMatchInfo[CAPTURE0];
129 var end = lastMatchInfo[CAPTURE1];
130 var result = %_RegExpConstructResult(numResults, start, s);
131 if (start + 1 == end) {
132 result[0] = %_StringCharAt(s, start);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000133 } else {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000134 result[0] = %_SubString(s, start, end);
135 }
136 var j = REGEXP_FIRST_CAPTURE + 2;
137 for (var i = 1; i < numResults; i++) {
138 start = lastMatchInfo[j++];
139 end = lastMatchInfo[j++];
140 if (end != -1) {
141 if (start + 1 == end) {
142 result[i] = %_StringCharAt(s, start);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000143 } else {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000144 result[i] = %_SubString(s, start, end);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000145 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000146 } else {
147 // Make sure the element is present. Avoid reading the undefined
148 // property from the global object since this may change.
149 result[i] = void 0;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000150 }
151 }
152 return result;
153}
154
155
156function RegExpExecNoTests(regexp, string, start) {
157 // Must be called with RegExp, string and positive integer as arguments.
whesse@chromium.org023421e2010-12-21 12:19:12 +0000158 var matchInfo = %_RegExpExec(regexp, string, start, lastMatchInfo);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000159 if (matchInfo !== null) {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000160 lastMatchInfoOverride = null;
161 return BuildResultFromMatchInfo(matchInfo, string);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000162 }
whesse@chromium.org023421e2010-12-21 12:19:12 +0000163 return null;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000164}
165
166
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000167function RegExpExec(string) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000168 if (!IS_REGEXP(this)) {
169 throw MakeTypeError('incompatible_method_receiver',
170 ['RegExp.prototype.exec', this]);
171 }
172
vegorov@chromium.org42841962010-10-18 11:18:59 +0000173 if (%_ArgumentsLength() === 0) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000174 var regExpInput = LAST_INPUT(lastMatchInfo);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000175 if (IS_UNDEFINED(regExpInput)) {
176 throw MakeError('no_input_to_regexp', [this]);
177 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000178 string = regExpInput;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000179 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000180 string = TO_STRING_INLINE(string);
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000181 var lastIndex = this.lastIndex;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000182
vegorov@chromium.org42841962010-10-18 11:18:59 +0000183 // Conversion is required by the ES5 specification (RegExp.prototype.exec
184 // algorithm, step 5) even if the value is discarded for non-global RegExps.
185 var i = TO_INTEGER(lastIndex);
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000186
187 var global = this.global;
vegorov@chromium.org42841962010-10-18 11:18:59 +0000188 if (global) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000189 if (i < 0 || i > string.length) {
vegorov@chromium.org42841962010-10-18 11:18:59 +0000190 this.lastIndex = 0;
191 return null;
192 }
193 } else {
194 i = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000195 }
196
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000197 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, string, lastIndex]);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000198 // matchIndices is either null or the lastMatchInfo array.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000199 var matchIndices = %_RegExpExec(this, string, i, lastMatchInfo);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000200
vegorov@chromium.org42841962010-10-18 11:18:59 +0000201 if (matchIndices === null) {
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000202 if (global) this.lastIndex = 0;
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000203 return null;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000204 }
vegorov@chromium.org42841962010-10-18 11:18:59 +0000205
206 // Successful match.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000207 lastMatchInfoOverride = null;
vegorov@chromium.org42841962010-10-18 11:18:59 +0000208 if (global) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000209 this.lastIndex = lastMatchInfo[CAPTURE1];
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000210 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000211 return BuildResultFromMatchInfo(matchIndices, string);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000212}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000213
214
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000215// One-element cache for the simplified test regexp.
216var regexp_key;
217var regexp_val;
218
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000219// Section 15.10.6.3 doesn't actually make sense, but the intention seems to be
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000220// that test is defined in terms of String.prototype.exec. However, it probably
221// means the original value of String.prototype.exec, which is what everybody
222// else implements.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000223function RegExpTest(string) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000224 if (!IS_REGEXP(this)) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000225 throw MakeTypeError('incompatible_method_receiver',
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000226 ['RegExp.prototype.test', this]);
227 }
228 if (%_ArgumentsLength() == 0) {
229 var regExpInput = LAST_INPUT(lastMatchInfo);
230 if (IS_UNDEFINED(regExpInput)) {
231 throw MakeError('no_input_to_regexp', [this]);
232 }
233 string = regExpInput;
234 }
vegorov@chromium.org42841962010-10-18 11:18:59 +0000235
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000236 string = TO_STRING_INLINE(string);
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000237
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000238 var lastIndex = this.lastIndex;
239
vegorov@chromium.org42841962010-10-18 11:18:59 +0000240 // Conversion is required by the ES5 specification (RegExp.prototype.exec
241 // algorithm, step 5) even if the value is discarded for non-global RegExps.
242 var i = TO_INTEGER(lastIndex);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000243
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000244 if (this.global) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000245 if (i < 0 || i > string.length) {
vegorov@chromium.org42841962010-10-18 11:18:59 +0000246 this.lastIndex = 0;
247 return false;
248 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000249 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, string, lastIndex]);
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000250 // matchIndices is either null or the lastMatchInfo array.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000251 var matchIndices = %_RegExpExec(this, string, i, lastMatchInfo);
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000252 if (matchIndices === null) {
253 this.lastIndex = 0;
254 return false;
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000255 }
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000256 lastMatchInfoOverride = null;
257 this.lastIndex = lastMatchInfo[CAPTURE1];
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000258 return true;
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000259 } else {
260 // Non-global regexp.
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000261 // Remove irrelevant preceeding '.*' in a non-global test regexp.
262 // The expression checks whether this.source starts with '.*' and
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000263 // that the third char is not a '?'.
264 if (%_StringCharCodeAt(this.source, 0) == 46 && // '.'
265 %_StringCharCodeAt(this.source, 1) == 42 && // '*'
266 %_StringCharCodeAt(this.source, 2) != 63) { // '?'
267 if (!%_ObjectEquals(regexp_key, this)) {
268 regexp_key = this;
whesse@chromium.org023421e2010-12-21 12:19:12 +0000269 regexp_val = new $RegExp(SubString(this.source, 2, this.source.length),
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000270 (!this.ignoreCase
whesse@chromium.org023421e2010-12-21 12:19:12 +0000271 ? !this.multiline ? "" : "m"
272 : !this.multiline ? "i" : "im"));
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000273 }
whesse@chromium.org023421e2010-12-21 12:19:12 +0000274 if (%_RegExpExec(regexp_val, string, 0, lastMatchInfo) === null) {
275 return false;
276 }
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000277 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000278 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, string, lastIndex]);
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000279 // matchIndices is either null or the lastMatchInfo array.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000280 var matchIndices = %_RegExpExec(this, string, 0, lastMatchInfo);
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000281 if (matchIndices === null) return false;
282 lastMatchInfoOverride = null;
283 return true;
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000284 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000285}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000286
287
288function RegExpToString() {
289 // If this.source is an empty string, output /(?:)/.
290 // http://bugzilla.mozilla.org/show_bug.cgi?id=225550
291 // ecma_2/RegExp/properties-001.js.
292 var src = this.source ? this.source : '(?:)';
293 var result = '/' + src + '/';
vegorov@chromium.org42841962010-10-18 11:18:59 +0000294 if (this.global) result += 'g';
295 if (this.ignoreCase) result += 'i';
296 if (this.multiline) result += 'm';
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000297 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000298}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000299
300
301// Getters for the static properties lastMatch, lastParen, leftContext, and
302// rightContext of the RegExp constructor. The properties are computed based
303// on the captures array of the last successful match and the subject string
304// of the last successful match.
305function RegExpGetLastMatch() {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000306 if (lastMatchInfoOverride !== null) {
307 return lastMatchInfoOverride[0];
308 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000309 var regExpSubject = LAST_SUBJECT(lastMatchInfo);
310 return SubString(regExpSubject,
311 lastMatchInfo[CAPTURE0],
312 lastMatchInfo[CAPTURE1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000313}
314
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000315
316function RegExpGetLastParen() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000317 if (lastMatchInfoOverride) {
318 var override = lastMatchInfoOverride;
319 if (override.length <= 3) return '';
320 return override[override.length - 3];
321 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000322 var length = NUMBER_OF_CAPTURES(lastMatchInfo);
323 if (length <= 2) return ''; // There were no captures.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000324 // We match the SpiderMonkey behavior: return the substring defined by the
325 // last pair (after the first pair) of elements of the capture array even if
326 // it is empty.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000327 var regExpSubject = LAST_SUBJECT(lastMatchInfo);
328 var start = lastMatchInfo[CAPTURE(length - 2)];
329 var end = lastMatchInfo[CAPTURE(length - 1)];
330 if (start != -1 && end != -1) {
331 return SubString(regExpSubject, start, end);
332 }
333 return "";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000334}
335
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000336
337function RegExpGetLeftContext() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000338 var start_index;
339 var subject;
340 if (!lastMatchInfoOverride) {
341 start_index = lastMatchInfo[CAPTURE0];
342 subject = LAST_SUBJECT(lastMatchInfo);
343 } else {
344 var override = lastMatchInfoOverride;
345 start_index = override[override.length - 2];
346 subject = override[override.length - 1];
347 }
348 return SubString(subject, 0, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000349}
350
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000351
352function RegExpGetRightContext() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000353 var start_index;
354 var subject;
355 if (!lastMatchInfoOverride) {
356 start_index = lastMatchInfo[CAPTURE1];
357 subject = LAST_SUBJECT(lastMatchInfo);
358 } else {
359 var override = lastMatchInfoOverride;
360 subject = override[override.length - 1];
361 start_index = override[override.length - 2] + subject.length;
362 }
363 return SubString(subject, start_index, subject.length);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000364}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000365
366
367// The properties $1..$9 are the first nine capturing substrings of the last
368// successful match, or ''. The function RegExpMakeCaptureGetter will be
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000369// called with indices from 1 to 9.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000370function RegExpMakeCaptureGetter(n) {
371 return function() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000372 if (lastMatchInfoOverride) {
373 if (n < lastMatchInfoOverride.length - 2) return lastMatchInfoOverride[n];
374 return '';
375 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000376 var index = n * 2;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000377 if (index >= NUMBER_OF_CAPTURES(lastMatchInfo)) return '';
378 var matchStart = lastMatchInfo[CAPTURE(index)];
379 var matchEnd = lastMatchInfo[CAPTURE(index + 1)];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000380 if (matchStart == -1 || matchEnd == -1) return '';
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000381 return SubString(LAST_SUBJECT(lastMatchInfo), matchStart, matchEnd);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000382 };
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000383}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000384
385
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000386// Property of the builtins object for recording the result of the last
387// regexp match. The property lastMatchInfo includes the matchIndices
388// array of the last successful regexp match (an array of start/end index
389// pairs for the match and all the captured substrings), the invariant is
390// that there are at least two capture indeces. The array also contains
391// the subject string for the last successful match.
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000392var lastMatchInfo = new InternalArray(
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000393 2, // REGEXP_NUMBER_OF_CAPTURES
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000394 "", // Last subject.
395 void 0, // Last input - settable with RegExpSetInput.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000396 0, // REGEXP_FIRST_CAPTURE + 0
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000397 0 // REGEXP_FIRST_CAPTURE + 1
398);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000399
lrn@chromium.org25156de2010-04-06 13:10:27 +0000400// Override last match info with an array of actual substrings.
401// Used internally by replace regexp with function.
402// The array has the format of an "apply" argument for a replacement
403// function.
404var lastMatchInfoOverride = null;
405
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000406// -------------------------------------------------------------------
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000407
fschneider@chromium.org1805e212011-09-05 10:49:12 +0000408function SetUpRegExp() {
409 %CheckIsBootstrapping();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000410 %FunctionSetInstanceClassName($RegExp, 'RegExp');
411 %FunctionSetPrototype($RegExp, new $Object());
412 %SetProperty($RegExp.prototype, 'constructor', $RegExp, DONT_ENUM);
413 %SetCode($RegExp, RegExpConstructor);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000414
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000415 InstallFunctions($RegExp.prototype, DONT_ENUM, $Array(
416 "exec", RegExpExec,
417 "test", RegExpTest,
418 "toString", RegExpToString,
419 "compile", CompileRegExp
420 ));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000421
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000422 // The length of compile is 1 in SpiderMonkey.
423 %FunctionSetLength($RegExp.prototype.compile, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000424
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000425 // The properties input, $input, and $_ are aliases for each other. When this
lrn@chromium.org25156de2010-04-06 13:10:27 +0000426 // value is set the value it is set to is coerced to a string.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000427 // Getter and setter for the input.
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000428 function RegExpGetInput() {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000429 var regExpInput = LAST_INPUT(lastMatchInfo);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000430 return IS_UNDEFINED(regExpInput) ? "" : regExpInput;
431 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000432 function RegExpSetInput(string) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000433 LAST_INPUT(lastMatchInfo) = ToString(string);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000434 };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000435
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000436 %DefineAccessor($RegExp, 'input', GETTER, RegExpGetInput, DONT_DELETE);
437 %DefineAccessor($RegExp, 'input', SETTER, RegExpSetInput, DONT_DELETE);
438 %DefineAccessor($RegExp, '$_', GETTER, RegExpGetInput, DONT_ENUM | DONT_DELETE);
439 %DefineAccessor($RegExp, '$_', SETTER, RegExpSetInput, DONT_ENUM | DONT_DELETE);
440 %DefineAccessor($RegExp, '$input', GETTER, RegExpGetInput, DONT_ENUM | DONT_DELETE);
441 %DefineAccessor($RegExp, '$input', SETTER, RegExpSetInput, DONT_ENUM | DONT_DELETE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000442
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000443 // The properties multiline and $* are aliases for each other. When this
444 // value is set in SpiderMonkey, the value it is set to is coerced to a
445 // boolean. We mimic that behavior with a slight difference: in SpiderMonkey
446 // the value of the expression 'RegExp.multiline = null' (for instance) is the
447 // boolean false (ie, the value after coercion), while in V8 it is the value
448 // null (ie, the value before coercion).
449
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000450 // Getter and setter for multiline.
451 var multiline = false;
452 function RegExpGetMultiline() { return multiline; };
453 function RegExpSetMultiline(flag) { multiline = flag ? true : false; };
454
455 %DefineAccessor($RegExp, 'multiline', GETTER, RegExpGetMultiline, DONT_DELETE);
456 %DefineAccessor($RegExp, 'multiline', SETTER, RegExpSetMultiline, DONT_DELETE);
457 %DefineAccessor($RegExp, '$*', GETTER, RegExpGetMultiline, DONT_ENUM | DONT_DELETE);
458 %DefineAccessor($RegExp, '$*', SETTER, RegExpSetMultiline, DONT_ENUM | DONT_DELETE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000459
460
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000461 function NoOpSetter(ignored) {}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000462
463
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000464 // Static properties set by a successful match.
465 %DefineAccessor($RegExp, 'lastMatch', GETTER, RegExpGetLastMatch, DONT_DELETE);
466 %DefineAccessor($RegExp, 'lastMatch', SETTER, NoOpSetter, DONT_DELETE);
467 %DefineAccessor($RegExp, '$&', GETTER, RegExpGetLastMatch, DONT_ENUM | DONT_DELETE);
468 %DefineAccessor($RegExp, '$&', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE);
469 %DefineAccessor($RegExp, 'lastParen', GETTER, RegExpGetLastParen, DONT_DELETE);
470 %DefineAccessor($RegExp, 'lastParen', SETTER, NoOpSetter, DONT_DELETE);
471 %DefineAccessor($RegExp, '$+', GETTER, RegExpGetLastParen, DONT_ENUM | DONT_DELETE);
472 %DefineAccessor($RegExp, '$+', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE);
473 %DefineAccessor($RegExp, 'leftContext', GETTER, RegExpGetLeftContext, DONT_DELETE);
474 %DefineAccessor($RegExp, 'leftContext', SETTER, NoOpSetter, DONT_DELETE);
475 %DefineAccessor($RegExp, '$`', GETTER, RegExpGetLeftContext, DONT_ENUM | DONT_DELETE);
476 %DefineAccessor($RegExp, '$`', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE);
477 %DefineAccessor($RegExp, 'rightContext', GETTER, RegExpGetRightContext, DONT_DELETE);
478 %DefineAccessor($RegExp, 'rightContext', SETTER, NoOpSetter, DONT_DELETE);
479 %DefineAccessor($RegExp, "$'", GETTER, RegExpGetRightContext, DONT_ENUM | DONT_DELETE);
480 %DefineAccessor($RegExp, "$'", SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000481
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000482 for (var i = 1; i < 10; ++i) {
483 %DefineAccessor($RegExp, '$' + i, GETTER, RegExpMakeCaptureGetter(i), DONT_DELETE);
484 %DefineAccessor($RegExp, '$' + i, SETTER, NoOpSetter, DONT_DELETE);
485 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000486}
487
fschneider@chromium.org1805e212011-09-05 10:49:12 +0000488SetUpRegExp();