blob: 9367f15f91d88cd36ae6e83b43f1945061166ee5 [file] [log] [blame]
Andrei Popescu31002712010-02-23 13:46:05 +00001// Copyright 2006-2009 the V8 project authors. All rights reserved.
2// 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++) {
55 var c = StringCharAt.call(flags, i);
56 switch (c) {
57 case 'g':
58 // Allow duplicate flags to be consistent with JSC and others.
59 global = true;
60 break;
61 case 'i':
62 ignoreCase = true;
63 break;
64 case 'm':
65 multiline = true;
66 break;
67 default:
68 // Ignore flags that have no meaning to be consistent with
69 // JSC.
70 break;
71 }
72 }
73
Steve Block6ded16b2010-05-10 14:33:55 +010074 if (!isConstructorCall) {
75 regExpCache.type = 'none';
Andrei Popescu31002712010-02-23 13:46:05 +000076 }
Steve Block6ded16b2010-05-10 14:33:55 +010077 %RegExpInitializeObject(object, pattern, global, ignoreCase, multiline);
Andrei Popescu31002712010-02-23 13:46:05 +000078
79 // Call internal function to compile the pattern.
80 %RegExpCompile(object, pattern, flags);
81}
82
83
84function RegExpConstructor(pattern, flags) {
85 if (%_IsConstructCall()) {
86 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 }
94}
95
96
97// Deprecated RegExp.prototype.compile method. We behave like the constructor
98// were called again. In SpiderMonkey, this method returns the regexp object.
99// In JSC, it returns undefined. For compatibility with JSC, we match their
100// behavior.
101function CompileRegExp(pattern, flags) {
102 // Both JSC and SpiderMonkey treat a missing pattern argument as the
103 // empty subject string, and an actual undefined value passed as the
104 // pattern as the string 'undefined'. Note that JSC is inconsistent
105 // here, treating undefined values differently in
106 // RegExp.prototype.compile and in the constructor, where they are
107 // the empty string. For compatibility with JSC, we match their
108 // 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
117function DoRegExpExec(regexp, string, index) {
Steve Block6ded16b2010-05-10 14:33:55 +0100118 var result = %_RegExpExec(regexp, string, index, lastMatchInfo);
119 if (result !== null) lastMatchInfoOverride = null;
120 return result;
121}
122
123
124function RegExpCache() {
125 this.type = 'none';
126 this.regExp = 0;
127 this.subject = 0;
128 this.replaceString = 0;
129 this.lastIndex = 0;
130 this.answer = 0;
131 // answerSaved marks whether the contents of answer is valid for a cache
132 // hit in RegExpExec, StringMatch and StringSplit.
133 this.answerSaved = false;
134}
135
136
137var regExpCache = new RegExpCache();
138
139
140function CloneRegExpResult(array) {
141 if (array == null) return null;
142 var length = array.length;
143 var answer = %_RegExpConstructResult(length, array.index, array.input);
144 for (var i = 0; i < length; i++) {
145 answer[i] = array[i];
146 }
147 return answer;
148}
149
150
151function 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;
Andrei Popescu31002712010-02-23 13:46:05 +0000183}
184
185
186function RegExpExec(string) {
187 if (!IS_REGEXP(this)) {
Steve Block6ded16b2010-05-10 14:33:55 +0100188 throw MakeTypeError('incompatible_method_receiver',
Andrei Popescu31002712010-02-23 13:46:05 +0000189 ['RegExp.prototype.exec', this]);
190 }
Steve Block6ded16b2010-05-10 14:33:55 +0100191
192 var cache = regExpCache;
193 var saveAnswer = false;
194
195 if (%_ObjectEquals(cache.type, 'exec') &&
196 %_ObjectEquals(cache.lastIndex, this.lastIndex) &&
197 %_ObjectEquals(cache.regExp, this) &&
198 %_ObjectEquals(cache.subject, string)) {
199 if (cache.answerSaved) {
200 return CloneRegExpResult(cache.answer);
201 } else {
202 saveAnswer = true;
203 }
204 }
205
Andrei Popescu31002712010-02-23 13:46:05 +0000206 if (%_ArgumentsLength() == 0) {
207 var regExpInput = LAST_INPUT(lastMatchInfo);
208 if (IS_UNDEFINED(regExpInput)) {
209 throw MakeError('no_input_to_regexp', [this]);
210 }
211 string = regExpInput;
212 }
Steve Block6ded16b2010-05-10 14:33:55 +0100213 var s;
214 if (IS_STRING(string)) {
215 s = string;
216 } else {
217 s = ToString(string);
218 }
Andrei Popescu31002712010-02-23 13:46:05 +0000219 var lastIndex = this.lastIndex;
Steve Block6ded16b2010-05-10 14:33:55 +0100220
Andrei Popescu31002712010-02-23 13:46:05 +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
228 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]);
229 // matchIndices is either null or the lastMatchInfo array.
230 var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo);
231
232 if (matchIndices == null) {
233 if (this.global) this.lastIndex = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100234 cache.lastIndex = lastIndex;
235 cache.regExp = this;
236 cache.subject = s;
237 cache.answer = matchIndices; // Null.
238 cache.answerSaved = true; // Safe since no cloning is needed.
239 cache.type = 'exec';
240 return matchIndices; // No match.
Andrei Popescu31002712010-02-23 13:46:05 +0000241 }
Steve Block6ded16b2010-05-10 14:33:55 +0100242 lastMatchInfoOverride = null;
243 var result = BuildResultFromMatchInfo(matchIndices, s);
Andrei Popescu31002712010-02-23 13:46:05 +0000244
Steve Block6ded16b2010-05-10 14:33:55 +0100245 if (this.global) {
Andrei Popescu31002712010-02-23 13:46:05 +0000246 this.lastIndex = lastMatchInfo[CAPTURE1];
Steve Block6ded16b2010-05-10 14:33:55 +0100247 } else {
248 cache.regExp = this;
249 cache.subject = s;
250 cache.lastIndex = lastIndex;
251 if (saveAnswer) cache.answer = CloneRegExpResult(result);
252 cache.answerSaved = saveAnswer;
253 cache.type = 'exec';
254 }
Andrei Popescu31002712010-02-23 13:46:05 +0000255 return result;
Steve Block6ded16b2010-05-10 14:33:55 +0100256
Andrei Popescu31002712010-02-23 13:46:05 +0000257}
258
259
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100260// One-element cache for the simplified test regexp.
261var regexp_key;
262var regexp_val;
263
Andrei Popescu31002712010-02-23 13:46:05 +0000264// Section 15.10.6.3 doesn't actually make sense, but the intention seems to be
265// that test is defined in terms of String.prototype.exec. However, it probably
266// means the original value of String.prototype.exec, which is what everybody
267// else implements.
268function RegExpTest(string) {
269 if (!IS_REGEXP(this)) {
Steve Block6ded16b2010-05-10 14:33:55 +0100270 throw MakeTypeError('incompatible_method_receiver',
Andrei Popescu31002712010-02-23 13:46:05 +0000271 ['RegExp.prototype.test', this]);
272 }
273 if (%_ArgumentsLength() == 0) {
274 var regExpInput = LAST_INPUT(lastMatchInfo);
275 if (IS_UNDEFINED(regExpInput)) {
276 throw MakeError('no_input_to_regexp', [this]);
277 }
278 string = regExpInput;
279 }
Steve Block6ded16b2010-05-10 14:33:55 +0100280 var s;
281 if (IS_STRING(string)) {
282 s = string;
283 } else {
284 s = ToString(string);
285 }
286
Andrei Popescu31002712010-02-23 13:46:05 +0000287 var lastIndex = this.lastIndex;
Steve Block6ded16b2010-05-10 14:33:55 +0100288 var cache = regExpCache;
Steve Block6ded16b2010-05-10 14:33:55 +0100289 if (%_ObjectEquals(cache.type, 'test') &&
290 %_ObjectEquals(cache.regExp, this) &&
291 %_ObjectEquals(cache.subject, string) &&
292 %_ObjectEquals(cache.lastIndex, lastIndex)) {
293 return cache.answer;
294 }
295
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100296 // Remove irrelevant preceeding '.*' in a test regexp. The expression
297 // checks whether this.source starts with '.*' and that the third
298 // char is not a '?'
299 if (%_StringCharCodeAt(this.source,0) == 46 && // '.'
300 %_StringCharCodeAt(this.source,1) == 42 && // '*'
301 %_StringCharCodeAt(this.source,2) != 63) { // '?'
302 if (!%_ObjectEquals(regexp_key, this)) {
303 regexp_key = this;
304 regexp_val = new $RegExp(this.source.substring(2, this.source.length),
305 (this.global ? 'g' : '')
306 + (this.ignoreCase ? 'i' : '')
307 + (this.multiline ? 'm' : ''));
308 }
309 if (!regexp_val.test(s)) return false;
310 }
311
Steve Block6ded16b2010-05-10 14:33:55 +0100312 var length = s.length;
Andrei Popescu31002712010-02-23 13:46:05 +0000313 var i = this.global ? TO_INTEGER(lastIndex) : 0;
314
Steve Block6ded16b2010-05-10 14:33:55 +0100315 cache.type = 'test';
316 cache.regExp = this;
317 cache.subject = s;
318 cache.lastIndex = i;
319
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100320 if (i < 0 || i > length) {
Andrei Popescu31002712010-02-23 13:46:05 +0000321 this.lastIndex = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100322 cache.answer = false;
Andrei Popescu31002712010-02-23 13:46:05 +0000323 return false;
324 }
325
326 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]);
327 // matchIndices is either null or the lastMatchInfo array.
328 var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo);
329
330 if (matchIndices == null) {
331 if (this.global) this.lastIndex = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100332 cache.answer = false;
Andrei Popescu31002712010-02-23 13:46:05 +0000333 return false;
334 }
Steve Block6ded16b2010-05-10 14:33:55 +0100335 lastMatchInfoOverride = null;
Andrei Popescu31002712010-02-23 13:46:05 +0000336 if (this.global) this.lastIndex = lastMatchInfo[CAPTURE1];
Steve Block6ded16b2010-05-10 14:33:55 +0100337 cache.answer = true;
Andrei Popescu31002712010-02-23 13:46:05 +0000338 return true;
339}
340
341
342function RegExpToString() {
343 // If this.source is an empty string, output /(?:)/.
344 // http://bugzilla.mozilla.org/show_bug.cgi?id=225550
345 // ecma_2/RegExp/properties-001.js.
346 var src = this.source ? this.source : '(?:)';
347 var result = '/' + src + '/';
348 if (this.global)
349 result += 'g';
350 if (this.ignoreCase)
351 result += 'i';
352 if (this.multiline)
353 result += 'm';
354 return result;
355}
356
357
358// Getters for the static properties lastMatch, lastParen, leftContext, and
359// rightContext of the RegExp constructor. The properties are computed based
360// on the captures array of the last successful match and the subject string
361// of the last successful match.
362function RegExpGetLastMatch() {
Steve Block6ded16b2010-05-10 14:33:55 +0100363 if (lastMatchInfoOverride !== null) {
364 return lastMatchInfoOverride[0];
365 }
Andrei Popescu31002712010-02-23 13:46:05 +0000366 var regExpSubject = LAST_SUBJECT(lastMatchInfo);
367 return SubString(regExpSubject,
368 lastMatchInfo[CAPTURE0],
369 lastMatchInfo[CAPTURE1]);
370}
371
372
373function RegExpGetLastParen() {
Steve Block6ded16b2010-05-10 14:33:55 +0100374 if (lastMatchInfoOverride) {
375 var override = lastMatchInfoOverride;
376 if (override.length <= 3) return '';
377 return override[override.length - 3];
378 }
Andrei Popescu31002712010-02-23 13:46:05 +0000379 var length = NUMBER_OF_CAPTURES(lastMatchInfo);
380 if (length <= 2) return ''; // There were no captures.
381 // We match the SpiderMonkey behavior: return the substring defined by the
382 // last pair (after the first pair) of elements of the capture array even if
383 // it is empty.
384 var regExpSubject = LAST_SUBJECT(lastMatchInfo);
385 var start = lastMatchInfo[CAPTURE(length - 2)];
386 var end = lastMatchInfo[CAPTURE(length - 1)];
387 if (start != -1 && end != -1) {
388 return SubString(regExpSubject, start, end);
389 }
390 return "";
391}
392
393
394function RegExpGetLeftContext() {
Steve Block6ded16b2010-05-10 14:33:55 +0100395 var start_index;
396 var subject;
397 if (!lastMatchInfoOverride) {
398 start_index = lastMatchInfo[CAPTURE0];
399 subject = LAST_SUBJECT(lastMatchInfo);
400 } else {
401 var override = lastMatchInfoOverride;
402 start_index = override[override.length - 2];
403 subject = override[override.length - 1];
404 }
405 return SubString(subject, 0, start_index);
Andrei Popescu31002712010-02-23 13:46:05 +0000406}
407
408
409function RegExpGetRightContext() {
Steve Block6ded16b2010-05-10 14:33:55 +0100410 var start_index;
411 var subject;
412 if (!lastMatchInfoOverride) {
413 start_index = lastMatchInfo[CAPTURE1];
414 subject = LAST_SUBJECT(lastMatchInfo);
415 } else {
416 var override = lastMatchInfoOverride;
417 subject = override[override.length - 1];
418 start_index = override[override.length - 2] + subject.length;
419 }
420 return SubString(subject, start_index, subject.length);
Andrei Popescu31002712010-02-23 13:46:05 +0000421}
422
423
424// The properties $1..$9 are the first nine capturing substrings of the last
425// successful match, or ''. The function RegExpMakeCaptureGetter will be
426// called with indices from 1 to 9.
427function RegExpMakeCaptureGetter(n) {
428 return function() {
Steve Block6ded16b2010-05-10 14:33:55 +0100429 if (lastMatchInfoOverride) {
430 if (n < lastMatchInfoOverride.length - 2) return lastMatchInfoOverride[n];
431 return '';
432 }
Andrei Popescu31002712010-02-23 13:46:05 +0000433 var index = n * 2;
434 if (index >= NUMBER_OF_CAPTURES(lastMatchInfo)) return '';
435 var matchStart = lastMatchInfo[CAPTURE(index)];
436 var matchEnd = lastMatchInfo[CAPTURE(index + 1)];
437 if (matchStart == -1 || matchEnd == -1) return '';
438 return SubString(LAST_SUBJECT(lastMatchInfo), matchStart, matchEnd);
439 };
440}
441
442
443// Property of the builtins object for recording the result of the last
444// regexp match. The property lastMatchInfo includes the matchIndices
445// array of the last successful regexp match (an array of start/end index
446// pairs for the match and all the captured substrings), the invariant is
447// that there are at least two capture indeces. The array also contains
448// the subject string for the last successful match.
449var lastMatchInfo = [
450 2, // REGEXP_NUMBER_OF_CAPTURES
451 "", // Last subject.
452 void 0, // Last input - settable with RegExpSetInput.
453 0, // REGEXP_FIRST_CAPTURE + 0
454 0, // REGEXP_FIRST_CAPTURE + 1
455];
456
Steve Block6ded16b2010-05-10 14:33:55 +0100457// Override last match info with an array of actual substrings.
458// Used internally by replace regexp with function.
459// The array has the format of an "apply" argument for a replacement
460// function.
461var lastMatchInfoOverride = null;
462
Andrei Popescu31002712010-02-23 13:46:05 +0000463// -------------------------------------------------------------------
464
465function SetupRegExp() {
466 %FunctionSetInstanceClassName($RegExp, 'RegExp');
467 %FunctionSetPrototype($RegExp, new $Object());
468 %SetProperty($RegExp.prototype, 'constructor', $RegExp, DONT_ENUM);
469 %SetCode($RegExp, RegExpConstructor);
470
471 InstallFunctions($RegExp.prototype, DONT_ENUM, $Array(
472 "exec", RegExpExec,
473 "test", RegExpTest,
474 "toString", RegExpToString,
475 "compile", CompileRegExp
476 ));
477
478 // The length of compile is 1 in SpiderMonkey.
479 %FunctionSetLength($RegExp.prototype.compile, 1);
480
481 // The properties input, $input, and $_ are aliases for each other. When this
Steve Block6ded16b2010-05-10 14:33:55 +0100482 // value is set the value it is set to is coerced to a string.
Andrei Popescu31002712010-02-23 13:46:05 +0000483 // Getter and setter for the input.
484 function RegExpGetInput() {
485 var regExpInput = LAST_INPUT(lastMatchInfo);
486 return IS_UNDEFINED(regExpInput) ? "" : regExpInput;
487 }
488 function RegExpSetInput(string) {
Steve Block6ded16b2010-05-10 14:33:55 +0100489 regExpCache.type = 'none';
Andrei Popescu31002712010-02-23 13:46:05 +0000490 LAST_INPUT(lastMatchInfo) = ToString(string);
491 };
492
493 %DefineAccessor($RegExp, 'input', GETTER, RegExpGetInput, DONT_DELETE);
494 %DefineAccessor($RegExp, 'input', SETTER, RegExpSetInput, DONT_DELETE);
495 %DefineAccessor($RegExp, '$_', GETTER, RegExpGetInput, DONT_ENUM | DONT_DELETE);
496 %DefineAccessor($RegExp, '$_', SETTER, RegExpSetInput, DONT_ENUM | DONT_DELETE);
497 %DefineAccessor($RegExp, '$input', GETTER, RegExpGetInput, DONT_ENUM | DONT_DELETE);
498 %DefineAccessor($RegExp, '$input', SETTER, RegExpSetInput, DONT_ENUM | DONT_DELETE);
499
500 // The properties multiline and $* are aliases for each other. When this
501 // value is set in SpiderMonkey, the value it is set to is coerced to a
502 // boolean. We mimic that behavior with a slight difference: in SpiderMonkey
503 // the value of the expression 'RegExp.multiline = null' (for instance) is the
504 // boolean false (ie, the value after coercion), while in V8 it is the value
505 // null (ie, the value before coercion).
506
507 // Getter and setter for multiline.
508 var multiline = false;
509 function RegExpGetMultiline() { return multiline; };
510 function RegExpSetMultiline(flag) { multiline = flag ? true : false; };
511
512 %DefineAccessor($RegExp, 'multiline', GETTER, RegExpGetMultiline, DONT_DELETE);
513 %DefineAccessor($RegExp, 'multiline', SETTER, RegExpSetMultiline, DONT_DELETE);
514 %DefineAccessor($RegExp, '$*', GETTER, RegExpGetMultiline, DONT_ENUM | DONT_DELETE);
515 %DefineAccessor($RegExp, '$*', SETTER, RegExpSetMultiline, DONT_ENUM | DONT_DELETE);
516
517
518 function NoOpSetter(ignored) {}
519
520
521 // Static properties set by a successful match.
522 %DefineAccessor($RegExp, 'lastMatch', GETTER, RegExpGetLastMatch, DONT_DELETE);
523 %DefineAccessor($RegExp, 'lastMatch', SETTER, NoOpSetter, DONT_DELETE);
524 %DefineAccessor($RegExp, '$&', GETTER, RegExpGetLastMatch, DONT_ENUM | DONT_DELETE);
525 %DefineAccessor($RegExp, '$&', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE);
526 %DefineAccessor($RegExp, 'lastParen', GETTER, RegExpGetLastParen, DONT_DELETE);
527 %DefineAccessor($RegExp, 'lastParen', SETTER, NoOpSetter, DONT_DELETE);
528 %DefineAccessor($RegExp, '$+', GETTER, RegExpGetLastParen, DONT_ENUM | DONT_DELETE);
529 %DefineAccessor($RegExp, '$+', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE);
530 %DefineAccessor($RegExp, 'leftContext', GETTER, RegExpGetLeftContext, DONT_DELETE);
531 %DefineAccessor($RegExp, 'leftContext', SETTER, NoOpSetter, DONT_DELETE);
532 %DefineAccessor($RegExp, '$`', GETTER, RegExpGetLeftContext, DONT_ENUM | DONT_DELETE);
533 %DefineAccessor($RegExp, '$`', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE);
534 %DefineAccessor($RegExp, 'rightContext', GETTER, RegExpGetRightContext, DONT_DELETE);
535 %DefineAccessor($RegExp, 'rightContext', SETTER, NoOpSetter, DONT_DELETE);
536 %DefineAccessor($RegExp, "$'", GETTER, RegExpGetRightContext, DONT_ENUM | DONT_DELETE);
537 %DefineAccessor($RegExp, "$'", SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE);
538
539 for (var i = 1; i < 10; ++i) {
540 %DefineAccessor($RegExp, '$' + i, GETTER, RegExpMakeCaptureGetter(i), DONT_DELETE);
541 %DefineAccessor($RegExp, '$' + i, SETTER, NoOpSetter, DONT_DELETE);
542 }
543}
544
545
546SetupRegExp();