blob: cb11ad107cf130ec8480ded2cf88c791d40c88cf [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
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000028// This file relies on the fact that the following declaration has been made
29// in runtime.js:
30// var $Object = global.Object;
31// var $Array = global.Array;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000032
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +000033var $RegExp = global.RegExp;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000034
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000035// -------------------------------------------------------------------
36
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000037// A recursive descent parser for Patterns according to the grammar of
38// ECMA-262 15.10.1, with deviations noted below.
whesse@chromium.org023421e2010-12-21 12:19:12 +000039function DoConstructRegExp(object, pattern, flags) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000040 // RegExp : Called as constructor; see ECMA-262, section 15.10.4.
41 if (IS_REGEXP(pattern)) {
42 if (!IS_UNDEFINED(flags)) {
43 throw MakeTypeError('regexp_flags', []);
44 }
45 flags = (pattern.global ? 'g' : '')
46 + (pattern.ignoreCase ? 'i' : '')
47 + (pattern.multiline ? 'm' : '');
48 pattern = pattern.source;
49 }
50
51 pattern = IS_UNDEFINED(pattern) ? '' : ToString(pattern);
52 flags = IS_UNDEFINED(flags) ? '' : ToString(flags);
53
54 var global = false;
55 var ignoreCase = false;
56 var multiline = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000057 for (var i = 0; i < flags.length; i++) {
whesse@chromium.org7a392b32011-01-31 11:30:36 +000058 var c = %_CallFunction(flags, i, StringCharAt);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000059 switch (c) {
60 case 'g':
rossberg@chromium.org28a37082011-08-22 11:03:23 +000061 if (global) {
62 throw MakeSyntaxError("invalid_regexp_flags", [flags]);
63 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000064 global = true;
65 break;
66 case 'i':
rossberg@chromium.org28a37082011-08-22 11:03:23 +000067 if (ignoreCase) {
68 throw MakeSyntaxError("invalid_regexp_flags", [flags]);
69 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000070 ignoreCase = true;
71 break;
72 case 'm':
rossberg@chromium.org28a37082011-08-22 11:03:23 +000073 if (multiline) {
74 throw MakeSyntaxError("invalid_regexp_flags", [flags]);
75 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000076 multiline = true;
77 break;
78 default:
rossberg@chromium.org28a37082011-08-22 11:03:23 +000079 throw MakeSyntaxError("invalid_regexp_flags", [flags]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000080 }
81 }
82
lrn@chromium.org25156de2010-04-06 13:10:27 +000083 %RegExpInitializeObject(object, pattern, global, ignoreCase, multiline);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000084
85 // Call internal function to compile the pattern.
86 %RegExpCompile(object, pattern, flags);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000087}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000088
89
90function RegExpConstructor(pattern, flags) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000091 if (%_IsConstructCall()) {
whesse@chromium.org023421e2010-12-21 12:19:12 +000092 DoConstructRegExp(this, pattern, flags);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000093 } else {
94 // RegExp : Called as function; see ECMA-262, section 15.10.3.1.
95 if (IS_REGEXP(pattern) && IS_UNDEFINED(flags)) {
96 return pattern;
97 }
98 return new $RegExp(pattern, flags);
99 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000100}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000101
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000102// Deprecated RegExp.prototype.compile method. We behave like the constructor
103// were called again. In SpiderMonkey, this method returns the regexp object.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000104// In JSC, it returns undefined. For compatibility with JSC, we match their
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000105// behavior.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000106function RegExpCompile(pattern, flags) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000107 // Both JSC and SpiderMonkey treat a missing pattern argument as the
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000108 // empty subject string, and an actual undefined value passed as the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000109 // pattern as the string 'undefined'. Note that JSC is inconsistent
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000110 // here, treating undefined values differently in
111 // RegExp.prototype.compile and in the constructor, where they are
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000112 // the empty string. For compatibility with JSC, we match their
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000113 // behavior.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000114 if (this == $RegExp.prototype) {
115 // We don't allow recompiling RegExp.prototype.
116 throw MakeTypeError('incompatible_method_receiver',
117 ['RegExp.prototype.compile', this]);
118 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000119 if (IS_UNDEFINED(pattern) && %_ArgumentsLength() != 0) {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000120 DoConstructRegExp(this, 'undefined', flags);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000121 } else {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000122 DoConstructRegExp(this, pattern, flags);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000123 }
124}
125
126
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000127function DoRegExpExec(regexp, string, index) {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000128 var result = %_RegExpExec(regexp, string, index, lastMatchInfo);
129 if (result !== null) lastMatchInfoOverride = null;
130 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000131}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000132
133
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000134function BuildResultFromMatchInfo(lastMatchInfo, s) {
135 var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1;
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000136 var start = lastMatchInfo[CAPTURE0];
137 var end = lastMatchInfo[CAPTURE1];
138 var result = %_RegExpConstructResult(numResults, start, s);
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000139 result[0] = %_SubString(s, start, end);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000140 var j = REGEXP_FIRST_CAPTURE + 2;
141 for (var i = 1; i < numResults; i++) {
142 start = lastMatchInfo[j++];
ulan@chromium.org56c14af2012-09-20 12:51:09 +0000143 if (start != -1) {
144 end = lastMatchInfo[j];
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000145 result[i] = %_SubString(s, start, end);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000146 }
ulan@chromium.org56c14af2012-09-20 12:51:09 +0000147 j++;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000148 }
149 return result;
150}
151
152
153function RegExpExecNoTests(regexp, string, start) {
154 // Must be called with RegExp, string and positive integer as arguments.
whesse@chromium.org023421e2010-12-21 12:19:12 +0000155 var matchInfo = %_RegExpExec(regexp, string, start, lastMatchInfo);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000156 if (matchInfo !== null) {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000157 lastMatchInfoOverride = null;
158 return BuildResultFromMatchInfo(matchInfo, string);
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000159 }
mstarzinger@chromium.org32280cf2012-12-06 17:32:37 +0000160 regexp.lastIndex = 0;
whesse@chromium.org023421e2010-12-21 12:19:12 +0000161 return null;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000162}
163
164
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000165function RegExpExec(string) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000166 if (!IS_REGEXP(this)) {
167 throw MakeTypeError('incompatible_method_receiver',
168 ['RegExp.prototype.exec', this]);
169 }
170
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000171 string = TO_STRING_INLINE(string);
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000172 var lastIndex = this.lastIndex;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000173
vegorov@chromium.org42841962010-10-18 11:18:59 +0000174 // Conversion is required by the ES5 specification (RegExp.prototype.exec
175 // algorithm, step 5) even if the value is discarded for non-global RegExps.
176 var i = TO_INTEGER(lastIndex);
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000177
178 var global = this.global;
vegorov@chromium.org42841962010-10-18 11:18:59 +0000179 if (global) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000180 if (i < 0 || i > string.length) {
vegorov@chromium.org42841962010-10-18 11:18:59 +0000181 this.lastIndex = 0;
182 return null;
183 }
184 } else {
185 i = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000186 }
187
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000188 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, string, lastIndex]);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000189 // matchIndices is either null or the lastMatchInfo array.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000190 var matchIndices = %_RegExpExec(this, string, i, lastMatchInfo);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000191
vegorov@chromium.org42841962010-10-18 11:18:59 +0000192 if (matchIndices === null) {
mstarzinger@chromium.org32280cf2012-12-06 17:32:37 +0000193 this.lastIndex = 0;
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000194 return null;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000195 }
vegorov@chromium.org42841962010-10-18 11:18:59 +0000196
197 // Successful match.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000198 lastMatchInfoOverride = null;
vegorov@chromium.org42841962010-10-18 11:18:59 +0000199 if (global) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000200 this.lastIndex = lastMatchInfo[CAPTURE1];
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000201 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000202 return BuildResultFromMatchInfo(matchIndices, string);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000203}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000204
205
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000206// One-element cache for the simplified test regexp.
207var regexp_key;
208var regexp_val;
209
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000210// Section 15.10.6.3 doesn't actually make sense, but the intention seems to be
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000211// that test is defined in terms of String.prototype.exec. However, it probably
212// means the original value of String.prototype.exec, which is what everybody
213// else implements.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000214function RegExpTest(string) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000215 if (!IS_REGEXP(this)) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000216 throw MakeTypeError('incompatible_method_receiver',
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000217 ['RegExp.prototype.test', this]);
218 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000219 string = TO_STRING_INLINE(string);
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000220
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000221 var lastIndex = this.lastIndex;
222
vegorov@chromium.org42841962010-10-18 11:18:59 +0000223 // Conversion is required by the ES5 specification (RegExp.prototype.exec
224 // algorithm, step 5) even if the value is discarded for non-global RegExps.
225 var i = TO_INTEGER(lastIndex);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000226
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000227 if (this.global) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000228 if (i < 0 || i > string.length) {
vegorov@chromium.org42841962010-10-18 11:18:59 +0000229 this.lastIndex = 0;
230 return false;
231 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000232 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, string, lastIndex]);
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000233 // matchIndices is either null or the lastMatchInfo array.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000234 var matchIndices = %_RegExpExec(this, string, i, lastMatchInfo);
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000235 if (matchIndices === null) {
236 this.lastIndex = 0;
237 return false;
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000238 }
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000239 lastMatchInfoOverride = null;
240 this.lastIndex = lastMatchInfo[CAPTURE1];
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000241 return true;
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000242 } else {
243 // Non-global regexp.
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000244 // Remove irrelevant preceeding '.*' in a non-global test regexp.
245 // The expression checks whether this.source starts with '.*' and
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000246 // that the third char is not a '?'.
rossberg@chromium.org2c067b12012-03-19 11:01:52 +0000247 var regexp = this;
248 if (%_StringCharCodeAt(regexp.source, 0) == 46 && // '.'
249 %_StringCharCodeAt(regexp.source, 1) == 42 && // '*'
250 %_StringCharCodeAt(regexp.source, 2) != 63) { // '?'
251 regexp = TrimRegExp(regexp);
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000252 }
rossberg@chromium.org2c067b12012-03-19 11:01:52 +0000253 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [regexp, string, lastIndex]);
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000254 // matchIndices is either null or the lastMatchInfo array.
rossberg@chromium.org2c067b12012-03-19 11:01:52 +0000255 var matchIndices = %_RegExpExec(regexp, string, 0, lastMatchInfo);
mstarzinger@chromium.org32280cf2012-12-06 17:32:37 +0000256 if (matchIndices === null) {
257 this.lastIndex = 0;
258 return false;
259 }
erik.corry@gmail.com4a6c3272010-11-18 12:04:40 +0000260 lastMatchInfoOverride = null;
261 return true;
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000262 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000263}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000264
rossberg@chromium.org2c067b12012-03-19 11:01:52 +0000265function TrimRegExp(regexp) {
266 if (!%_ObjectEquals(regexp_key, regexp)) {
267 regexp_key = regexp;
268 regexp_val =
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000269 new $RegExp(%_SubString(regexp.source, 2, regexp.source.length),
rossberg@chromium.org2c067b12012-03-19 11:01:52 +0000270 (regexp.ignoreCase ? regexp.multiline ? "im" : "i"
271 : regexp.multiline ? "m" : ""));
272 }
273 return regexp_val;
274}
275
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000276
277function RegExpToString() {
jkummerow@chromium.org777db6f2012-05-24 09:33:09 +0000278 if (!IS_REGEXP(this)) {
279 throw MakeTypeError('incompatible_method_receiver',
280 ['RegExp.prototype.toString', this]);
281 }
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +0000282 var result = '/' + this.source + '/';
vegorov@chromium.org42841962010-10-18 11:18:59 +0000283 if (this.global) result += 'g';
284 if (this.ignoreCase) result += 'i';
285 if (this.multiline) result += 'm';
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000286 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000287}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000288
289
290// Getters for the static properties lastMatch, lastParen, leftContext, and
291// rightContext of the RegExp constructor. The properties are computed based
292// on the captures array of the last successful match and the subject string
293// of the last successful match.
294function RegExpGetLastMatch() {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000295 if (lastMatchInfoOverride !== null) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +0000296 return OVERRIDE_MATCH(lastMatchInfoOverride);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000297 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000298 var regExpSubject = LAST_SUBJECT(lastMatchInfo);
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000299 return %_SubString(regExpSubject,
300 lastMatchInfo[CAPTURE0],
301 lastMatchInfo[CAPTURE1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000302}
303
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000304
305function RegExpGetLastParen() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000306 if (lastMatchInfoOverride) {
307 var override = lastMatchInfoOverride;
308 if (override.length <= 3) return '';
309 return override[override.length - 3];
310 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000311 var length = NUMBER_OF_CAPTURES(lastMatchInfo);
312 if (length <= 2) return ''; // There were no captures.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000313 // We match the SpiderMonkey behavior: return the substring defined by the
314 // last pair (after the first pair) of elements of the capture array even if
315 // it is empty.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000316 var regExpSubject = LAST_SUBJECT(lastMatchInfo);
317 var start = lastMatchInfo[CAPTURE(length - 2)];
318 var end = lastMatchInfo[CAPTURE(length - 1)];
319 if (start != -1 && end != -1) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000320 return %_SubString(regExpSubject, start, end);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000321 }
322 return "";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000323}
324
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000325
326function RegExpGetLeftContext() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000327 var start_index;
328 var subject;
329 if (!lastMatchInfoOverride) {
330 start_index = lastMatchInfo[CAPTURE0];
331 subject = LAST_SUBJECT(lastMatchInfo);
332 } else {
333 var override = lastMatchInfoOverride;
erik.corry@gmail.comed49e962012-04-17 11:57:53 +0000334 start_index = OVERRIDE_POS(override);
335 subject = OVERRIDE_SUBJECT(override);
lrn@chromium.org25156de2010-04-06 13:10:27 +0000336 }
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000337 return %_SubString(subject, 0, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000338}
339
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000340
341function RegExpGetRightContext() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000342 var start_index;
343 var subject;
344 if (!lastMatchInfoOverride) {
345 start_index = lastMatchInfo[CAPTURE1];
346 subject = LAST_SUBJECT(lastMatchInfo);
347 } else {
348 var override = lastMatchInfoOverride;
erik.corry@gmail.comed49e962012-04-17 11:57:53 +0000349 subject = OVERRIDE_SUBJECT(override);
350 var match = OVERRIDE_MATCH(override);
351 start_index = OVERRIDE_POS(override) + match.length;
lrn@chromium.org25156de2010-04-06 13:10:27 +0000352 }
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000353 return %_SubString(subject, start_index, subject.length);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000354}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000355
356
357// The properties $1..$9 are the first nine capturing substrings of the last
358// successful match, or ''. The function RegExpMakeCaptureGetter will be
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000359// called with indices from 1 to 9.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000360function RegExpMakeCaptureGetter(n) {
361 return function() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000362 if (lastMatchInfoOverride) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +0000363 if (n < lastMatchInfoOverride.length - 2) {
364 return OVERRIDE_CAPTURE(lastMatchInfoOverride, n);
365 }
lrn@chromium.org25156de2010-04-06 13:10:27 +0000366 return '';
367 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000368 var index = n * 2;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000369 if (index >= NUMBER_OF_CAPTURES(lastMatchInfo)) return '';
370 var matchStart = lastMatchInfo[CAPTURE(index)];
371 var matchEnd = lastMatchInfo[CAPTURE(index + 1)];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000372 if (matchStart == -1 || matchEnd == -1) return '';
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000373 return %_SubString(LAST_SUBJECT(lastMatchInfo), matchStart, matchEnd);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000374 };
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000375}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000376
377
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000378// Property of the builtins object for recording the result of the last
379// regexp match. The property lastMatchInfo includes the matchIndices
380// array of the last successful regexp match (an array of start/end index
381// pairs for the match and all the captured substrings), the invariant is
382// that there are at least two capture indeces. The array also contains
383// the subject string for the last successful match.
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +0000384var lastMatchInfo = new InternalPackedArray(
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000385 2, // REGEXP_NUMBER_OF_CAPTURES
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000386 "", // Last subject.
387 void 0, // Last input - settable with RegExpSetInput.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000388 0, // REGEXP_FIRST_CAPTURE + 0
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000389 0 // REGEXP_FIRST_CAPTURE + 1
390);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000391
lrn@chromium.org25156de2010-04-06 13:10:27 +0000392// Override last match info with an array of actual substrings.
393// Used internally by replace regexp with function.
394// The array has the format of an "apply" argument for a replacement
395// function.
396var lastMatchInfoOverride = null;
397
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000398// -------------------------------------------------------------------
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000399
fschneider@chromium.org1805e212011-09-05 10:49:12 +0000400function SetUpRegExp() {
401 %CheckIsBootstrapping();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000402 %FunctionSetInstanceClassName($RegExp, 'RegExp');
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000403 %SetProperty($RegExp.prototype, 'constructor', $RegExp, DONT_ENUM);
404 %SetCode($RegExp, RegExpConstructor);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000405
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000406 InstallFunctions($RegExp.prototype, DONT_ENUM, $Array(
407 "exec", RegExpExec,
408 "test", RegExpTest,
409 "toString", RegExpToString,
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000410 "compile", RegExpCompile
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000411 ));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000412
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000413 // The length of compile is 1 in SpiderMonkey.
414 %FunctionSetLength($RegExp.prototype.compile, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000415
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000416 // The properties input, $input, and $_ are aliases for each other. When this
lrn@chromium.org25156de2010-04-06 13:10:27 +0000417 // value is set the value it is set to is coerced to a string.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000418 // Getter and setter for the input.
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000419 var RegExpGetInput = function() {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000420 var regExpInput = LAST_INPUT(lastMatchInfo);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000421 return IS_UNDEFINED(regExpInput) ? "" : regExpInput;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000422 };
423 var RegExpSetInput = function(string) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000424 LAST_INPUT(lastMatchInfo) = ToString(string);
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000425 };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000426
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +0000427 %OptimizeObjectForAddingMultipleProperties($RegExp, 22);
erik.corry@gmail.combbceb572012-03-09 10:52:05 +0000428 %DefineOrRedefineAccessorProperty($RegExp, 'input', RegExpGetInput,
429 RegExpSetInput, DONT_DELETE);
430 %DefineOrRedefineAccessorProperty($RegExp, '$_', RegExpGetInput,
431 RegExpSetInput, DONT_ENUM | DONT_DELETE);
432 %DefineOrRedefineAccessorProperty($RegExp, '$input', RegExpGetInput,
433 RegExpSetInput, DONT_ENUM | DONT_DELETE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000434
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000435 // The properties multiline and $* are aliases for each other. When this
436 // value is set in SpiderMonkey, the value it is set to is coerced to a
437 // boolean. We mimic that behavior with a slight difference: in SpiderMonkey
438 // the value of the expression 'RegExp.multiline = null' (for instance) is the
ulan@chromium.org2efb9002012-01-19 15:36:35 +0000439 // boolean false (i.e., the value after coercion), while in V8 it is the value
440 // null (i.e., the value before coercion).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000441
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000442 // Getter and setter for multiline.
443 var multiline = false;
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000444 var RegExpGetMultiline = function() { return multiline; };
445 var RegExpSetMultiline = function(flag) { multiline = flag ? true : false; };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000446
erik.corry@gmail.combbceb572012-03-09 10:52:05 +0000447 %DefineOrRedefineAccessorProperty($RegExp, 'multiline', RegExpGetMultiline,
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +0000448 RegExpSetMultiline, DONT_DELETE);
erik.corry@gmail.combbceb572012-03-09 10:52:05 +0000449 %DefineOrRedefineAccessorProperty($RegExp, '$*', RegExpGetMultiline,
450 RegExpSetMultiline,
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +0000451 DONT_ENUM | DONT_DELETE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000452
453
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000454 var NoOpSetter = function(ignored) {};
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000455
456
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000457 // Static properties set by a successful match.
erik.corry@gmail.combbceb572012-03-09 10:52:05 +0000458 %DefineOrRedefineAccessorProperty($RegExp, 'lastMatch', RegExpGetLastMatch,
459 NoOpSetter, DONT_DELETE);
460 %DefineOrRedefineAccessorProperty($RegExp, '$&', RegExpGetLastMatch,
461 NoOpSetter, DONT_ENUM | DONT_DELETE);
462 %DefineOrRedefineAccessorProperty($RegExp, 'lastParen', RegExpGetLastParen,
463 NoOpSetter, DONT_DELETE);
464 %DefineOrRedefineAccessorProperty($RegExp, '$+', RegExpGetLastParen,
465 NoOpSetter, DONT_ENUM | DONT_DELETE);
466 %DefineOrRedefineAccessorProperty($RegExp, 'leftContext',
467 RegExpGetLeftContext, NoOpSetter,
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +0000468 DONT_DELETE);
erik.corry@gmail.combbceb572012-03-09 10:52:05 +0000469 %DefineOrRedefineAccessorProperty($RegExp, '$`', RegExpGetLeftContext,
470 NoOpSetter, DONT_ENUM | DONT_DELETE);
471 %DefineOrRedefineAccessorProperty($RegExp, 'rightContext',
472 RegExpGetRightContext, NoOpSetter,
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +0000473 DONT_DELETE);
erik.corry@gmail.combbceb572012-03-09 10:52:05 +0000474 %DefineOrRedefineAccessorProperty($RegExp, "$'", RegExpGetRightContext,
475 NoOpSetter, DONT_ENUM | DONT_DELETE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000476
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000477 for (var i = 1; i < 10; ++i) {
erik.corry@gmail.combbceb572012-03-09 10:52:05 +0000478 %DefineOrRedefineAccessorProperty($RegExp, '$' + i,
479 RegExpMakeCaptureGetter(i), NoOpSetter,
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +0000480 DONT_DELETE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000481 }
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +0000482 %ToFastProperties($RegExp);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000483}
484
fschneider@chromium.org1805e212011-09-05 10:49:12 +0000485SetUpRegExp();