blob: fa702b2a8276b078a44f52082b874596763050ce [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.
35function DoConstructRegExp(object, pattern, flags, isConstructorCall) {
36 // RegExp : Called as constructor; see ECMA-262, section 15.10.4.
37 if (IS_REGEXP(pattern)) {
38 if (!IS_UNDEFINED(flags)) {
39 throw MakeTypeError('regexp_flags', []);
40 }
41 flags = (pattern.global ? 'g' : '')
42 + (pattern.ignoreCase ? 'i' : '')
43 + (pattern.multiline ? 'm' : '');
44 pattern = pattern.source;
45 }
46
47 pattern = IS_UNDEFINED(pattern) ? '' : ToString(pattern);
48 flags = IS_UNDEFINED(flags) ? '' : ToString(flags);
49
50 var global = false;
51 var ignoreCase = false;
52 var multiline = false;
53
54 for (var i = 0; i < flags.length; i++) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000055 var c = StringCharAt.call(flags, i);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000056 switch (c) {
57 case 'g':
iposva@chromium.org245aa852009-02-10 00:49:54 +000058 // Allow duplicate flags to be consistent with JSC and others.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000059 global = true;
60 break;
61 case 'i':
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000062 ignoreCase = true;
63 break;
64 case 'm':
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000065 multiline = true;
66 break;
67 default:
68 // Ignore flags that have no meaning to be consistent with
iposva@chromium.org245aa852009-02-10 00:49:54 +000069 // JSC.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000070 break;
71 }
72 }
73
lrn@chromium.org25156de2010-04-06 13:10:27 +000074 if (!isConstructorCall) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +000075 regExpCache.type = 'none';
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000076 }
lrn@chromium.org25156de2010-04-06 13:10:27 +000077 %RegExpInitializeObject(object, pattern, global, ignoreCase, multiline);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000078
79 // Call internal function to compile the pattern.
80 %RegExpCompile(object, pattern, flags);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000081}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000082
83
84function RegExpConstructor(pattern, flags) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000085 if (%_IsConstructCall()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000086 DoConstructRegExp(this, pattern, flags, true);
87 } else {
88 // RegExp : Called as function; see ECMA-262, section 15.10.3.1.
89 if (IS_REGEXP(pattern) && IS_UNDEFINED(flags)) {
90 return pattern;
91 }
92 return new $RegExp(pattern, flags);
93 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000094}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000095
96
97// Deprecated RegExp.prototype.compile method. We behave like the constructor
98// were called again. In SpiderMonkey, this method returns the regexp object.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000099// In JSC, it returns undefined. For compatibility with JSC, we match their
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000100// behavior.
101function CompileRegExp(pattern, flags) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000102 // Both JSC and SpiderMonkey treat a missing pattern argument as the
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000103 // empty subject string, and an actual undefined value passed as the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000104 // pattern as the string 'undefined'. Note that JSC is inconsistent
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000105 // here, treating undefined values differently in
106 // RegExp.prototype.compile and in the constructor, where they are
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000107 // the empty string. For compatibility with JSC, we match their
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000108 // behavior.
109 if (IS_UNDEFINED(pattern) && %_ArgumentsLength() != 0) {
110 DoConstructRegExp(this, 'undefined', flags, false);
111 } else {
112 DoConstructRegExp(this, pattern, flags, false);
113 }
114}
115
116
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000117function DoRegExpExec(regexp, string, index) {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000118 var result = %_RegExpExec(regexp, string, index, lastMatchInfo);
119 if (result !== null) lastMatchInfoOverride = null;
120 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000121}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000122
123
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000124function RegExpCache() {
125 this.type = 'none';
126 this.regExp = 0;
127 this.subject = 0;
128 this.replaceString = 0;
lrn@chromium.orgc4e51ac2010-08-09 09:47:21 +0000129 this.lastIndex = 0; // Also used for splitLimit when type is "split"
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000130 this.answer = 0;
ager@chromium.org357bf652010-04-12 11:30:10 +0000131 // answerSaved marks whether the contents of answer is valid for a cache
132 // hit in RegExpExec, StringMatch and StringSplit.
133 this.answerSaved = false;
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000134}
135
136
137var regExpCache = new RegExpCache();
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000138
139
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000140function CloneRegExpResult(array) {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000141 if (array == null) return null;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000142 var length = array.length;
143 var answer = %_RegExpConstructResult(length, array.index, array.input);
144 for (var i = 0; i < length; i++) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000145 answer[i] = array[i];
146 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000147 return answer;
148}
149
150
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000151function BuildResultFromMatchInfo(lastMatchInfo, s) {
152 var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1;
153 var result = %_RegExpConstructResult(numResults, lastMatchInfo[CAPTURE0], s);
154 if (numResults === 1) {
155 var matchStart = lastMatchInfo[CAPTURE(0)];
156 var matchEnd = lastMatchInfo[CAPTURE(1)];
157 result[0] = SubString(s, matchStart, matchEnd);
158 } else {
159 for (var i = 0; i < numResults; i++) {
160 var matchStart = lastMatchInfo[CAPTURE(i << 1)];
161 var matchEnd = lastMatchInfo[CAPTURE((i << 1) + 1)];
162 if (matchStart != -1 && matchEnd != -1) {
163 result[i] = SubString(s, matchStart, matchEnd);
164 } else {
165 // Make sure the element is present. Avoid reading the undefined
166 // property from the global object since this may change.
167 result[i] = void 0;
168 }
169 }
170 }
171 return result;
172}
173
174
175function RegExpExecNoTests(regexp, string, start) {
176 // Must be called with RegExp, string and positive integer as arguments.
177 var matchInfo = DoRegExpExec(regexp, string, start);
178 var result = null;
179 if (matchInfo !== null) {
180 result = BuildResultFromMatchInfo(matchInfo, string);
181 }
182 return result;
183}
184
185
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000186function RegExpExec(string) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000187 if (!IS_REGEXP(this)) {
188 throw MakeTypeError('incompatible_method_receiver',
189 ['RegExp.prototype.exec', this]);
190 }
191
192 var cache = regExpCache;
ager@chromium.org357bf652010-04-12 11:30:10 +0000193 var saveAnswer = false;
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000194
195 if (%_ObjectEquals(cache.type, 'exec') &&
196 %_ObjectEquals(cache.lastIndex, this.lastIndex) &&
lrn@chromium.orgc4e51ac2010-08-09 09:47:21 +0000197 %_IsRegExpEquivalent(cache.regExp, this) &&
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000198 %_ObjectEquals(cache.subject, string)) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000199 if (cache.answerSaved) {
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000200 return CloneRegExpResult(cache.answer);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000201 } else {
ager@chromium.org357bf652010-04-12 11:30:10 +0000202 saveAnswer = true;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000203 }
204 }
205
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000206 if (%_ArgumentsLength() == 0) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000207 var regExpInput = LAST_INPUT(lastMatchInfo);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000208 if (IS_UNDEFINED(regExpInput)) {
209 throw MakeError('no_input_to_regexp', [this]);
210 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000211 string = regExpInput;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000212 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000213 var s;
214 if (IS_STRING(string)) {
215 s = string;
216 } else {
217 s = ToString(string);
218 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000219 var lastIndex = this.lastIndex;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000220
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000221 var i = this.global ? TO_INTEGER(lastIndex) : 0;
222
223 if (i < 0 || i > s.length) {
224 this.lastIndex = 0;
225 return null;
226 }
227
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000228 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000229 // matchIndices is either null or the lastMatchInfo array.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000230 var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000231
232 if (matchIndices == null) {
fschneider@chromium.org40b9da32010-06-28 11:29:21 +0000233 if (this.global) {
234 this.lastIndex = 0;
235 if (lastIndex != 0) return matchIndices;
236 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000237 cache.lastIndex = lastIndex;
238 cache.regExp = this;
239 cache.subject = s;
240 cache.answer = matchIndices; // Null.
ager@chromium.org357bf652010-04-12 11:30:10 +0000241 cache.answerSaved = true; // Safe since no cloning is needed.
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000242 cache.type = 'exec';
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000243 return matchIndices; // No match.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000244 }
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000245 lastMatchInfoOverride = null;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000246 var result = BuildResultFromMatchInfo(matchIndices, s);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000247
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000248 if (this.global) {
249 this.lastIndex = lastMatchInfo[CAPTURE1];
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000250 } else {
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000251 cache.regExp = this;
252 cache.subject = s;
253 cache.lastIndex = lastIndex;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000254 if (saveAnswer) cache.answer = CloneRegExpResult(result);
ager@chromium.org357bf652010-04-12 11:30:10 +0000255 cache.answerSaved = saveAnswer;
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000256 cache.type = 'exec';
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000257 }
ager@chromium.org357bf652010-04-12 11:30:10 +0000258 return result;
259
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000260}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000261
262
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000263// One-element cache for the simplified test regexp.
264var regexp_key;
265var regexp_val;
266
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000267// Section 15.10.6.3 doesn't actually make sense, but the intention seems to be
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000268// that test is defined in terms of String.prototype.exec. However, it probably
269// means the original value of String.prototype.exec, which is what everybody
270// else implements.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000271function RegExpTest(string) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000272 if (!IS_REGEXP(this)) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000273 throw MakeTypeError('incompatible_method_receiver',
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000274 ['RegExp.prototype.test', this]);
275 }
276 if (%_ArgumentsLength() == 0) {
277 var regExpInput = LAST_INPUT(lastMatchInfo);
278 if (IS_UNDEFINED(regExpInput)) {
279 throw MakeError('no_input_to_regexp', [this]);
280 }
281 string = regExpInput;
282 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000283 var s;
284 if (IS_STRING(string)) {
285 s = string;
286 } else {
287 s = ToString(string);
288 }
289
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000290 var lastIndex = this.lastIndex;
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000291 var cache = regExpCache;
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000292 if (%_ObjectEquals(cache.type, 'test') &&
lrn@chromium.orgc4e51ac2010-08-09 09:47:21 +0000293 %_IsRegExpEquivalent(cache.regExp, this) &&
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000294 %_ObjectEquals(cache.subject, string) &&
295 %_ObjectEquals(cache.lastIndex, lastIndex)) {
296 return cache.answer;
297 }
298
lrn@chromium.org32d961d2010-06-30 09:09:34 +0000299 // Remove irrelevant preceeding '.*' in a test regexp. The expression
300 // checks whether this.source starts with '.*' and that the third
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000301 // char is not a '?'
302 if (%_StringCharCodeAt(this.source,0) == 46 && // '.'
lrn@chromium.org32d961d2010-06-30 09:09:34 +0000303 %_StringCharCodeAt(this.source,1) == 42 && // '*'
304 %_StringCharCodeAt(this.source,2) != 63) { // '?'
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000305 if (!%_ObjectEquals(regexp_key, this)) {
lrn@chromium.org32d961d2010-06-30 09:09:34 +0000306 regexp_key = this;
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000307 regexp_val = new $RegExp(this.source.substring(2, this.source.length),
308 (this.global ? 'g' : '')
309 + (this.ignoreCase ? 'i' : '')
310 + (this.multiline ? 'm' : ''));
311 }
312 if (!regexp_val.test(s)) return false;
313 }
lrn@chromium.org32d961d2010-06-30 09:09:34 +0000314
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000315 var length = s.length;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000316 var i = this.global ? TO_INTEGER(lastIndex) : 0;
317
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000318 cache.type = 'test';
319 cache.regExp = this;
320 cache.subject = s;
321 cache.lastIndex = i;
322
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000323 if (i < 0 || i > length) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000324 this.lastIndex = 0;
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000325 cache.answer = false;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000326 return false;
327 }
328
329 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]);
330 // matchIndices is either null or the lastMatchInfo array.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000331 var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000332
333 if (matchIndices == null) {
334 if (this.global) this.lastIndex = 0;
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000335 cache.answer = false;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000336 return false;
337 }
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000338 lastMatchInfoOverride = null;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000339 if (this.global) this.lastIndex = lastMatchInfo[CAPTURE1];
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000340 cache.answer = true;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000341 return true;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000342}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000343
344
345function RegExpToString() {
346 // If this.source is an empty string, output /(?:)/.
347 // http://bugzilla.mozilla.org/show_bug.cgi?id=225550
348 // ecma_2/RegExp/properties-001.js.
349 var src = this.source ? this.source : '(?:)';
350 var result = '/' + src + '/';
351 if (this.global)
352 result += 'g';
353 if (this.ignoreCase)
354 result += 'i';
355 if (this.multiline)
356 result += 'm';
357 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000358}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000359
360
361// Getters for the static properties lastMatch, lastParen, leftContext, and
362// rightContext of the RegExp constructor. The properties are computed based
363// on the captures array of the last successful match and the subject string
364// of the last successful match.
365function RegExpGetLastMatch() {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000366 if (lastMatchInfoOverride !== null) {
367 return lastMatchInfoOverride[0];
368 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000369 var regExpSubject = LAST_SUBJECT(lastMatchInfo);
370 return SubString(regExpSubject,
371 lastMatchInfo[CAPTURE0],
372 lastMatchInfo[CAPTURE1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000373}
374
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000375
376function RegExpGetLastParen() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000377 if (lastMatchInfoOverride) {
378 var override = lastMatchInfoOverride;
379 if (override.length <= 3) return '';
380 return override[override.length - 3];
381 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000382 var length = NUMBER_OF_CAPTURES(lastMatchInfo);
383 if (length <= 2) return ''; // There were no captures.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000384 // We match the SpiderMonkey behavior: return the substring defined by the
385 // last pair (after the first pair) of elements of the capture array even if
386 // it is empty.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000387 var regExpSubject = LAST_SUBJECT(lastMatchInfo);
388 var start = lastMatchInfo[CAPTURE(length - 2)];
389 var end = lastMatchInfo[CAPTURE(length - 1)];
390 if (start != -1 && end != -1) {
391 return SubString(regExpSubject, start, end);
392 }
393 return "";
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000394}
395
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000396
397function RegExpGetLeftContext() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000398 var start_index;
399 var subject;
400 if (!lastMatchInfoOverride) {
401 start_index = lastMatchInfo[CAPTURE0];
402 subject = LAST_SUBJECT(lastMatchInfo);
403 } else {
404 var override = lastMatchInfoOverride;
405 start_index = override[override.length - 2];
406 subject = override[override.length - 1];
407 }
408 return SubString(subject, 0, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000409}
410
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000411
412function RegExpGetRightContext() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000413 var start_index;
414 var subject;
415 if (!lastMatchInfoOverride) {
416 start_index = lastMatchInfo[CAPTURE1];
417 subject = LAST_SUBJECT(lastMatchInfo);
418 } else {
419 var override = lastMatchInfoOverride;
420 subject = override[override.length - 1];
421 start_index = override[override.length - 2] + subject.length;
422 }
423 return SubString(subject, start_index, subject.length);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000424}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000425
426
427// The properties $1..$9 are the first nine capturing substrings of the last
428// successful match, or ''. The function RegExpMakeCaptureGetter will be
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000429// called with indices from 1 to 9.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000430function RegExpMakeCaptureGetter(n) {
431 return function() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000432 if (lastMatchInfoOverride) {
433 if (n < lastMatchInfoOverride.length - 2) return lastMatchInfoOverride[n];
434 return '';
435 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000436 var index = n * 2;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000437 if (index >= NUMBER_OF_CAPTURES(lastMatchInfo)) return '';
438 var matchStart = lastMatchInfo[CAPTURE(index)];
439 var matchEnd = lastMatchInfo[CAPTURE(index + 1)];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000440 if (matchStart == -1 || matchEnd == -1) return '';
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000441 return SubString(LAST_SUBJECT(lastMatchInfo), matchStart, matchEnd);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000442 };
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000443}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000444
445
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000446// Property of the builtins object for recording the result of the last
447// regexp match. The property lastMatchInfo includes the matchIndices
448// array of the last successful regexp match (an array of start/end index
449// pairs for the match and all the captured substrings), the invariant is
450// that there are at least two capture indeces. The array also contains
451// the subject string for the last successful match.
452var lastMatchInfo = [
453 2, // REGEXP_NUMBER_OF_CAPTURES
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000454 "", // Last subject.
455 void 0, // Last input - settable with RegExpSetInput.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000456 0, // REGEXP_FIRST_CAPTURE + 0
457 0, // REGEXP_FIRST_CAPTURE + 1
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000458];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000459
lrn@chromium.org25156de2010-04-06 13:10:27 +0000460// Override last match info with an array of actual substrings.
461// Used internally by replace regexp with function.
462// The array has the format of an "apply" argument for a replacement
463// function.
464var lastMatchInfoOverride = null;
465
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000466// -------------------------------------------------------------------
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000467
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000468function SetupRegExp() {
469 %FunctionSetInstanceClassName($RegExp, 'RegExp');
470 %FunctionSetPrototype($RegExp, new $Object());
471 %SetProperty($RegExp.prototype, 'constructor', $RegExp, DONT_ENUM);
472 %SetCode($RegExp, RegExpConstructor);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000473
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000474 InstallFunctions($RegExp.prototype, DONT_ENUM, $Array(
475 "exec", RegExpExec,
476 "test", RegExpTest,
477 "toString", RegExpToString,
478 "compile", CompileRegExp
479 ));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000480
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000481 // The length of compile is 1 in SpiderMonkey.
482 %FunctionSetLength($RegExp.prototype.compile, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000483
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000484 // The properties input, $input, and $_ are aliases for each other. When this
lrn@chromium.org25156de2010-04-06 13:10:27 +0000485 // value is set the value it is set to is coerced to a string.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000486 // Getter and setter for the input.
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000487 function RegExpGetInput() {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000488 var regExpInput = LAST_INPUT(lastMatchInfo);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000489 return IS_UNDEFINED(regExpInput) ? "" : regExpInput;
490 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000491 function RegExpSetInput(string) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000492 regExpCache.type = 'none';
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000493 LAST_INPUT(lastMatchInfo) = ToString(string);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000494 };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000495
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000496 %DefineAccessor($RegExp, 'input', GETTER, RegExpGetInput, DONT_DELETE);
497 %DefineAccessor($RegExp, 'input', SETTER, RegExpSetInput, DONT_DELETE);
498 %DefineAccessor($RegExp, '$_', GETTER, RegExpGetInput, DONT_ENUM | DONT_DELETE);
499 %DefineAccessor($RegExp, '$_', SETTER, RegExpSetInput, DONT_ENUM | DONT_DELETE);
500 %DefineAccessor($RegExp, '$input', GETTER, RegExpGetInput, DONT_ENUM | DONT_DELETE);
501 %DefineAccessor($RegExp, '$input', SETTER, RegExpSetInput, DONT_ENUM | DONT_DELETE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000502
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000503 // The properties multiline and $* are aliases for each other. When this
504 // value is set in SpiderMonkey, the value it is set to is coerced to a
505 // boolean. We mimic that behavior with a slight difference: in SpiderMonkey
506 // the value of the expression 'RegExp.multiline = null' (for instance) is the
507 // boolean false (ie, the value after coercion), while in V8 it is the value
508 // null (ie, the value before coercion).
509
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000510 // Getter and setter for multiline.
511 var multiline = false;
512 function RegExpGetMultiline() { return multiline; };
513 function RegExpSetMultiline(flag) { multiline = flag ? true : false; };
514
515 %DefineAccessor($RegExp, 'multiline', GETTER, RegExpGetMultiline, DONT_DELETE);
516 %DefineAccessor($RegExp, 'multiline', SETTER, RegExpSetMultiline, DONT_DELETE);
517 %DefineAccessor($RegExp, '$*', GETTER, RegExpGetMultiline, DONT_ENUM | DONT_DELETE);
518 %DefineAccessor($RegExp, '$*', SETTER, RegExpSetMultiline, DONT_ENUM | DONT_DELETE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000519
520
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000521 function NoOpSetter(ignored) {}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000522
523
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000524 // Static properties set by a successful match.
525 %DefineAccessor($RegExp, 'lastMatch', GETTER, RegExpGetLastMatch, DONT_DELETE);
526 %DefineAccessor($RegExp, 'lastMatch', SETTER, NoOpSetter, DONT_DELETE);
527 %DefineAccessor($RegExp, '$&', GETTER, RegExpGetLastMatch, DONT_ENUM | DONT_DELETE);
528 %DefineAccessor($RegExp, '$&', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE);
529 %DefineAccessor($RegExp, 'lastParen', GETTER, RegExpGetLastParen, DONT_DELETE);
530 %DefineAccessor($RegExp, 'lastParen', SETTER, NoOpSetter, DONT_DELETE);
531 %DefineAccessor($RegExp, '$+', GETTER, RegExpGetLastParen, DONT_ENUM | DONT_DELETE);
532 %DefineAccessor($RegExp, '$+', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE);
533 %DefineAccessor($RegExp, 'leftContext', GETTER, RegExpGetLeftContext, DONT_DELETE);
534 %DefineAccessor($RegExp, 'leftContext', SETTER, NoOpSetter, DONT_DELETE);
535 %DefineAccessor($RegExp, '$`', GETTER, RegExpGetLeftContext, DONT_ENUM | DONT_DELETE);
536 %DefineAccessor($RegExp, '$`', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE);
537 %DefineAccessor($RegExp, 'rightContext', GETTER, RegExpGetRightContext, DONT_DELETE);
538 %DefineAccessor($RegExp, 'rightContext', SETTER, NoOpSetter, DONT_DELETE);
539 %DefineAccessor($RegExp, "$'", GETTER, RegExpGetRightContext, DONT_ENUM | DONT_DELETE);
540 %DefineAccessor($RegExp, "$'", SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000541
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000542 for (var i = 1; i < 10; ++i) {
543 %DefineAccessor($RegExp, '$' + i, GETTER, RegExpMakeCaptureGetter(i), DONT_DELETE);
544 %DefineAccessor($RegExp, '$' + i, SETTER, NoOpSetter, DONT_DELETE);
545 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000546}
547
548
549SetupRegExp();