1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set sw=4 ts=8 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
17 * The Original Code is Mozilla Communicator client code, released
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
42 * JS regular expressions, after Perl.
58 #include "jsobjinlines.h"
59 #include "jsregexpinlines.h"
61 #include "yarr/RegexParser.h"
65 using namespace avmplus
;
66 using namespace nanojit
;
70 using namespace js::gc
;
73 * RegExpStatics allocates memory -- in order to keep the statics stored
74 * per-global and not leak, we create a js::Class to wrap the C++ instance and
75 * provide an appropriate finalizer. We store an instance of that js::Class in
76 * a global reserved slot.
80 resc_finalize(JSContext
*cx
, JSObject
*obj
)
82 RegExpStatics
*res
= static_cast<RegExpStatics
*>(obj
->getPrivate());
83 cx
->destroy
<RegExpStatics
>(res
);
87 resc_trace(JSTracer
*trc
, JSObject
*obj
)
89 void *pdata
= obj
->getPrivate();
91 RegExpStatics
*res
= static_cast<RegExpStatics
*>(pdata
);
95 Class
js::regexp_statics_class
= {
97 JSCLASS_HAS_PRIVATE
| JSCLASS_MARK_IS_TRACE
,
98 PropertyStub
, /* addProperty */
99 PropertyStub
, /* delProperty */
100 PropertyStub
, /* getProperty */
101 PropertyStub
, /* setProperty */
106 NULL
, /* reserved0 */
107 NULL
, /* checkAccess */
109 NULL
, /* construct */
110 NULL
, /* xdrObject */
111 NULL
, /* hasInstance */
112 JS_CLASS_TRACE(resc_trace
)
116 * Replace the regexp internals of |obj| with |newRegExp|.
117 * Decref the replaced regexp internals.
118 * Note that the refcount of |newRegExp| is unchanged.
121 SwapObjectRegExp(JSContext
*cx
, JSObject
*obj
, AlreadyIncRefed
<RegExp
> newRegExp
)
123 RegExp
*oldRegExp
= RegExp::extractFrom(obj
);
126 assertSameCompartment(cx
, obj
, oldRegExp
->compartment
);
127 assertSameCompartment(cx
, obj
, newRegExp
->compartment
);
130 obj
->setPrivate(newRegExp
.get());
131 obj
->zeroRegExpLastIndex();
133 oldRegExp
->decref(cx
);
136 JSObject
* JS_FASTCALL
137 js_CloneRegExpObject(JSContext
*cx
, JSObject
*obj
, JSObject
*proto
)
139 JS_ASSERT(obj
->getClass() == &js_RegExpClass
);
141 JS_ASSERT(proto
->getClass() == &js_RegExpClass
);
143 JSObject
*clone
= NewNativeClassInstance(cx
, &js_RegExpClass
, proto
, proto
->getParent());
148 * This clone functionality does not duplicate the JITted code blob, which is necessary for
149 * cross-compartment cloning functionality.
151 assertSameCompartment(cx
, obj
, clone
);
153 RegExpStatics
*res
= cx
->regExpStatics();
154 RegExp
*re
= RegExp::extractFrom(obj
);
156 uint32 origFlags
= re
->getFlags();
157 uint32 staticsFlags
= res
->getFlags();
158 if ((origFlags
& staticsFlags
) != staticsFlags
) {
160 * This regex is lacking flags from the statics, so we must recompile with the new
161 * flags instead of increffing.
163 AlreadyIncRefed
<RegExp
> clone
= RegExp::create(cx
, re
->getSource(), origFlags
| staticsFlags
);
172 clone
->setPrivate(re
);
173 clone
->zeroRegExpLastIndex();
178 JS_DEFINE_CALLINFO_3(extern, OBJECT
, js_CloneRegExpObject
, CONTEXT
, OBJECT
, OBJECT
, 0,
183 js_ObjectIsRegExp(JSObject
*obj
)
185 return obj
->isRegExp();
193 RegExp::handleYarrError(JSContext
*cx
, int error
)
196 case JSC::Yarr::NoError
:
197 JS_NOT_REACHED("Precondition violation: an error must have occurred.");
199 #define COMPILE_EMSG(__code, __msg) \
200 case JSC::Yarr::__code: \
201 JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, __msg); \
203 COMPILE_EMSG(PatternTooLarge
, JSMSG_REGEXP_TOO_COMPLEX
);
204 COMPILE_EMSG(QuantifierOutOfOrder
, JSMSG_BAD_QUANTIFIER
);
205 COMPILE_EMSG(QuantifierWithoutAtom
, JSMSG_BAD_QUANTIFIER
);
206 COMPILE_EMSG(MissingParentheses
, JSMSG_MISSING_PAREN
);
207 COMPILE_EMSG(ParenthesesUnmatched
, JSMSG_UNMATCHED_RIGHT_PAREN
);
208 COMPILE_EMSG(ParenthesesTypeInvalid
, JSMSG_BAD_QUANTIFIER
); /* "(?" with bad next char */
209 COMPILE_EMSG(CharacterClassUnmatched
, JSMSG_BAD_CLASS_RANGE
);
210 COMPILE_EMSG(CharacterClassOutOfOrder
, JSMSG_BAD_CLASS_RANGE
);
211 COMPILE_EMSG(CharacterClassRangeSingleChar
, JSMSG_BAD_CLASS_RANGE
);
212 COMPILE_EMSG(EscapeUnterminated
, JSMSG_TRAILING_SLASH
);
213 COMPILE_EMSG(QuantifierTooLarge
, JSMSG_BAD_QUANTIFIER
);
216 JS_NOT_REACHED("Precondition violation: unknown Yarr error code.");
221 RegExp::handlePCREError(JSContext
*cx
, int error
)
223 #define REPORT(msg_) \
224 JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, msg_); \
227 case 0: JS_NOT_REACHED("Precondition violation: an error must have occurred.");
228 case 1: REPORT(JSMSG_TRAILING_SLASH
);
229 case 2: REPORT(JSMSG_TRAILING_SLASH
);
230 case 3: REPORT(JSMSG_REGEXP_TOO_COMPLEX
);
231 case 4: REPORT(JSMSG_BAD_QUANTIFIER
);
232 case 5: REPORT(JSMSG_BAD_QUANTIFIER
);
233 case 6: REPORT(JSMSG_BAD_CLASS_RANGE
);
234 case 7: REPORT(JSMSG_REGEXP_TOO_COMPLEX
);
235 case 8: REPORT(JSMSG_BAD_CLASS_RANGE
);
236 case 9: REPORT(JSMSG_BAD_QUANTIFIER
);
237 case 10: REPORT(JSMSG_UNMATCHED_RIGHT_PAREN
);
238 case 11: REPORT(JSMSG_REGEXP_TOO_COMPLEX
);
239 case 12: REPORT(JSMSG_UNMATCHED_RIGHT_PAREN
);
240 case 13: REPORT(JSMSG_REGEXP_TOO_COMPLEX
);
241 case 14: REPORT(JSMSG_MISSING_PAREN
);
242 case 15: REPORT(JSMSG_BAD_BACKREF
);
243 case 16: REPORT(JSMSG_REGEXP_TOO_COMPLEX
);
244 case 17: REPORT(JSMSG_REGEXP_TOO_COMPLEX
);
246 JS_NOT_REACHED("Precondition violation: unknown PCRE error code.");
252 RegExp::parseFlags(JSContext
*cx
, JSString
*flagStr
, uintN
*flagsOut
)
254 size_t n
= flagStr
->length();
255 const jschar
*s
= flagStr
->getChars(cx
);
260 for (size_t i
= 0; i
< n
; i
++) {
261 #define HANDLE_FLAG(name_) \
263 if (*flagsOut & (name_)) \
265 *flagsOut |= (name_); \
268 case 'i': HANDLE_FLAG(JSREG_FOLD
); break;
269 case 'g': HANDLE_FLAG(JSREG_GLOB
); break;
270 case 'm': HANDLE_FLAG(JSREG_MULTILINE
); break;
271 case 'y': HANDLE_FLAG(JSREG_STICKY
); break;
276 charBuf
[0] = char(s
[i
]);
278 JS_ReportErrorFlagsAndNumber(cx
, JSREPORT_ERROR
, js_GetErrorMessage
, NULL
,
279 JSMSG_BAD_REGEXP_FLAG
, charBuf
);
288 AlreadyIncRefed
<RegExp
>
289 RegExp::createFlagged(JSContext
*cx
, JSString
*str
, JSString
*opt
)
292 return create(cx
, str
, 0);
294 if (!parseFlags(cx
, opt
, &flags
))
295 return AlreadyIncRefed
<RegExp
>(NULL
);
296 return create(cx
, str
, flags
);
300 * RegExp instance properties.
302 #define DEFINE_GETTER(name, code) \
304 name(JSContext *cx, JSObject *obj, jsid id, Value *vp) \
306 while (obj->getClass() != &js_RegExpClass) { \
307 obj = obj->getProto(); \
311 RegExp *re = RegExp::extractFrom(obj); \
316 /* lastIndex is stored in the object, re = re silences the compiler warning. */
317 DEFINE_GETTER(lastIndex_getter
, re
= re
; *vp
= obj
->getRegExpLastIndex())
318 DEFINE_GETTER(source_getter
, *vp
= StringValue(re
->getSource()))
319 DEFINE_GETTER(global_getter
, *vp
= BooleanValue(re
->global()))
320 DEFINE_GETTER(ignoreCase_getter
, *vp
= BooleanValue(re
->ignoreCase()))
321 DEFINE_GETTER(multiline_getter
, *vp
= BooleanValue(re
->multiline()))
322 DEFINE_GETTER(sticky_getter
, *vp
= BooleanValue(re
->sticky()))
325 lastIndex_setter(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
327 while (obj
->getClass() != &js_RegExpClass
) {
328 obj
= obj
->getProto();
332 obj
->setRegExpLastIndex(*vp
);
336 static const struct LazyProp
{
340 } lazyRegExpProps
[] = {
341 { js_source_str
, ATOM_OFFSET(source
), source_getter
},
342 { js_global_str
, ATOM_OFFSET(global
), global_getter
},
343 { js_ignoreCase_str
, ATOM_OFFSET(ignoreCase
), ignoreCase_getter
},
344 { js_multiline_str
, ATOM_OFFSET(multiline
), multiline_getter
},
345 { js_sticky_str
, ATOM_OFFSET(sticky
), sticky_getter
}
349 regexp_resolve(JSContext
*cx
, JSObject
*obj
, jsid id
, uint32 flags
, JSObject
**objp
)
351 JS_ASSERT(obj
->isRegExp());
353 if (!JSID_IS_ATOM(id
))
356 if (id
== ATOM_TO_JSID(cx
->runtime
->atomState
.lastIndexAtom
)) {
357 if (!js_DefineNativeProperty(cx
, obj
, id
, UndefinedValue(),
358 lastIndex_getter
, lastIndex_setter
,
359 JSPROP_PERMANENT
| JSPROP_SHARED
,
367 for (size_t i
= 0; i
< JS_ARRAY_LENGTH(lazyRegExpProps
); i
++) {
368 const LazyProp
&lazy
= lazyRegExpProps
[i
];
369 JSAtom
*atom
= OFFSET_TO_ATOM(cx
->runtime
, lazy
.atomOffset
);
370 if (id
== ATOM_TO_JSID(atom
)) {
371 if (!js_DefineNativeProperty(cx
, obj
, id
, UndefinedValue(),
373 JSPROP_PERMANENT
| JSPROP_SHARED
| JSPROP_READONLY
,
386 * RegExp static properties.
388 * RegExp class static properties and their Perl counterparts:
391 * RegExp.multiline $*
392 * RegExp.lastMatch $&
393 * RegExp.lastParen $+
394 * RegExp.leftContext $`
395 * RegExp.rightContext $'
398 #define DEFINE_STATIC_GETTER(name, code) \
400 name(JSContext *cx, JSObject *obj, jsid id, jsval *vp) \
402 RegExpStatics *res = cx->regExpStatics(); \
406 DEFINE_STATIC_GETTER(static_input_getter
, return res
->createPendingInput(cx
, Valueify(vp
)))
407 DEFINE_STATIC_GETTER(static_multiline_getter
, *vp
= BOOLEAN_TO_JSVAL(res
->multiline());
409 DEFINE_STATIC_GETTER(static_lastMatch_getter
, return res
->createLastMatch(cx
, Valueify(vp
)))
410 DEFINE_STATIC_GETTER(static_lastParen_getter
, return res
->createLastParen(cx
, Valueify(vp
)))
411 DEFINE_STATIC_GETTER(static_leftContext_getter
, return res
->createLeftContext(cx
, Valueify(vp
)))
412 DEFINE_STATIC_GETTER(static_rightContext_getter
, return res
->createRightContext(cx
, Valueify(vp
)))
414 DEFINE_STATIC_GETTER(static_paren1_getter
, return res
->createParen(cx
, 1, Valueify(vp
)))
415 DEFINE_STATIC_GETTER(static_paren2_getter
, return res
->createParen(cx
, 2, Valueify(vp
)))
416 DEFINE_STATIC_GETTER(static_paren3_getter
, return res
->createParen(cx
, 3, Valueify(vp
)))
417 DEFINE_STATIC_GETTER(static_paren4_getter
, return res
->createParen(cx
, 4, Valueify(vp
)))
418 DEFINE_STATIC_GETTER(static_paren5_getter
, return res
->createParen(cx
, 5, Valueify(vp
)))
419 DEFINE_STATIC_GETTER(static_paren6_getter
, return res
->createParen(cx
, 6, Valueify(vp
)))
420 DEFINE_STATIC_GETTER(static_paren7_getter
, return res
->createParen(cx
, 7, Valueify(vp
)))
421 DEFINE_STATIC_GETTER(static_paren8_getter
, return res
->createParen(cx
, 8, Valueify(vp
)))
422 DEFINE_STATIC_GETTER(static_paren9_getter
, return res
->createParen(cx
, 9, Valueify(vp
)))
424 #define DEFINE_STATIC_SETTER(name, code) \
426 name(JSContext *cx, JSObject *obj, jsid id, jsval *vp) \
428 RegExpStatics *res = cx->regExpStatics(); \
433 DEFINE_STATIC_SETTER(static_input_setter
,
434 if (!JSVAL_IS_STRING(*vp
) && !JS_ConvertValue(cx
, *vp
, JSTYPE_STRING
, vp
))
436 res
->setPendingInput(JSVAL_TO_STRING(*vp
)))
437 DEFINE_STATIC_SETTER(static_multiline_setter
,
438 if (!JSVAL_IS_BOOLEAN(*vp
) && !JS_ConvertValue(cx
, *vp
, JSTYPE_BOOLEAN
, vp
))
440 res
->setMultiline(!!JSVAL_TO_BOOLEAN(*vp
)))
442 const uint8 REGEXP_STATIC_PROP_ATTRS
= JSPROP_PERMANENT
| JSPROP_SHARED
| JSPROP_ENUMERATE
;
443 const uint8 RO_REGEXP_STATIC_PROP_ATTRS
= REGEXP_STATIC_PROP_ATTRS
| JSPROP_READONLY
;
445 static JSPropertySpec regexp_static_props
[] = {
446 {"input", 0, REGEXP_STATIC_PROP_ATTRS
, static_input_getter
, static_input_setter
},
447 {"multiline", 0, REGEXP_STATIC_PROP_ATTRS
, static_multiline_getter
,
448 static_multiline_setter
},
449 {"lastMatch", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_lastMatch_getter
, NULL
},
450 {"lastParen", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_lastParen_getter
, NULL
},
451 {"leftContext", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_leftContext_getter
, NULL
},
452 {"rightContext", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_rightContext_getter
, NULL
},
453 {"$1", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren1_getter
, NULL
},
454 {"$2", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren2_getter
, NULL
},
455 {"$3", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren3_getter
, NULL
},
456 {"$4", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren4_getter
, NULL
},
457 {"$5", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren5_getter
, NULL
},
458 {"$6", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren6_getter
, NULL
},
459 {"$7", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren7_getter
, NULL
},
460 {"$8", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren8_getter
, NULL
},
461 {"$9", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren9_getter
, NULL
},
466 regexp_finalize(JSContext
*cx
, JSObject
*obj
)
468 RegExp
*re
= RegExp::extractFrom(obj
);
474 /* Forward static prototype. */
476 regexp_exec_sub(JSContext
*cx
, JSObject
*obj
, uintN argc
, Value
*argv
, JSBool test
, Value
*rval
);
479 regexp_call(JSContext
*cx
, uintN argc
, Value
*vp
)
481 return regexp_exec_sub(cx
, &JS_CALLEE(cx
, vp
).toObject(), argc
, JS_ARGV(cx
, vp
), false, vp
);
486 #include "jsxdrapi.h"
489 js_XDRRegExpObject(JSXDRState
*xdr
, JSObject
**objp
)
491 JSString
*source
= 0;
492 uint32 flagsword
= 0;
494 if (xdr
->mode
== JSXDR_ENCODE
) {
496 RegExp
*re
= RegExp::extractFrom(*objp
);
499 source
= re
->getSource();
500 flagsword
= re
->getFlags();
502 if (!JS_XDRString(xdr
, &source
) || !JS_XDRUint32(xdr
, &flagsword
))
504 if (xdr
->mode
== JSXDR_DECODE
) {
505 JSObject
*obj
= NewBuiltinClassInstance(xdr
->cx
, &js_RegExpClass
);
510 AlreadyIncRefed
<RegExp
> re
= RegExp::create(xdr
->cx
, source
, flagsword
);
513 obj
->setPrivate(re
.get());
514 obj
->zeroRegExpLastIndex();
520 #else /* !JS_HAS_XDR */
522 #define js_XDRRegExpObject NULL
524 #endif /* !JS_HAS_XDR */
527 regexp_trace(JSTracer
*trc
, JSObject
*obj
)
529 RegExp
*re
= RegExp::extractFrom(obj
);
530 if (re
&& re
->getSource())
531 MarkString(trc
, re
->getSource(), "source");
535 regexp_enumerate(JSContext
*cx
, JSObject
*obj
)
537 JS_ASSERT(obj
->isRegExp());
540 if (!JS_LookupPropertyById(cx
, obj
, ATOM_TO_JSID(cx
->runtime
->atomState
.lastIndexAtom
), &v
))
543 for (size_t i
= 0; i
< JS_ARRAY_LENGTH(lazyRegExpProps
); i
++) {
544 const LazyProp
&lazy
= lazyRegExpProps
[i
];
545 jsid id
= ATOM_TO_JSID(OFFSET_TO_ATOM(cx
->runtime
, lazy
.atomOffset
));
546 if (!JS_LookupPropertyById(cx
, obj
, id
, &v
))
553 js::Class js_RegExpClass
= {
555 JSCLASS_HAS_PRIVATE
| JSCLASS_NEW_RESOLVE
|
556 JSCLASS_HAS_RESERVED_SLOTS(JSObject::REGEXP_CLASS_RESERVED_SLOTS
) |
557 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp
),
558 PropertyStub
, /* addProperty */
559 PropertyStub
, /* delProperty */
560 PropertyStub
, /* getProperty */
561 PropertyStub
, /* setProperty */
563 reinterpret_cast<JSResolveOp
>(regexp_resolve
),
566 NULL
, /* reserved0 */
567 NULL
, /* checkAccess */
569 NULL
, /* construct */
571 NULL
, /* hasInstance */
572 JS_CLASS_TRACE(regexp_trace
)
576 * RegExp instance methods.
580 js_regexp_toString(JSContext
*cx
, JSObject
*obj
, Value
*vp
)
582 if (!InstanceOf(cx
, obj
, &js_RegExpClass
, vp
+ 2))
585 RegExp
*re
= RegExp::extractFrom(obj
);
587 *vp
= StringValue(cx
->runtime
->emptyString
);
591 JSLinearString
*src
= re
->getSource();
593 if (size_t len
= src
->length()) {
594 if (!sb
.reserve(len
+ 2))
596 JS_ALWAYS_TRUE(sb
.append('/'));
597 JS_ALWAYS_TRUE(sb
.append(src
->chars(), len
));
598 JS_ALWAYS_TRUE(sb
.append('/'));
600 if (!sb
.append("/(?:)/"))
603 if (re
->global() && !sb
.append('g'))
605 if (re
->ignoreCase() && !sb
.append('i'))
607 if (re
->multiline() && !sb
.append('m'))
609 if (re
->sticky() && !sb
.append('y'))
612 JSFlatString
*str
= sb
.finishString();
615 *vp
= StringValue(str
);
620 regexp_toString(JSContext
*cx
, uintN argc
, Value
*vp
)
622 JSObject
*obj
= JS_THIS_OBJECT(cx
, Jsvalify(vp
));
623 return obj
&& js_regexp_toString(cx
, obj
, vp
);
628 * - The original if no escaping need be performed.
629 * - A new string if escaping need be performed.
633 EscapeNakedForwardSlashes(JSContext
*cx
, JSString
*unescaped
)
635 size_t oldLen
= unescaped
->length();
636 const jschar
*oldChars
= unescaped
->getChars(cx
);
640 js::Vector
<jschar
, 128> newChars(cx
);
641 for (const jschar
*it
= oldChars
; it
< oldChars
+ oldLen
; ++it
) {
642 if (*it
== '/' && (it
== oldChars
|| it
[-1] != '\\')) {
643 if (!newChars
.length()) {
644 if (!newChars
.reserve(oldLen
+ 1))
646 newChars
.append(oldChars
, size_t(it
- oldChars
));
648 newChars
.append('\\');
651 if (newChars
.length())
652 newChars
.append(*it
);
655 if (newChars
.length()) {
656 size_t len
= newChars
.length();
657 if (!newChars
.append('\0'))
659 jschar
*chars
= newChars
.extractRawBuffer();
660 JSString
*escaped
= js_NewString(cx
, chars
, len
);
669 SwapRegExpInternals(JSContext
*cx
, JSObject
*obj
, Value
*rval
, JSString
*str
, uint32 flags
= 0)
671 flags
|= cx
->regExpStatics()->getFlags();
672 AlreadyIncRefed
<RegExp
> re
= RegExp::create(cx
, str
, flags
);
675 SwapObjectRegExp(cx
, obj
, re
);
676 *rval
= ObjectValue(*obj
);
681 regexp_exec_sub(JSContext
*cx
, JSObject
*obj
, uintN argc
, Value
*argv
, JSBool test
, Value
*rval
)
683 if (!InstanceOf(cx
, obj
, &js_RegExpClass
, argv
))
686 RegExp
*re
= RegExp::extractFrom(obj
);
691 * Code execution under this call could swap out the guts of |obj|, so we
692 * have to take a defensive refcount here.
694 AutoRefCount
<RegExp
> arc(cx
, NeedsIncRef
<RegExp
>(re
));
697 if (re
->global() || re
->sticky()) {
698 const Value v
= obj
->getRegExpLastIndex();
700 lastIndex
= v
.toInt32();
703 lastIndex
= v
.toDouble();
704 else if (!ValueToNumber(cx
, v
, &lastIndex
))
706 lastIndex
= js_DoubleToInteger(lastIndex
);
712 RegExpStatics
*res
= cx
->regExpStatics();
716 input
= js_ValueToString(cx
, argv
[0]);
719 argv
[0] = StringValue(input
);
721 /* Need to grab input from statics. */
722 input
= res
->getPendingInput();
724 JSAutoByteString
sourceBytes(cx
, re
->getSource());
726 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_NO_INPUT
,
728 re
->global() ? "g" : "",
729 re
->ignoreCase() ? "i" : "",
730 re
->multiline() ? "m" : "",
731 re
->sticky() ? "y" : "");
737 if (lastIndex
< 0 || input
->length() < lastIndex
) {
738 obj
->zeroRegExpLastIndex();
743 size_t lastIndexInt(lastIndex
);
744 if (!re
->execute(cx
, res
, input
, &lastIndexInt
, !!test
, rval
))
747 /* Update lastIndex. */
748 if (re
->global() || (!rval
->isNull() && re
->sticky())) {
750 obj
->zeroRegExpLastIndex();
752 obj
->setRegExpLastIndex(lastIndexInt
);
759 js_regexp_exec(JSContext
*cx
, uintN argc
, Value
*vp
)
761 return regexp_exec_sub(cx
, JS_THIS_OBJECT(cx
, Jsvalify(vp
)), argc
, vp
+ 2, JS_FALSE
, vp
);
765 js_regexp_test(JSContext
*cx
, uintN argc
, Value
*vp
)
767 if (!regexp_exec_sub(cx
, JS_THIS_OBJECT(cx
, Jsvalify(vp
)), argc
, vp
+ 2, JS_TRUE
, vp
))
770 vp
->setBoolean(false);
774 static JSFunctionSpec regexp_methods
[] = {
776 JS_FN(js_toSource_str
, regexp_toString
, 0,0),
778 JS_FN(js_toString_str
, regexp_toString
, 0,0),
779 JS_FN("exec", js_regexp_exec
, 1,0),
780 JS_FN("test", js_regexp_test
, 1,0),
785 regexp_construct(JSContext
*cx
, uintN argc
, Value
*vp
)
787 Value
*argv
= JS_ARGV(cx
, vp
);
788 Value
*rval
= &JS_RVAL(cx
, vp
);
790 if (!IsConstructing(vp
)) {
792 * If first arg is regexp and no flags are given, just return the arg.
793 * Otherwise, delegate to the standard constructor.
794 * See ECMAv5 15.10.3.1.
796 if (argc
>= 1 && argv
[0].isObject() && argv
[0].toObject().isRegExp() &&
797 (argc
== 1 || argv
[1].isUndefined())) {
804 * Per ECMAv5 15.10.4.1, we act on combinations of (pattern, flags):
805 * RegExp, undefined => flags := pattern.flags
806 * RegExp, _ => throw TypeError
807 * _ => pattern := ToString(pattern) if defined(pattern) else ''
808 * flags := ToString(flags) if defined(flags) else ''
811 JSObject
*obj
= NewBuiltinClassInstance(cx
, &js_RegExpClass
);
816 return SwapRegExpInternals(cx
, obj
, rval
, cx
->runtime
->emptyString
);
818 Value sourceValue
= argv
[0];
819 if (sourceValue
.isObject() && sourceValue
.toObject().getClass() == &js_RegExpClass
) {
821 * If we get passed in a RegExp object we return a new object with the
822 * same RegExp (internal matcher program) guts.
823 * Note: the regexp static flags are not taken into consideration here.
825 JSObject
&sourceObj
= sourceValue
.toObject();
826 if (argc
>= 2 && !argv
[1].isUndefined()) {
827 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_NEWREGEXP_FLAGGED
);
830 RegExp
*re
= RegExp::extractFrom(&sourceObj
);
835 SwapObjectRegExp(cx
, obj
, AlreadyIncRefed
<RegExp
>(re
));
837 *rval
= ObjectValue(*obj
);
841 /* Coerce to string and compile. */
842 JSString
*sourceStr
= js_ValueToString(cx
, sourceValue
);
847 if (argc
> 1 && !argv
[1].isUndefined()) {
848 JSString
*flagStr
= js_ValueToString(cx
, argv
[1]);
851 argv
[1].setString(flagStr
);
852 if (!RegExp::parseFlags(cx
, flagStr
, &flags
))
856 JSString
*escapedSourceStr
= EscapeNakedForwardSlashes(cx
, sourceStr
);
857 if (!escapedSourceStr
)
860 return SwapRegExpInternals(cx
, obj
, rval
, escapedSourceStr
, flags
);
863 /* Similar to SwapRegExpInternals. */
865 InitRegExpClassCompile(JSContext
*cx
, JSObject
*obj
)
867 AlreadyIncRefed
<RegExp
> re
= RegExp::create(cx
, cx
->runtime
->emptyString
, 0);
870 SwapObjectRegExp(cx
, obj
, re
);
875 js_InitRegExpClass(JSContext
*cx
, JSObject
*obj
)
877 JSObject
*proto
= js_InitClass(cx
, obj
, NULL
, &js_RegExpClass
, regexp_construct
, 2,
878 NULL
, regexp_methods
, regexp_static_props
, NULL
);
882 JSObject
*ctor
= JS_GetConstructor(cx
, proto
);
886 /* Give RegExp.prototype private data so it matches the empty string. */
887 if (!JS_AliasProperty(cx
, ctor
, "input", "$_") ||
888 !JS_AliasProperty(cx
, ctor
, "multiline", "$*") ||
889 !JS_AliasProperty(cx
, ctor
, "lastMatch", "$&") ||
890 !JS_AliasProperty(cx
, ctor
, "lastParen", "$+") ||
891 !JS_AliasProperty(cx
, ctor
, "leftContext", "$`") ||
892 !JS_AliasProperty(cx
, ctor
, "rightContext", "$'") ||
893 !InitRegExpClassCompile(cx
, proto
)) {