blob: 38090397aa21c037b6f2b047d947c026d7b3cf02 [file] [log] [blame]
erik.corry@gmail.combbceb572012-03-09 10:52:05 +00001// Copyright 2012 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
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +000031var $RegExp = global.RegExp;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000032
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
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000098// Deprecated RegExp.prototype.compile method. We behave like the constructor
99// were called again. In SpiderMonkey, this method returns the regexp object.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000100// In JSC, it returns undefined. For compatibility with JSC, we match their
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000101// behavior.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000102function RegExpCompile(pattern, flags) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000103 // Both JSC and SpiderMonkey treat a missing pattern argument as the
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000104 // empty subject string, and an actual undefined value passed as the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000105 // pattern as the string 'undefined'. Note that JSC is inconsistent
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000106 // here, treating undefined values differently in
107 // RegExp.prototype.compile and in the constructor, where they are
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000108 // the empty string. For compatibility with JSC, we match their
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000109 // behavior.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000110 if (this == $RegExp.prototype) {
111 // We don't allow recompiling RegExp.prototype.
112 throw MakeTypeError('incompatible_method_receiver',
113 ['RegExp.prototype.compile', this]);
114 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000115 if (IS_UNDEFINED(pattern) && %_ArgumentsLength() != 0) {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000116 DoConstructRegExp(this, 'undefined', flags);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000117 } else {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000118 DoConstructRegExp(this, pattern, flags);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000119 }
120}
121
122
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000123function DoRegExpExec(regexp, string, index) {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000124 var result = %_RegExpExec(regexp, string, index, lastMatchInfo);
125 if (result !== null) lastMatchInfoOverride = null;
126 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000127}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000128
129
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000130function BuildResultFromMatchInfo(lastMatchInfo, s) {
131 var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1;
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000132 var start = lastMatchInfo[CAPTURE0];
133 var end = lastMatchInfo[CAPTURE1];
134 var result = %_RegExpConstructResult(numResults, start, s);
135 if (start + 1 == end) {
136 result[0] = %_StringCharAt(s, start);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000137 } else {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000138 result[0] = %_SubString(s, start, end);
139 }
140 var j = REGEXP_FIRST_CAPTURE + 2;
141 for (var i = 1; i < numResults; i++) {
142 start = lastMatchInfo[j++];
143 end = lastMatchInfo[j++];
144 if (end != -1) {
145 if (start + 1 == end) {
146 result[i] = %_StringCharAt(s, start);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000147 } else {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000148 result[i] = %_SubString(s, start, end);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000149 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000150 } else {
151 // Make sure the element is present. Avoid reading the undefined
152 // property from the global object since this may change.
153 result[i] = void 0;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000154 }
155 }
156 return result;
157}
158
159
160function RegExpExecNoTests(regexp, string, start) {
161 // Must be called with RegExp, string and positive integer as arguments.
whesse@chromium.org023421e2010-12-21 12:19:12 +0000162 var matchInfo = %_RegExpExec(regexp, string, start, lastMatchInfo);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000163 if (matchInfo !== null) {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000164 lastMatchInfoOverride = null;
165 return BuildResultFromMatchInfo(matchInfo, string);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000166 }
whesse@chromium.org023421e2010-12-21 12:19:12 +0000167 return null;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000168}
169
170
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000171function RegExpExec(string) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000172 if (!IS_REGEXP(this)) {
173 throw MakeTypeError('incompatible_method_receiver',
174 ['RegExp.prototype.exec', this]);
175 }
176
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000177 string = TO_STRING_INLINE(string);
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000178 var lastIndex = this.lastIndex;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000179
vegorov@chromium.org42841962010-10-18 11:18:59 +0000180 // Conversion is required by the ES5 specification (RegExp.prototype.exec
181 // algorithm, step 5) even if the value is discarded for non-global RegExps.
182 var i = TO_INTEGER(lastIndex);
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000183
184 var global = this.global;
vegorov@chromium.org42841962010-10-18 11:18:59 +0000185 if (global) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000186 if (i < 0 || i > string.length) {
vegorov@chromium.org42841962010-10-18 11:18:59 +0000187 this.lastIndex = 0;
188 return null;
189 }
190 } else {
191 i = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000192 }
193
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000194 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, string, lastIndex]);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000195 // matchIndices is either null or the lastMatchInfo array.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000196 var matchIndices = %_RegExpExec(this, string, i, lastMatchInfo);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000197
vegorov@chromium.org42841962010-10-18 11:18:59 +0000198 if (matchIndices === null) {
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000199 if (global) this.lastIndex = 0;
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000200 return null;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000201 }
vegorov@chromium.org42841962010-10-18 11:18:59 +0000202
203 // Successful match.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000204 lastMatchInfoOverride = null;
vegorov@chromium.org42841962010-10-18 11:18:59 +0000205 if (global) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000206 this.lastIndex = lastMatchInfo[CAPTURE1];
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000207 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000208 return BuildResultFromMatchInfo(matchIndices, string);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000209}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000210
211
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000212// One-element cache for the simplified test regexp.
213var regexp_key;
214var regexp_val;
215
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000216// Section 15.10.6.3 doesn't actually make sense, but the intention seems to be
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000217// that test is defined in terms of String.prototype.exec. However, it probably
218// means the original value of String.prototype.exec, which is what everybody
219// else implements.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000220function RegExpTest(string) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000221 if (!IS_REGEXP(this)) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000222 throw MakeTypeError('incompatible_method_receiver',
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000223 ['RegExp.prototype.test', this]);
224 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000225 string = TO_STRING_INLINE(string);
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000226
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000227 var lastIndex = this.lastIndex;
228
vegorov@chromium.org42841962010-10-18 11:18:59 +0000229 // Conversion is required by the ES5 specification (RegExp.prototype.exec
230 // algorithm, step 5) even if the value is discarded for non-global RegExps.
231 var i = TO_INTEGER(lastIndex);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000232
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000233 if (this.global) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000234 if (i < 0 || i > string.length) {
vegorov@chromium.org42841962010-10-18 11:18:59 +0000235 this.lastIndex = 0;
236 return false;
237 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000238 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, string, lastIndex]);
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000239 // matchIndices is either null or the lastMatchInfo array.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000240 var matchIndices = %_RegExpExec(this, string, i, lastMatchInfo);
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000241 if (matchIndices === null) {
242 this.lastIndex = 0;
243 return false;
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000244 }
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000245 lastMatchInfoOverride = null;
246 this.lastIndex = lastMatchInfo[CAPTURE1];
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000247 return true;
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000248 } else {
249 // Non-global regexp.
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000250 // Remove irrelevant preceeding '.*' in a non-global test regexp.
251 // The expression checks whether this.source starts with '.*' and
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000252 // that the third char is not a '?'.
rossberg@chromium.org2c067b12012-03-19 11:01:52 +0000253 var regexp = this;
254 if (%_StringCharCodeAt(regexp.source, 0) == 46 && // '.'
255 %_StringCharCodeAt(regexp.source, 1) == 42 && // '*'
256 %_StringCharCodeAt(regexp.source, 2) != 63) { // '?'
257 regexp = TrimRegExp(regexp);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000258 }
rossberg@chromium.org2c067b12012-03-19 11:01:52 +0000259 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [regexp, string, lastIndex]);
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000260 // matchIndices is either null or the lastMatchInfo array.
rossberg@chromium.org2c067b12012-03-19 11:01:52 +0000261 var matchIndices = %_RegExpExec(regexp, string, 0, lastMatchInfo);
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000262 if (matchIndices === null) return false;
263 lastMatchInfoOverride = null;
264 return true;
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000265 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000266}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000267
rossberg@chromium.org2c067b12012-03-19 11:01:52 +0000268function TrimRegExp(regexp) {
269 if (!%_ObjectEquals(regexp_key, regexp)) {
270 regexp_key = regexp;
271 regexp_val =
272 new $RegExp(SubString(regexp.source, 2, regexp.source.length),
273 (regexp.ignoreCase ? regexp.multiline ? "im" : "i"
274 : regexp.multiline ? "m" : ""));
275 }
276 return regexp_val;
277}
278
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000279
280function RegExpToString() {
jkummerow@chromium.org777db6f2012-05-24 09:33:09 +0000281 if (!IS_REGEXP(this)) {
282 throw MakeTypeError('incompatible_method_receiver',
283 ['RegExp.prototype.toString', this]);
284 }
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +0000285 var result = '/' + this.source + '/';
vegorov@chromium.org42841962010-10-18 11:18:59 +0000286 if (this.global) result += 'g';
287 if (this.ignoreCase) result += 'i';
288 if (this.multiline) result += 'm';
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000289 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000290}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000291
292
293// Getters for the static properties lastMatch, lastParen, leftContext, and
294// rightContext of the RegExp constructor. The properties are computed based
295// on the captures array of the last successful match and the subject string
296// of the last successful match.
297function RegExpGetLastMatch() {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000298 if (lastMatchInfoOverride !== null) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +0000299 return OVERRIDE_MATCH(lastMatchInfoOverride);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000300 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000301 var regExpSubject = LAST_SUBJECT(lastMatchInfo);
302 return SubString(regExpSubject,
303 lastMatchInfo[CAPTURE0],
304 lastMatchInfo[CAPTURE1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000305}
306
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000307
308function RegExpGetLastParen() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000309 if (lastMatchInfoOverride) {
310 var override = lastMatchInfoOverride;
311 if (override.length <= 3) return '';
312 return override[override.length - 3];
313 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000314 var length = NUMBER_OF_CAPTURES(lastMatchInfo);
315 if (length <= 2) return ''; // There were no captures.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000316 // We match the SpiderMonkey behavior: return the substring defined by the
317 // last pair (after the first pair) of elements of the capture array even if
318 // it is empty.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000319 var regExpSubject = LAST_SUBJECT(lastMatchInfo);
320 var start = lastMatchInfo[CAPTURE(length - 2)];
321 var end = lastMatchInfo[CAPTURE(length - 1)];
322 if (start != -1 && end != -1) {
323 return SubString(regExpSubject, start, end);
324 }
325 return "";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000326}
327
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000328
329function RegExpGetLeftContext() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000330 var start_index;
331 var subject;
332 if (!lastMatchInfoOverride) {
333 start_index = lastMatchInfo[CAPTURE0];
334 subject = LAST_SUBJECT(lastMatchInfo);
335 } else {
336 var override = lastMatchInfoOverride;
erik.corry@gmail.comed49e962012-04-17 11:57:53 +0000337 start_index = OVERRIDE_POS(override);
338 subject = OVERRIDE_SUBJECT(override);
lrn@chromium.org25156de2010-04-06 13:10:27 +0000339 }
340 return SubString(subject, 0, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000341}
342
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000343
344function RegExpGetRightContext() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000345 var start_index;
346 var subject;
347 if (!lastMatchInfoOverride) {
348 start_index = lastMatchInfo[CAPTURE1];
349 subject = LAST_SUBJECT(lastMatchInfo);
350 } else {
351 var override = lastMatchInfoOverride;
erik.corry@gmail.comed49e962012-04-17 11:57:53 +0000352 subject = OVERRIDE_SUBJECT(override);
353 var match = OVERRIDE_MATCH(override);
354 start_index = OVERRIDE_POS(override) + match.length;
lrn@chromium.org25156de2010-04-06 13:10:27 +0000355 }
356 return SubString(subject, start_index, subject.length);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000357}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000358
359
360// The properties $1..$9 are the first nine capturing substrings of the last
361// successful match, or ''. The function RegExpMakeCaptureGetter will be
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000362// called with indices from 1 to 9.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000363function RegExpMakeCaptureGetter(n) {
364 return function() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000365 if (lastMatchInfoOverride) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +0000366 if (n < lastMatchInfoOverride.length - 2) {
367 return OVERRIDE_CAPTURE(lastMatchInfoOverride, n);
368 }
lrn@chromium.org25156de2010-04-06 13:10:27 +0000369 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
fschneider@chromium.org1805e212011-09-05 10:49:12 +0000403function SetUpRegExp() {
404 %CheckIsBootstrapping();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000405 %FunctionSetInstanceClassName($RegExp, 'RegExp');
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000406 %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,
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000413 "compile", RegExpCompile
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000414 ));
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.
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000422 var RegExpGetInput = function() {
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;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000425 };
426 var RegExpSetInput = function(string) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000427 LAST_INPUT(lastMatchInfo) = ToString(string);
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000428 };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000429
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +0000430 %OptimizeObjectForAddingMultipleProperties($RegExp, 22);
erik.corry@gmail.combbceb572012-03-09 10:52:05 +0000431 %DefineOrRedefineAccessorProperty($RegExp, 'input', RegExpGetInput,
432 RegExpSetInput, DONT_DELETE);
433 %DefineOrRedefineAccessorProperty($RegExp, '$_', RegExpGetInput,
434 RegExpSetInput, DONT_ENUM | DONT_DELETE);
435 %DefineOrRedefineAccessorProperty($RegExp, '$input', RegExpGetInput,
436 RegExpSetInput, DONT_ENUM | DONT_DELETE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000437
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000438 // The properties multiline and $* are aliases for each other. When this
439 // value is set in SpiderMonkey, the value it is set to is coerced to a
440 // boolean. We mimic that behavior with a slight difference: in SpiderMonkey
441 // the value of the expression 'RegExp.multiline = null' (for instance) is the
ulan@chromium.org2efb9002012-01-19 15:36:35 +0000442 // boolean false (i.e., the value after coercion), while in V8 it is the value
443 // null (i.e., the value before coercion).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000444
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000445 // Getter and setter for multiline.
446 var multiline = false;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000447 var RegExpGetMultiline = function() { return multiline; };
448 var RegExpSetMultiline = function(flag) { multiline = flag ? true : false; };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000449
erik.corry@gmail.combbceb572012-03-09 10:52:05 +0000450 %DefineOrRedefineAccessorProperty($RegExp, 'multiline', RegExpGetMultiline,
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +0000451 RegExpSetMultiline, DONT_DELETE);
erik.corry@gmail.combbceb572012-03-09 10:52:05 +0000452 %DefineOrRedefineAccessorProperty($RegExp, '$*', RegExpGetMultiline,
453 RegExpSetMultiline,
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +0000454 DONT_ENUM | DONT_DELETE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000455
456
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000457 var NoOpSetter = function(ignored) {};
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000458
459
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000460 // Static properties set by a successful match.
erik.corry@gmail.combbceb572012-03-09 10:52:05 +0000461 %DefineOrRedefineAccessorProperty($RegExp, 'lastMatch', RegExpGetLastMatch,
462 NoOpSetter, DONT_DELETE);
463 %DefineOrRedefineAccessorProperty($RegExp, '$&', RegExpGetLastMatch,
464 NoOpSetter, DONT_ENUM | DONT_DELETE);
465 %DefineOrRedefineAccessorProperty($RegExp, 'lastParen', RegExpGetLastParen,
466 NoOpSetter, DONT_DELETE);
467 %DefineOrRedefineAccessorProperty($RegExp, '$+', RegExpGetLastParen,
468 NoOpSetter, DONT_ENUM | DONT_DELETE);
469 %DefineOrRedefineAccessorProperty($RegExp, 'leftContext',
470 RegExpGetLeftContext, NoOpSetter,
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +0000471 DONT_DELETE);
erik.corry@gmail.combbceb572012-03-09 10:52:05 +0000472 %DefineOrRedefineAccessorProperty($RegExp, '$`', RegExpGetLeftContext,
473 NoOpSetter, DONT_ENUM | DONT_DELETE);
474 %DefineOrRedefineAccessorProperty($RegExp, 'rightContext',
475 RegExpGetRightContext, NoOpSetter,
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +0000476 DONT_DELETE);
erik.corry@gmail.combbceb572012-03-09 10:52:05 +0000477 %DefineOrRedefineAccessorProperty($RegExp, "$'", RegExpGetRightContext,
478 NoOpSetter, DONT_ENUM | DONT_DELETE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000479
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000480 for (var i = 1; i < 10; ++i) {
erik.corry@gmail.combbceb572012-03-09 10:52:05 +0000481 %DefineOrRedefineAccessorProperty($RegExp, '$' + i,
482 RegExpMakeCaptureGetter(i), NoOpSetter,
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +0000483 DONT_DELETE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000484 }
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +0000485 %ToFastProperties($RegExp);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000486}
487
fschneider@chromium.org1805e212011-09-05 10:49:12 +0000488SetUpRegExp();