blob: 24e3309805f9c1a522ca512fea90a80110fd5848 [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
260// Section 15.10.6.3 doesn't actually make sense, but the intention seems to be
261// that test is defined in terms of String.prototype.exec. However, it probably
262// means the original value of String.prototype.exec, which is what everybody
263// else implements.
264function RegExpTest(string) {
265 if (!IS_REGEXP(this)) {
Steve Block6ded16b2010-05-10 14:33:55 +0100266 throw MakeTypeError('incompatible_method_receiver',
Andrei Popescu31002712010-02-23 13:46:05 +0000267 ['RegExp.prototype.test', this]);
268 }
269 if (%_ArgumentsLength() == 0) {
270 var regExpInput = LAST_INPUT(lastMatchInfo);
271 if (IS_UNDEFINED(regExpInput)) {
272 throw MakeError('no_input_to_regexp', [this]);
273 }
274 string = regExpInput;
275 }
Steve Block6ded16b2010-05-10 14:33:55 +0100276 var s;
277 if (IS_STRING(string)) {
278 s = string;
279 } else {
280 s = ToString(string);
281 }
282
Andrei Popescu31002712010-02-23 13:46:05 +0000283 var lastIndex = this.lastIndex;
Steve Block6ded16b2010-05-10 14:33:55 +0100284
285 var cache = regExpCache;
286
287 if (%_ObjectEquals(cache.type, 'test') &&
288 %_ObjectEquals(cache.regExp, this) &&
289 %_ObjectEquals(cache.subject, string) &&
290 %_ObjectEquals(cache.lastIndex, lastIndex)) {
291 return cache.answer;
292 }
293
294 var length = s.length;
Andrei Popescu31002712010-02-23 13:46:05 +0000295 var i = this.global ? TO_INTEGER(lastIndex) : 0;
296
Steve Block6ded16b2010-05-10 14:33:55 +0100297 cache.type = 'test';
298 cache.regExp = this;
299 cache.subject = s;
300 cache.lastIndex = i;
301
Andrei Popescu31002712010-02-23 13:46:05 +0000302 if (i < 0 || i > s.length) {
303 this.lastIndex = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100304 cache.answer = false;
Andrei Popescu31002712010-02-23 13:46:05 +0000305 return false;
306 }
307
308 %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]);
309 // matchIndices is either null or the lastMatchInfo array.
310 var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo);
311
312 if (matchIndices == null) {
313 if (this.global) this.lastIndex = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100314 cache.answer = false;
Andrei Popescu31002712010-02-23 13:46:05 +0000315 return false;
316 }
Steve Block6ded16b2010-05-10 14:33:55 +0100317 lastMatchInfoOverride = null;
Andrei Popescu31002712010-02-23 13:46:05 +0000318 if (this.global) this.lastIndex = lastMatchInfo[CAPTURE1];
Steve Block6ded16b2010-05-10 14:33:55 +0100319 cache.answer = true;
Andrei Popescu31002712010-02-23 13:46:05 +0000320 return true;
321}
322
323
324function RegExpToString() {
325 // If this.source is an empty string, output /(?:)/.
326 // http://bugzilla.mozilla.org/show_bug.cgi?id=225550
327 // ecma_2/RegExp/properties-001.js.
328 var src = this.source ? this.source : '(?:)';
329 var result = '/' + src + '/';
330 if (this.global)
331 result += 'g';
332 if (this.ignoreCase)
333 result += 'i';
334 if (this.multiline)
335 result += 'm';
336 return result;
337}
338
339
340// Getters for the static properties lastMatch, lastParen, leftContext, and
341// rightContext of the RegExp constructor. The properties are computed based
342// on the captures array of the last successful match and the subject string
343// of the last successful match.
344function RegExpGetLastMatch() {
Steve Block6ded16b2010-05-10 14:33:55 +0100345 if (lastMatchInfoOverride !== null) {
346 return lastMatchInfoOverride[0];
347 }
Andrei Popescu31002712010-02-23 13:46:05 +0000348 var regExpSubject = LAST_SUBJECT(lastMatchInfo);
349 return SubString(regExpSubject,
350 lastMatchInfo[CAPTURE0],
351 lastMatchInfo[CAPTURE1]);
352}
353
354
355function RegExpGetLastParen() {
Steve Block6ded16b2010-05-10 14:33:55 +0100356 if (lastMatchInfoOverride) {
357 var override = lastMatchInfoOverride;
358 if (override.length <= 3) return '';
359 return override[override.length - 3];
360 }
Andrei Popescu31002712010-02-23 13:46:05 +0000361 var length = NUMBER_OF_CAPTURES(lastMatchInfo);
362 if (length <= 2) return ''; // There were no captures.
363 // We match the SpiderMonkey behavior: return the substring defined by the
364 // last pair (after the first pair) of elements of the capture array even if
365 // it is empty.
366 var regExpSubject = LAST_SUBJECT(lastMatchInfo);
367 var start = lastMatchInfo[CAPTURE(length - 2)];
368 var end = lastMatchInfo[CAPTURE(length - 1)];
369 if (start != -1 && end != -1) {
370 return SubString(regExpSubject, start, end);
371 }
372 return "";
373}
374
375
376function RegExpGetLeftContext() {
Steve Block6ded16b2010-05-10 14:33:55 +0100377 var start_index;
378 var subject;
379 if (!lastMatchInfoOverride) {
380 start_index = lastMatchInfo[CAPTURE0];
381 subject = LAST_SUBJECT(lastMatchInfo);
382 } else {
383 var override = lastMatchInfoOverride;
384 start_index = override[override.length - 2];
385 subject = override[override.length - 1];
386 }
387 return SubString(subject, 0, start_index);
Andrei Popescu31002712010-02-23 13:46:05 +0000388}
389
390
391function RegExpGetRightContext() {
Steve Block6ded16b2010-05-10 14:33:55 +0100392 var start_index;
393 var subject;
394 if (!lastMatchInfoOverride) {
395 start_index = lastMatchInfo[CAPTURE1];
396 subject = LAST_SUBJECT(lastMatchInfo);
397 } else {
398 var override = lastMatchInfoOverride;
399 subject = override[override.length - 1];
400 start_index = override[override.length - 2] + subject.length;
401 }
402 return SubString(subject, start_index, subject.length);
Andrei Popescu31002712010-02-23 13:46:05 +0000403}
404
405
406// The properties $1..$9 are the first nine capturing substrings of the last
407// successful match, or ''. The function RegExpMakeCaptureGetter will be
408// called with indices from 1 to 9.
409function RegExpMakeCaptureGetter(n) {
410 return function() {
Steve Block6ded16b2010-05-10 14:33:55 +0100411 if (lastMatchInfoOverride) {
412 if (n < lastMatchInfoOverride.length - 2) return lastMatchInfoOverride[n];
413 return '';
414 }
Andrei Popescu31002712010-02-23 13:46:05 +0000415 var index = n * 2;
416 if (index >= NUMBER_OF_CAPTURES(lastMatchInfo)) return '';
417 var matchStart = lastMatchInfo[CAPTURE(index)];
418 var matchEnd = lastMatchInfo[CAPTURE(index + 1)];
419 if (matchStart == -1 || matchEnd == -1) return '';
420 return SubString(LAST_SUBJECT(lastMatchInfo), matchStart, matchEnd);
421 };
422}
423
424
425// Property of the builtins object for recording the result of the last
426// regexp match. The property lastMatchInfo includes the matchIndices
427// array of the last successful regexp match (an array of start/end index
428// pairs for the match and all the captured substrings), the invariant is
429// that there are at least two capture indeces. The array also contains
430// the subject string for the last successful match.
431var lastMatchInfo = [
432 2, // REGEXP_NUMBER_OF_CAPTURES
433 "", // Last subject.
434 void 0, // Last input - settable with RegExpSetInput.
435 0, // REGEXP_FIRST_CAPTURE + 0
436 0, // REGEXP_FIRST_CAPTURE + 1
437];
438
Steve Block6ded16b2010-05-10 14:33:55 +0100439// Override last match info with an array of actual substrings.
440// Used internally by replace regexp with function.
441// The array has the format of an "apply" argument for a replacement
442// function.
443var lastMatchInfoOverride = null;
444
Andrei Popescu31002712010-02-23 13:46:05 +0000445// -------------------------------------------------------------------
446
447function SetupRegExp() {
448 %FunctionSetInstanceClassName($RegExp, 'RegExp');
449 %FunctionSetPrototype($RegExp, new $Object());
450 %SetProperty($RegExp.prototype, 'constructor', $RegExp, DONT_ENUM);
451 %SetCode($RegExp, RegExpConstructor);
452
453 InstallFunctions($RegExp.prototype, DONT_ENUM, $Array(
454 "exec", RegExpExec,
455 "test", RegExpTest,
456 "toString", RegExpToString,
457 "compile", CompileRegExp
458 ));
459
460 // The length of compile is 1 in SpiderMonkey.
461 %FunctionSetLength($RegExp.prototype.compile, 1);
462
463 // The properties input, $input, and $_ are aliases for each other. When this
Steve Block6ded16b2010-05-10 14:33:55 +0100464 // value is set the value it is set to is coerced to a string.
Andrei Popescu31002712010-02-23 13:46:05 +0000465 // Getter and setter for the input.
466 function RegExpGetInput() {
467 var regExpInput = LAST_INPUT(lastMatchInfo);
468 return IS_UNDEFINED(regExpInput) ? "" : regExpInput;
469 }
470 function RegExpSetInput(string) {
Steve Block6ded16b2010-05-10 14:33:55 +0100471 regExpCache.type = 'none';
Andrei Popescu31002712010-02-23 13:46:05 +0000472 LAST_INPUT(lastMatchInfo) = ToString(string);
473 };
474
475 %DefineAccessor($RegExp, 'input', GETTER, RegExpGetInput, DONT_DELETE);
476 %DefineAccessor($RegExp, 'input', SETTER, RegExpSetInput, DONT_DELETE);
477 %DefineAccessor($RegExp, '$_', GETTER, RegExpGetInput, DONT_ENUM | DONT_DELETE);
478 %DefineAccessor($RegExp, '$_', SETTER, RegExpSetInput, DONT_ENUM | DONT_DELETE);
479 %DefineAccessor($RegExp, '$input', GETTER, RegExpGetInput, DONT_ENUM | DONT_DELETE);
480 %DefineAccessor($RegExp, '$input', SETTER, RegExpSetInput, DONT_ENUM | DONT_DELETE);
481
482 // The properties multiline and $* are aliases for each other. When this
483 // value is set in SpiderMonkey, the value it is set to is coerced to a
484 // boolean. We mimic that behavior with a slight difference: in SpiderMonkey
485 // the value of the expression 'RegExp.multiline = null' (for instance) is the
486 // boolean false (ie, the value after coercion), while in V8 it is the value
487 // null (ie, the value before coercion).
488
489 // Getter and setter for multiline.
490 var multiline = false;
491 function RegExpGetMultiline() { return multiline; };
492 function RegExpSetMultiline(flag) { multiline = flag ? true : false; };
493
494 %DefineAccessor($RegExp, 'multiline', GETTER, RegExpGetMultiline, DONT_DELETE);
495 %DefineAccessor($RegExp, 'multiline', SETTER, RegExpSetMultiline, DONT_DELETE);
496 %DefineAccessor($RegExp, '$*', GETTER, RegExpGetMultiline, DONT_ENUM | DONT_DELETE);
497 %DefineAccessor($RegExp, '$*', SETTER, RegExpSetMultiline, DONT_ENUM | DONT_DELETE);
498
499
500 function NoOpSetter(ignored) {}
501
502
503 // Static properties set by a successful match.
504 %DefineAccessor($RegExp, 'lastMatch', GETTER, RegExpGetLastMatch, DONT_DELETE);
505 %DefineAccessor($RegExp, 'lastMatch', SETTER, NoOpSetter, DONT_DELETE);
506 %DefineAccessor($RegExp, '$&', GETTER, RegExpGetLastMatch, DONT_ENUM | DONT_DELETE);
507 %DefineAccessor($RegExp, '$&', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE);
508 %DefineAccessor($RegExp, 'lastParen', GETTER, RegExpGetLastParen, DONT_DELETE);
509 %DefineAccessor($RegExp, 'lastParen', SETTER, NoOpSetter, DONT_DELETE);
510 %DefineAccessor($RegExp, '$+', GETTER, RegExpGetLastParen, DONT_ENUM | DONT_DELETE);
511 %DefineAccessor($RegExp, '$+', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE);
512 %DefineAccessor($RegExp, 'leftContext', GETTER, RegExpGetLeftContext, DONT_DELETE);
513 %DefineAccessor($RegExp, 'leftContext', SETTER, NoOpSetter, DONT_DELETE);
514 %DefineAccessor($RegExp, '$`', GETTER, RegExpGetLeftContext, DONT_ENUM | DONT_DELETE);
515 %DefineAccessor($RegExp, '$`', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE);
516 %DefineAccessor($RegExp, 'rightContext', GETTER, RegExpGetRightContext, DONT_DELETE);
517 %DefineAccessor($RegExp, 'rightContext', SETTER, NoOpSetter, DONT_DELETE);
518 %DefineAccessor($RegExp, "$'", GETTER, RegExpGetRightContext, DONT_ENUM | DONT_DELETE);
519 %DefineAccessor($RegExp, "$'", SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE);
520
521 for (var i = 1; i < 10; ++i) {
522 %DefineAccessor($RegExp, '$' + i, GETTER, RegExpMakeCaptureGetter(i), DONT_DELETE);
523 %DefineAccessor($RegExp, '$' + i, SETTER, NoOpSetter, DONT_DELETE);
524 }
525}
526
527
528SetupRegExp();