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 StrictPropertyStub
, /* 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
);
214 COMPILE_EMSG(HitRecursionLimit
, JSMSG_REGEXP_TOO_COMPLEX
);
217 JS_NOT_REACHED("Precondition violation: unknown Yarr error code.");
222 RegExp::handlePCREError(JSContext
*cx
, int error
)
224 #define REPORT(msg_) \
225 JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, msg_); \
228 case -2: REPORT(JSMSG_REGEXP_TOO_COMPLEX
);
229 case 0: JS_NOT_REACHED("Precondition violation: an error must have occurred.");
230 case 1: REPORT(JSMSG_TRAILING_SLASH
);
231 case 2: REPORT(JSMSG_TRAILING_SLASH
);
232 case 3: REPORT(JSMSG_REGEXP_TOO_COMPLEX
);
233 case 4: REPORT(JSMSG_BAD_QUANTIFIER
);
234 case 5: REPORT(JSMSG_BAD_QUANTIFIER
);
235 case 6: REPORT(JSMSG_BAD_CLASS_RANGE
);
236 case 7: REPORT(JSMSG_REGEXP_TOO_COMPLEX
);
237 case 8: REPORT(JSMSG_BAD_CLASS_RANGE
);
238 case 9: REPORT(JSMSG_BAD_QUANTIFIER
);
239 case 10: REPORT(JSMSG_UNMATCHED_RIGHT_PAREN
);
240 case 11: REPORT(JSMSG_REGEXP_TOO_COMPLEX
);
241 case 12: REPORT(JSMSG_UNMATCHED_RIGHT_PAREN
);
242 case 13: REPORT(JSMSG_REGEXP_TOO_COMPLEX
);
243 case 14: REPORT(JSMSG_MISSING_PAREN
);
244 case 15: REPORT(JSMSG_BAD_BACKREF
);
245 case 16: REPORT(JSMSG_REGEXP_TOO_COMPLEX
);
246 case 17: REPORT(JSMSG_REGEXP_TOO_COMPLEX
);
248 JS_NOT_REACHED("Precondition violation: unknown PCRE error code.");
254 RegExp::parseFlags(JSContext
*cx
, JSString
*flagStr
, uintN
*flagsOut
)
256 size_t n
= flagStr
->length();
257 const jschar
*s
= flagStr
->getChars(cx
);
262 for (size_t i
= 0; i
< n
; i
++) {
263 #define HANDLE_FLAG(name_) \
265 if (*flagsOut & (name_)) \
267 *flagsOut |= (name_); \
270 case 'i': HANDLE_FLAG(JSREG_FOLD
); break;
271 case 'g': HANDLE_FLAG(JSREG_GLOB
); break;
272 case 'm': HANDLE_FLAG(JSREG_MULTILINE
); break;
273 case 'y': HANDLE_FLAG(JSREG_STICKY
); break;
278 charBuf
[0] = char(s
[i
]);
280 JS_ReportErrorFlagsAndNumber(cx
, JSREPORT_ERROR
, js_GetErrorMessage
, NULL
,
281 JSMSG_BAD_REGEXP_FLAG
, charBuf
);
290 AlreadyIncRefed
<RegExp
>
291 RegExp::createFlagged(JSContext
*cx
, JSString
*str
, JSString
*opt
)
294 return create(cx
, str
, 0);
296 if (!parseFlags(cx
, opt
, &flags
))
297 return AlreadyIncRefed
<RegExp
>(NULL
);
298 return create(cx
, str
, flags
);
302 * RegExp instance properties.
304 #define DEFINE_GETTER(name, code) \
306 name(JSContext *cx, JSObject *obj, jsid id, Value *vp) \
308 while (obj->getClass() != &js_RegExpClass) { \
309 obj = obj->getProto(); \
313 RegExp *re = RegExp::extractFrom(obj); \
318 /* lastIndex is stored in the object, re = re silences the compiler warning. */
319 DEFINE_GETTER(lastIndex_getter
, re
= re
; *vp
= obj
->getRegExpLastIndex())
320 DEFINE_GETTER(source_getter
, *vp
= StringValue(re
->getSource()))
321 DEFINE_GETTER(global_getter
, *vp
= BooleanValue(re
->global()))
322 DEFINE_GETTER(ignoreCase_getter
, *vp
= BooleanValue(re
->ignoreCase()))
323 DEFINE_GETTER(multiline_getter
, *vp
= BooleanValue(re
->multiline()))
324 DEFINE_GETTER(sticky_getter
, *vp
= BooleanValue(re
->sticky()))
327 lastIndex_setter(JSContext
*cx
, JSObject
*obj
, jsid id
, JSBool strict
, Value
*vp
)
329 while (obj
->getClass() != &js_RegExpClass
) {
330 obj
= obj
->getProto();
334 obj
->setRegExpLastIndex(*vp
);
338 static const struct LazyProp
{
342 } lazyRegExpProps
[] = {
343 { js_source_str
, ATOM_OFFSET(source
), source_getter
},
344 { js_global_str
, ATOM_OFFSET(global
), global_getter
},
345 { js_ignoreCase_str
, ATOM_OFFSET(ignoreCase
), ignoreCase_getter
},
346 { js_multiline_str
, ATOM_OFFSET(multiline
), multiline_getter
},
347 { js_sticky_str
, ATOM_OFFSET(sticky
), sticky_getter
}
351 regexp_resolve(JSContext
*cx
, JSObject
*obj
, jsid id
, uint32 flags
, JSObject
**objp
)
353 JS_ASSERT(obj
->isRegExp());
355 if (!JSID_IS_ATOM(id
))
358 if (id
== ATOM_TO_JSID(cx
->runtime
->atomState
.lastIndexAtom
)) {
359 if (!js_DefineNativeProperty(cx
, obj
, id
, UndefinedValue(),
360 lastIndex_getter
, lastIndex_setter
,
361 JSPROP_PERMANENT
| JSPROP_SHARED
,
369 for (size_t i
= 0; i
< JS_ARRAY_LENGTH(lazyRegExpProps
); i
++) {
370 const LazyProp
&lazy
= lazyRegExpProps
[i
];
371 JSAtom
*atom
= OFFSET_TO_ATOM(cx
->runtime
, lazy
.atomOffset
);
372 if (id
== ATOM_TO_JSID(atom
)) {
373 if (!js_DefineNativeProperty(cx
, obj
, id
, UndefinedValue(),
375 JSPROP_PERMANENT
| JSPROP_SHARED
| JSPROP_READONLY
,
388 * RegExp static properties.
390 * RegExp class static properties and their Perl counterparts:
393 * RegExp.multiline $*
394 * RegExp.lastMatch $&
395 * RegExp.lastParen $+
396 * RegExp.leftContext $`
397 * RegExp.rightContext $'
400 #define DEFINE_STATIC_GETTER(name, code) \
402 name(JSContext *cx, JSObject *obj, jsid id, jsval *vp) \
404 RegExpStatics *res = cx->regExpStatics(); \
408 DEFINE_STATIC_GETTER(static_input_getter
, return res
->createPendingInput(cx
, Valueify(vp
)))
409 DEFINE_STATIC_GETTER(static_multiline_getter
, *vp
= BOOLEAN_TO_JSVAL(res
->multiline());
411 DEFINE_STATIC_GETTER(static_lastMatch_getter
, return res
->createLastMatch(cx
, Valueify(vp
)))
412 DEFINE_STATIC_GETTER(static_lastParen_getter
, return res
->createLastParen(cx
, Valueify(vp
)))
413 DEFINE_STATIC_GETTER(static_leftContext_getter
, return res
->createLeftContext(cx
, Valueify(vp
)))
414 DEFINE_STATIC_GETTER(static_rightContext_getter
, return res
->createRightContext(cx
, Valueify(vp
)))
416 DEFINE_STATIC_GETTER(static_paren1_getter
, return res
->createParen(cx
, 1, Valueify(vp
)))
417 DEFINE_STATIC_GETTER(static_paren2_getter
, return res
->createParen(cx
, 2, Valueify(vp
)))
418 DEFINE_STATIC_GETTER(static_paren3_getter
, return res
->createParen(cx
, 3, Valueify(vp
)))
419 DEFINE_STATIC_GETTER(static_paren4_getter
, return res
->createParen(cx
, 4, Valueify(vp
)))
420 DEFINE_STATIC_GETTER(static_paren5_getter
, return res
->createParen(cx
, 5, Valueify(vp
)))
421 DEFINE_STATIC_GETTER(static_paren6_getter
, return res
->createParen(cx
, 6, Valueify(vp
)))
422 DEFINE_STATIC_GETTER(static_paren7_getter
, return res
->createParen(cx
, 7, Valueify(vp
)))
423 DEFINE_STATIC_GETTER(static_paren8_getter
, return res
->createParen(cx
, 8, Valueify(vp
)))
424 DEFINE_STATIC_GETTER(static_paren9_getter
, return res
->createParen(cx
, 9, Valueify(vp
)))
426 #define DEFINE_STATIC_SETTER(name, code) \
428 name(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp) \
430 RegExpStatics *res = cx->regExpStatics(); \
435 DEFINE_STATIC_SETTER(static_input_setter
,
436 if (!JSVAL_IS_STRING(*vp
) && !JS_ConvertValue(cx
, *vp
, JSTYPE_STRING
, vp
))
438 res
->setPendingInput(JSVAL_TO_STRING(*vp
)))
439 DEFINE_STATIC_SETTER(static_multiline_setter
,
440 if (!JSVAL_IS_BOOLEAN(*vp
) && !JS_ConvertValue(cx
, *vp
, JSTYPE_BOOLEAN
, vp
))
442 res
->setMultiline(!!JSVAL_TO_BOOLEAN(*vp
)))
444 const uint8 REGEXP_STATIC_PROP_ATTRS
= JSPROP_PERMANENT
| JSPROP_SHARED
| JSPROP_ENUMERATE
;
445 const uint8 RO_REGEXP_STATIC_PROP_ATTRS
= REGEXP_STATIC_PROP_ATTRS
| JSPROP_READONLY
;
447 static JSPropertySpec regexp_static_props
[] = {
448 {"input", 0, REGEXP_STATIC_PROP_ATTRS
, static_input_getter
, static_input_setter
},
449 {"multiline", 0, REGEXP_STATIC_PROP_ATTRS
, static_multiline_getter
,
450 static_multiline_setter
},
451 {"lastMatch", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_lastMatch_getter
, NULL
},
452 {"lastParen", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_lastParen_getter
, NULL
},
453 {"leftContext", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_leftContext_getter
, NULL
},
454 {"rightContext", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_rightContext_getter
, NULL
},
455 {"$1", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren1_getter
, NULL
},
456 {"$2", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren2_getter
, NULL
},
457 {"$3", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren3_getter
, NULL
},
458 {"$4", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren4_getter
, NULL
},
459 {"$5", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren5_getter
, NULL
},
460 {"$6", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren6_getter
, NULL
},
461 {"$7", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren7_getter
, NULL
},
462 {"$8", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren8_getter
, NULL
},
463 {"$9", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren9_getter
, NULL
},
468 regexp_finalize(JSContext
*cx
, JSObject
*obj
)
470 RegExp
*re
= RegExp::extractFrom(obj
);
476 /* Forward static prototype. */
478 regexp_exec_sub(JSContext
*cx
, JSObject
*obj
, uintN argc
, Value
*argv
, JSBool test
, Value
*rval
);
481 regexp_call(JSContext
*cx
, uintN argc
, Value
*vp
)
483 return regexp_exec_sub(cx
, &JS_CALLEE(cx
, vp
).toObject(), argc
, JS_ARGV(cx
, vp
), false, vp
);
488 #include "jsxdrapi.h"
491 js_XDRRegExpObject(JSXDRState
*xdr
, JSObject
**objp
)
493 JSString
*source
= 0;
494 uint32 flagsword
= 0;
496 if (xdr
->mode
== JSXDR_ENCODE
) {
498 RegExp
*re
= RegExp::extractFrom(*objp
);
501 source
= re
->getSource();
502 flagsword
= re
->getFlags();
504 if (!JS_XDRString(xdr
, &source
) || !JS_XDRUint32(xdr
, &flagsword
))
506 if (xdr
->mode
== JSXDR_DECODE
) {
507 JSObject
*obj
= NewBuiltinClassInstance(xdr
->cx
, &js_RegExpClass
);
512 AlreadyIncRefed
<RegExp
> re
= RegExp::create(xdr
->cx
, source
, flagsword
);
515 obj
->setPrivate(re
.get());
516 obj
->zeroRegExpLastIndex();
522 #else /* !JS_HAS_XDR */
524 #define js_XDRRegExpObject NULL
526 #endif /* !JS_HAS_XDR */
529 regexp_trace(JSTracer
*trc
, JSObject
*obj
)
531 RegExp
*re
= RegExp::extractFrom(obj
);
532 if (re
&& re
->getSource())
533 MarkString(trc
, re
->getSource(), "source");
537 regexp_enumerate(JSContext
*cx
, JSObject
*obj
)
539 JS_ASSERT(obj
->isRegExp());
542 if (!JS_LookupPropertyById(cx
, obj
, ATOM_TO_JSID(cx
->runtime
->atomState
.lastIndexAtom
), &v
))
545 for (size_t i
= 0; i
< JS_ARRAY_LENGTH(lazyRegExpProps
); i
++) {
546 const LazyProp
&lazy
= lazyRegExpProps
[i
];
547 jsid id
= ATOM_TO_JSID(OFFSET_TO_ATOM(cx
->runtime
, lazy
.atomOffset
));
548 if (!JS_LookupPropertyById(cx
, obj
, id
, &v
))
555 js::Class js_RegExpClass
= {
557 JSCLASS_HAS_PRIVATE
| JSCLASS_NEW_RESOLVE
|
558 JSCLASS_HAS_RESERVED_SLOTS(JSObject::REGEXP_CLASS_RESERVED_SLOTS
) |
559 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp
),
560 PropertyStub
, /* addProperty */
561 PropertyStub
, /* delProperty */
562 PropertyStub
, /* getProperty */
563 StrictPropertyStub
, /* setProperty */
565 reinterpret_cast<JSResolveOp
>(regexp_resolve
),
568 NULL
, /* reserved0 */
569 NULL
, /* checkAccess */
571 NULL
, /* construct */
573 NULL
, /* hasInstance */
574 JS_CLASS_TRACE(regexp_trace
)
578 * RegExp instance methods.
582 js_regexp_toString(JSContext
*cx
, JSObject
*obj
, Value
*vp
)
584 if (!InstanceOf(cx
, obj
, &js_RegExpClass
, vp
+ 2))
587 RegExp
*re
= RegExp::extractFrom(obj
);
589 *vp
= StringValue(cx
->runtime
->emptyString
);
593 JSLinearString
*src
= re
->getSource();
595 if (size_t len
= src
->length()) {
596 if (!sb
.reserve(len
+ 2))
598 JS_ALWAYS_TRUE(sb
.append('/'));
599 JS_ALWAYS_TRUE(sb
.append(src
->chars(), len
));
600 JS_ALWAYS_TRUE(sb
.append('/'));
602 if (!sb
.append("/(?:)/"))
605 if (re
->global() && !sb
.append('g'))
607 if (re
->ignoreCase() && !sb
.append('i'))
609 if (re
->multiline() && !sb
.append('m'))
611 if (re
->sticky() && !sb
.append('y'))
614 JSFlatString
*str
= sb
.finishString();
617 *vp
= StringValue(str
);
622 regexp_toString(JSContext
*cx
, uintN argc
, Value
*vp
)
624 JSObject
*obj
= ToObject(cx
, &vp
[1]);
627 return js_regexp_toString(cx
, obj
, vp
);
632 * - The original if no escaping need be performed.
633 * - A new string if escaping need be performed.
637 EscapeNakedForwardSlashes(JSContext
*cx
, JSString
*unescaped
)
639 size_t oldLen
= unescaped
->length();
640 const jschar
*oldChars
= unescaped
->getChars(cx
);
644 js::Vector
<jschar
, 128> newChars(cx
);
645 for (const jschar
*it
= oldChars
; it
< oldChars
+ oldLen
; ++it
) {
646 if (*it
== '/' && (it
== oldChars
|| it
[-1] != '\\')) {
647 if (!newChars
.length()) {
648 if (!newChars
.reserve(oldLen
+ 1))
650 newChars
.append(oldChars
, size_t(it
- oldChars
));
652 newChars
.append('\\');
655 if (newChars
.length())
656 newChars
.append(*it
);
659 if (newChars
.length()) {
660 size_t len
= newChars
.length();
661 if (!newChars
.append('\0'))
663 jschar
*chars
= newChars
.extractRawBuffer();
664 JSString
*escaped
= js_NewString(cx
, chars
, len
);
673 SwapRegExpInternals(JSContext
*cx
, JSObject
*obj
, Value
*rval
, JSString
*str
, uint32 flags
= 0)
675 flags
|= cx
->regExpStatics()->getFlags();
676 AlreadyIncRefed
<RegExp
> re
= RegExp::create(cx
, str
, flags
);
679 SwapObjectRegExp(cx
, obj
, re
);
680 *rval
= ObjectValue(*obj
);
685 regexp_exec_sub(JSContext
*cx
, JSObject
*obj
, uintN argc
, Value
*argv
, JSBool test
, Value
*rval
)
687 if (!InstanceOf(cx
, obj
, &js_RegExpClass
, argv
))
690 RegExp
*re
= RegExp::extractFrom(obj
);
695 * Code execution under this call could swap out the guts of |obj|, so we
696 * have to take a defensive refcount here.
698 AutoRefCount
<RegExp
> arc(cx
, NeedsIncRef
<RegExp
>(re
));
701 if (re
->global() || re
->sticky()) {
702 const Value v
= obj
->getRegExpLastIndex();
704 lastIndex
= v
.toInt32();
707 lastIndex
= v
.toDouble();
708 else if (!ValueToNumber(cx
, v
, &lastIndex
))
710 lastIndex
= js_DoubleToInteger(lastIndex
);
716 RegExpStatics
*res
= cx
->regExpStatics();
720 input
= js_ValueToString(cx
, argv
[0]);
723 argv
[0] = StringValue(input
);
725 /* Need to grab input from statics. */
726 input
= res
->getPendingInput();
728 JSAutoByteString
sourceBytes(cx
, re
->getSource());
730 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_NO_INPUT
,
732 re
->global() ? "g" : "",
733 re
->ignoreCase() ? "i" : "",
734 re
->multiline() ? "m" : "",
735 re
->sticky() ? "y" : "");
741 if (lastIndex
< 0 || input
->length() < lastIndex
) {
742 obj
->zeroRegExpLastIndex();
747 size_t lastIndexInt(lastIndex
);
748 if (!re
->execute(cx
, res
, input
, &lastIndexInt
, !!test
, rval
))
751 /* Update lastIndex. */
752 if (re
->global() || (!rval
->isNull() && re
->sticky())) {
754 obj
->zeroRegExpLastIndex();
756 obj
->setRegExpLastIndex(lastIndexInt
);
763 js_regexp_exec(JSContext
*cx
, uintN argc
, Value
*vp
)
765 JSObject
*obj
= ToObject(cx
, &vp
[1]);
768 return regexp_exec_sub(cx
, obj
, argc
, vp
+ 2, JS_FALSE
, vp
);
772 js_regexp_test(JSContext
*cx
, uintN argc
, Value
*vp
)
774 JSObject
*obj
= ToObject(cx
, &vp
[1]);
777 if (!regexp_exec_sub(cx
, obj
, argc
, vp
+ 2, JS_TRUE
, vp
))
780 vp
->setBoolean(false);
785 * Compile new js::RegExp guts for obj.
787 * Per ECMAv5 15.10.4.1, we act on combinations of (pattern, flags) as
790 * RegExp, undefined => flags := pattern.flags
791 * RegExp, _ => throw TypeError
792 * _ => pattern := ToString(pattern) if defined(pattern) else ''
793 * flags := ToString(flags) if defined(flags) else ''
796 CompileRegExpAndSwap(JSContext
*cx
, JSObject
*obj
, uintN argc
, Value
*argv
, Value
*rval
)
799 return SwapRegExpInternals(cx
, obj
, rval
, cx
->runtime
->emptyString
);
801 Value sourceValue
= argv
[0];
802 if (sourceValue
.isObject() && sourceValue
.toObject().getClass() == &js_RegExpClass
) {
804 * If we get passed in a RegExp object we return a new object with the
805 * same RegExp (internal matcher program) guts.
806 * Note: the regexp static flags are not taken into consideration here.
808 JSObject
&sourceObj
= sourceValue
.toObject();
809 if (argc
>= 2 && !argv
[1].isUndefined()) {
810 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_NEWREGEXP_FLAGGED
);
813 RegExp
*re
= RegExp::extractFrom(&sourceObj
);
818 SwapObjectRegExp(cx
, obj
, AlreadyIncRefed
<RegExp
>(re
));
820 *rval
= ObjectValue(*obj
);
824 /* Coerce to string and compile. */
825 JSString
*sourceStr
= js_ValueToString(cx
, sourceValue
);
830 if (argc
> 1 && !argv
[1].isUndefined()) {
831 JSString
*flagStr
= js_ValueToString(cx
, argv
[1]);
834 argv
[1].setString(flagStr
);
835 if (!RegExp::parseFlags(cx
, flagStr
, &flags
))
839 JSString
*escapedSourceStr
= EscapeNakedForwardSlashes(cx
, sourceStr
);
840 if (!escapedSourceStr
)
843 return SwapRegExpInternals(cx
, obj
, rval
, escapedSourceStr
, flags
);
847 regexp_compile(JSContext
*cx
, uintN argc
, Value
*vp
)
849 JSObject
*obj
= ToObject(cx
, &vp
[1]);
850 if (!obj
|| !InstanceOf(cx
, obj
, &js_RegExpClass
, JS_ARGV(cx
, vp
)))
853 return CompileRegExpAndSwap(cx
, obj
, argc
, JS_ARGV(cx
, vp
), &JS_RVAL(cx
, vp
));
857 regexp_construct(JSContext
*cx
, uintN argc
, Value
*vp
)
859 Value
*argv
= JS_ARGV(cx
, vp
);
861 if (!IsConstructing(vp
)) {
863 * If first arg is regexp and no flags are given, just return the arg.
864 * Otherwise, delegate to the standard constructor.
865 * See ECMAv5 15.10.3.1.
867 if (argc
>= 1 && argv
[0].isObject() && argv
[0].toObject().isRegExp() &&
868 (argc
== 1 || argv
[1].isUndefined())) {
874 JSObject
*obj
= NewBuiltinClassInstance(cx
, &js_RegExpClass
);
878 return CompileRegExpAndSwap(cx
, obj
, argc
, argv
, &JS_RVAL(cx
, vp
));
881 static JSFunctionSpec regexp_methods
[] = {
883 JS_FN(js_toSource_str
, regexp_toString
, 0,0),
885 JS_FN(js_toString_str
, regexp_toString
, 0,0),
886 JS_FN("compile", regexp_compile
, 2,0),
887 JS_FN("exec", js_regexp_exec
, 1,0),
888 JS_FN("test", js_regexp_test
, 1,0),
892 /* Similar to SwapRegExpInternals. */
894 InitRegExpClassCompile(JSContext
*cx
, JSObject
*obj
)
896 AlreadyIncRefed
<RegExp
> re
= RegExp::create(cx
, cx
->runtime
->emptyString
, 0);
899 SwapObjectRegExp(cx
, obj
, re
);
904 js_InitRegExpClass(JSContext
*cx
, JSObject
*obj
)
906 JSObject
*proto
= js_InitClass(cx
, obj
, NULL
, &js_RegExpClass
, regexp_construct
, 2,
907 NULL
, regexp_methods
, regexp_static_props
, NULL
);
911 JSObject
*ctor
= JS_GetConstructor(cx
, proto
);
915 /* Give RegExp.prototype private data so it matches the empty string. */
916 if (!JS_AliasProperty(cx
, ctor
, "input", "$_") ||
917 !JS_AliasProperty(cx
, ctor
, "multiline", "$*") ||
918 !JS_AliasProperty(cx
, ctor
, "lastMatch", "$&") ||
919 !JS_AliasProperty(cx
, ctor
, "lastParen", "$+") ||
920 !JS_AliasProperty(cx
, ctor
, "leftContext", "$`") ||
921 !JS_AliasProperty(cx
, ctor
, "rightContext", "$'") ||
922 !InitRegExpClassCompile(cx
, proto
)) {