blob: 2349ca7fbc92a9973484bc9645420e645ad98d31 [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);
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000135 result[0] = %_SubString(s, start, end);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000136 var j = REGEXP_FIRST_CAPTURE + 2;
137 for (var i = 1; i < numResults; i++) {
138 start = lastMatchInfo[j++];
ulan@chromium.org56c14af2012-09-20 12:51:09 +0000139 if (start != -1) {
140 end = lastMatchInfo[j];
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000141 result[i] = %_SubString(s, start, end);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000142 }
ulan@chromium.org56c14af2012-09-20 12:51:09 +0000143 j++;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000144 }
145 return result;
146}
147
148
149function RegExpExecNoTests(regexp, string, start) {
150 // Must be called with RegExp, string and positive integer as arguments.
whesse@chromium.org023421e2010-12-21 12:19:12 +0000151 var matchInfo = %_RegExpExec(regexp, string, start, lastMatchInfo);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000152 if (matchInfo !== null) {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000153 lastMatchInfoOverride = null;
154 return BuildResultFromMatchInfo(matchInfo, string);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000155 }
mstarzinger@chromium.org32280cf2012-12-06 17:32:37 +0000156 regexp.lastIndex = 0;
whesse@chromium.org023421e2010-12-21 12:19:12 +0000157 return null;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000158}
159
160
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000161function RegExpExec(string) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000162 if (!IS_REGEXP(this)) {
163 throw MakeTypeError('incompatible_method_receiver',
164 ['RegExp.prototype.exec', this]);
165 }
166
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000167 string = TO_STRING_INLINE(string);
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000168 var lastIndex = this.lastIndex;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000169
vegorov@chromium.org42841962010-10-18 11:18:59 +0000170 // Conversion is required by the ES5 specification (RegExp.prototype.exec
171 // algorithm, step 5) even if the value is discarded for non-global RegExps.
172 var i = TO_INTEGER(lastIndex);
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000173
174 var global = this.global;
vegorov@chromium.org42841962010-10-18 11:18:59 +0000175 if (global) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000176 if (i < 0 || i > string.length) {
vegorov@chromium.org42841962010-10-18 11:18:59 +0000177 this.lastIndex = 0;
178 return null;
179 }
180 } else {
181 i = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000182 }
183
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000184 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, string, lastIndex]);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000185 // matchIndices is either null or the lastMatchInfo array.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000186 var matchIndices = %_RegExpExec(this, string, i, lastMatchInfo);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000187
vegorov@chromium.org42841962010-10-18 11:18:59 +0000188 if (matchIndices === null) {
mstarzinger@chromium.org32280cf2012-12-06 17:32:37 +0000189 this.lastIndex = 0;
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000190 return null;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000191 }
vegorov@chromium.org42841962010-10-18 11:18:59 +0000192
193 // Successful match.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000194 lastMatchInfoOverride = null;
vegorov@chromium.org42841962010-10-18 11:18:59 +0000195 if (global) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000196 this.lastIndex = lastMatchInfo[CAPTURE1];
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000197 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000198 return BuildResultFromMatchInfo(matchIndices, string);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000199}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000200
201
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000202// One-element cache for the simplified test regexp.
203var regexp_key;
204var regexp_val;
205
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000206// Section 15.10.6.3 doesn't actually make sense, but the intention seems to be
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000207// that test is defined in terms of String.prototype.exec. However, it probably
208// means the original value of String.prototype.exec, which is what everybody
209// else implements.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000210function RegExpTest(string) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000211 if (!IS_REGEXP(this)) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000212 throw MakeTypeError('incompatible_method_receiver',
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000213 ['RegExp.prototype.test', this]);
214 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000215 string = TO_STRING_INLINE(string);
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000216
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000217 var lastIndex = this.lastIndex;
218
vegorov@chromium.org42841962010-10-18 11:18:59 +0000219 // Conversion is required by the ES5 specification (RegExp.prototype.exec
220 // algorithm, step 5) even if the value is discarded for non-global RegExps.
221 var i = TO_INTEGER(lastIndex);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000222
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000223 if (this.global) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000224 if (i < 0 || i > string.length) {
vegorov@chromium.org42841962010-10-18 11:18:59 +0000225 this.lastIndex = 0;
226 return false;
227 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000228 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, string, lastIndex]);
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000229 // matchIndices is either null or the lastMatchInfo array.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000230 var matchIndices = %_RegExpExec(this, string, i, lastMatchInfo);
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000231 if (matchIndices === null) {
232 this.lastIndex = 0;
233 return false;
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000234 }
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000235 lastMatchInfoOverride = null;
236 this.lastIndex = lastMatchInfo[CAPTURE1];
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000237 return true;
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000238 } else {
239 // Non-global regexp.
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000240 // Remove irrelevant preceeding '.*' in a non-global test regexp.
241 // The expression checks whether this.source starts with '.*' and
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000242 // that the third char is not a '?'.
rossberg@chromium.org2c067b12012-03-19 11:01:52 +0000243 var regexp = this;
244 if (%_StringCharCodeAt(regexp.source, 0) == 46 && // '.'
245 %_StringCharCodeAt(regexp.source, 1) == 42 && // '*'
246 %_StringCharCodeAt(regexp.source, 2) != 63) { // '?'
247 regexp = TrimRegExp(regexp);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000248 }
rossberg@chromium.org2c067b12012-03-19 11:01:52 +0000249 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [regexp, string, lastIndex]);
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000250 // matchIndices is either null or the lastMatchInfo array.
rossberg@chromium.org2c067b12012-03-19 11:01:52 +0000251 var matchIndices = %_RegExpExec(regexp, string, 0, lastMatchInfo);
mstarzinger@chromium.org32280cf2012-12-06 17:32:37 +0000252 if (matchIndices === null) {
253 this.lastIndex = 0;
254 return false;
255 }
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000256 lastMatchInfoOverride = null;
257 return true;
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000258 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000259}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000260
rossberg@chromium.org2c067b12012-03-19 11:01:52 +0000261function TrimRegExp(regexp) {
262 if (!%_ObjectEquals(regexp_key, regexp)) {
263 regexp_key = regexp;
264 regexp_val =
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000265 new $RegExp(%_SubString(regexp.source, 2, regexp.source.length),
rossberg@chromium.org2c067b12012-03-19 11:01:52 +0000266 (regexp.ignoreCase ? regexp.multiline ? "im" : "i"
267 : regexp.multiline ? "m" : ""));
268 }
269 return regexp_val;
270}
271
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000272
273function RegExpToString() {
jkummerow@chromium.org777db6f2012-05-24 09:33:09 +0000274 if (!IS_REGEXP(this)) {
275 throw MakeTypeError('incompatible_method_receiver',
276 ['RegExp.prototype.toString', this]);
277 }
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +0000278 var result = '/' + this.source + '/';
vegorov@chromium.org42841962010-10-18 11:18:59 +0000279 if (this.global) result += 'g';
280 if (this.ignoreCase) result += 'i';
281 if (this.multiline) result += 'm';
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000282 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000283}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000284
285
286// Getters for the static properties lastMatch, lastParen, leftContext, and
287// rightContext of the RegExp constructor. The properties are computed based
288// on the captures array of the last successful match and the subject string
289// of the last successful match.
290function RegExpGetLastMatch() {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000291 if (lastMatchInfoOverride !== null) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +0000292 return OVERRIDE_MATCH(lastMatchInfoOverride);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000293 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000294 var regExpSubject = LAST_SUBJECT(lastMatchInfo);
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000295 return %_SubString(regExpSubject,
296 lastMatchInfo[CAPTURE0],
297 lastMatchInfo[CAPTURE1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000298}
299
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000300
301function RegExpGetLastParen() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000302 if (lastMatchInfoOverride) {
303 var override = lastMatchInfoOverride;
304 if (override.length <= 3) return '';
305 return override[override.length - 3];
306 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000307 var length = NUMBER_OF_CAPTURES(lastMatchInfo);
308 if (length <= 2) return ''; // There were no captures.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000309 // We match the SpiderMonkey behavior: return the substring defined by the
310 // last pair (after the first pair) of elements of the capture array even if
311 // it is empty.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000312 var regExpSubject = LAST_SUBJECT(lastMatchInfo);
313 var start = lastMatchInfo[CAPTURE(length - 2)];
314 var end = lastMatchInfo[CAPTURE(length - 1)];
315 if (start != -1 && end != -1) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000316 return %_SubString(regExpSubject, start, end);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000317 }
318 return "";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000319}
320
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000321
322function RegExpGetLeftContext() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000323 var start_index;
324 var subject;
325 if (!lastMatchInfoOverride) {
326 start_index = lastMatchInfo[CAPTURE0];
327 subject = LAST_SUBJECT(lastMatchInfo);
328 } else {
329 var override = lastMatchInfoOverride;
erik.corry@gmail.comed49e962012-04-17 11:57:53 +0000330 start_index = OVERRIDE_POS(override);
331 subject = OVERRIDE_SUBJECT(override);
lrn@chromium.org25156de2010-04-06 13:10:27 +0000332 }
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000333 return %_SubString(subject, 0, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000334}
335
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000336
337function RegExpGetRightContext() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000338 var start_index;
339 var subject;
340 if (!lastMatchInfoOverride) {
341 start_index = lastMatchInfo[CAPTURE1];
342 subject = LAST_SUBJECT(lastMatchInfo);
343 } else {
344 var override = lastMatchInfoOverride;
erik.corry@gmail.comed49e962012-04-17 11:57:53 +0000345 subject = OVERRIDE_SUBJECT(override);
346 var match = OVERRIDE_MATCH(override);
347 start_index = OVERRIDE_POS(override) + match.length;
lrn@chromium.org25156de2010-04-06 13:10:27 +0000348 }
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000349 return %_SubString(subject, start_index, subject.length);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000350}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000351
352
353// The properties $1..$9 are the first nine capturing substrings of the last
354// successful match, or ''. The function RegExpMakeCaptureGetter will be
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000355// called with indices from 1 to 9.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000356function RegExpMakeCaptureGetter(n) {
357 return function() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000358 if (lastMatchInfoOverride) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +0000359 if (n < lastMatchInfoOverride.length - 2) {
360 return OVERRIDE_CAPTURE(lastMatchInfoOverride, n);
361 }
lrn@chromium.org25156de2010-04-06 13:10:27 +0000362 return '';
363 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000364 var index = n * 2;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000365 if (index >= NUMBER_OF_CAPTURES(lastMatchInfo)) return '';
366 var matchStart = lastMatchInfo[CAPTURE(index)];
367 var matchEnd = lastMatchInfo[CAPTURE(index + 1)];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000368 if (matchStart == -1 || matchEnd == -1) return '';
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000369 return %_SubString(LAST_SUBJECT(lastMatchInfo), matchStart, matchEnd);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000370 };
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000371}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000372
373
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000374// Property of the builtins object for recording the result of the last
375// regexp match. The property lastMatchInfo includes the matchIndices
376// array of the last successful regexp match (an array of start/end index
377// pairs for the match and all the captured substrings), the invariant is
378// that there are at least two capture indeces. The array also contains
379// the subject string for the last successful match.
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +0000380var lastMatchInfo = new InternalPackedArray(
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000381 2, // REGEXP_NUMBER_OF_CAPTURES
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000382 "", // Last subject.
383 void 0, // Last input - settable with RegExpSetInput.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000384 0, // REGEXP_FIRST_CAPTURE + 0
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000385 0 // REGEXP_FIRST_CAPTURE + 1
386);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000387
lrn@chromium.org25156de2010-04-06 13:10:27 +0000388// Override last match info with an array of actual substrings.
389// Used internally by replace regexp with function.
390// The array has the format of an "apply" argument for a replacement
391// function.
392var lastMatchInfoOverride = null;
393
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000394// -------------------------------------------------------------------
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000395
fschneider@chromium.org1805e212011-09-05 10:49:12 +0000396function SetUpRegExp() {
397 %CheckIsBootstrapping();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000398 %FunctionSetInstanceClassName($RegExp, 'RegExp');
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000399 %SetProperty($RegExp.prototype, 'constructor', $RegExp, DONT_ENUM);
400 %SetCode($RegExp, RegExpConstructor);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000401
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000402 InstallFunctions($RegExp.prototype, DONT_ENUM, $Array(
403 "exec", RegExpExec,
404 "test", RegExpTest,
405 "toString", RegExpToString,
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000406 "compile", RegExpCompile
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000407 ));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000408
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000409 // The length of compile is 1 in SpiderMonkey.
410 %FunctionSetLength($RegExp.prototype.compile, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000411
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000412 // The properties input, $input, and $_ are aliases for each other. When this
lrn@chromium.org25156de2010-04-06 13:10:27 +0000413 // value is set the value it is set to is coerced to a string.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000414 // Getter and setter for the input.
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000415 var RegExpGetInput = function() {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000416 var regExpInput = LAST_INPUT(lastMatchInfo);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000417 return IS_UNDEFINED(regExpInput) ? "" : regExpInput;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000418 };
419 var RegExpSetInput = function(string) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000420 LAST_INPUT(lastMatchInfo) = ToString(string);
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000421 };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000422
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +0000423 %OptimizeObjectForAddingMultipleProperties($RegExp, 22);
erik.corry@gmail.combbceb572012-03-09 10:52:05 +0000424 %DefineOrRedefineAccessorProperty($RegExp, 'input', RegExpGetInput,
425 RegExpSetInput, DONT_DELETE);
426 %DefineOrRedefineAccessorProperty($RegExp, '$_', RegExpGetInput,
427 RegExpSetInput, DONT_ENUM | DONT_DELETE);
428 %DefineOrRedefineAccessorProperty($RegExp, '$input', RegExpGetInput,
429 RegExpSetInput, DONT_ENUM | DONT_DELETE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000430
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000431 // The properties multiline and $* are aliases for each other. When this
432 // value is set in SpiderMonkey, the value it is set to is coerced to a
433 // boolean. We mimic that behavior with a slight difference: in SpiderMonkey
434 // the value of the expression 'RegExp.multiline = null' (for instance) is the
ulan@chromium.org2efb9002012-01-19 15:36:35 +0000435 // boolean false (i.e., the value after coercion), while in V8 it is the value
436 // null (i.e., the value before coercion).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000437
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000438 // Getter and setter for multiline.
439 var multiline = false;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000440 var RegExpGetMultiline = function() { return multiline; };
441 var RegExpSetMultiline = function(flag) { multiline = flag ? true : false; };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000442
erik.corry@gmail.combbceb572012-03-09 10:52:05 +0000443 %DefineOrRedefineAccessorProperty($RegExp, 'multiline', RegExpGetMultiline,
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +0000444 RegExpSetMultiline, DONT_DELETE);
erik.corry@gmail.combbceb572012-03-09 10:52:05 +0000445 %DefineOrRedefineAccessorProperty($RegExp, '$*', RegExpGetMultiline,
446 RegExpSetMultiline,
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +0000447 DONT_ENUM | DONT_DELETE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000448
449
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000450 var NoOpSetter = function(ignored) {};
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000451
452
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000453 // Static properties set by a successful match.
erik.corry@gmail.combbceb572012-03-09 10:52:05 +0000454 %DefineOrRedefineAccessorProperty($RegExp, 'lastMatch', RegExpGetLastMatch,
455 NoOpSetter, DONT_DELETE);
456 %DefineOrRedefineAccessorProperty($RegExp, '$&', RegExpGetLastMatch,
457 NoOpSetter, DONT_ENUM | DONT_DELETE);
458 %DefineOrRedefineAccessorProperty($RegExp, 'lastParen', RegExpGetLastParen,
459 NoOpSetter, DONT_DELETE);
460 %DefineOrRedefineAccessorProperty($RegExp, '$+', RegExpGetLastParen,
461 NoOpSetter, DONT_ENUM | DONT_DELETE);
462 %DefineOrRedefineAccessorProperty($RegExp, 'leftContext',
463 RegExpGetLeftContext, NoOpSetter,
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +0000464 DONT_DELETE);
erik.corry@gmail.combbceb572012-03-09 10:52:05 +0000465 %DefineOrRedefineAccessorProperty($RegExp, '$`', RegExpGetLeftContext,
466 NoOpSetter, DONT_ENUM | DONT_DELETE);
467 %DefineOrRedefineAccessorProperty($RegExp, 'rightContext',
468 RegExpGetRightContext, NoOpSetter,
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +0000469 DONT_DELETE);
erik.corry@gmail.combbceb572012-03-09 10:52:05 +0000470 %DefineOrRedefineAccessorProperty($RegExp, "$'", RegExpGetRightContext,
471 NoOpSetter, DONT_ENUM | DONT_DELETE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000472
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000473 for (var i = 1; i < 10; ++i) {
erik.corry@gmail.combbceb572012-03-09 10:52:05 +0000474 %DefineOrRedefineAccessorProperty($RegExp, '$' + i,
475 RegExpMakeCaptureGetter(i), NoOpSetter,
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +0000476 DONT_DELETE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000477 }
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +0000478 %ToFastProperties($RegExp);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000479}
480
fschneider@chromium.org1805e212011-09-05 10:49:12 +0000481SetUpRegExp();