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"
63 using namespace avmplus
;
64 using namespace nanojit
;
68 using namespace js::gc
;
71 * RegExpStatics allocates memory -- in order to keep the statics stored
72 * per-global and not leak, we create a js::Class to wrap the C++ instance and
73 * provide an appropriate finalizer. We store an instance of that js::Class in
74 * a global reserved slot.
78 resc_finalize(JSContext
*cx
, JSObject
*obj
)
80 RegExpStatics
*res
= static_cast<RegExpStatics
*>(obj
->getPrivate());
81 cx
->destroy
<RegExpStatics
>(res
);
85 resc_trace(JSTracer
*trc
, JSObject
*obj
)
87 void *pdata
= obj
->getPrivate();
89 RegExpStatics
*res
= static_cast<RegExpStatics
*>(pdata
);
93 Class
js::regexp_statics_class
= {
95 JSCLASS_HAS_PRIVATE
| JSCLASS_MARK_IS_TRACE
,
96 PropertyStub
, /* addProperty */
97 PropertyStub
, /* delProperty */
98 PropertyStub
, /* getProperty */
99 PropertyStub
, /* setProperty */
104 NULL
, /* reserved0 */
105 NULL
, /* checkAccess */
107 NULL
, /* construct */
108 NULL
, /* xdrObject */
109 NULL
, /* hasInstance */
110 JS_CLASS_TRACE(resc_trace
)
114 * Lock obj and replace its regexp internals with |newRegExp|.
115 * Decref the replaced regexp internals.
118 SwapObjectRegExp(JSContext
*cx
, JSObject
*obj
, RegExp
&newRegExp
)
120 RegExp
*oldRegExp
= RegExp::extractFrom(obj
);
121 obj
->setPrivate(&newRegExp
);
122 obj
->zeroRegExpLastIndex();
124 oldRegExp
->decref(cx
);
127 JSObject
* JS_FASTCALL
128 js_CloneRegExpObject(JSContext
*cx
, JSObject
*obj
, JSObject
*proto
)
130 JS_ASSERT(obj
->getClass() == &js_RegExpClass
);
132 JS_ASSERT(proto
->getClass() == &js_RegExpClass
);
133 JSObject
*clone
= NewNativeClassInstance(cx
, &js_RegExpClass
, proto
, proto
->getParent());
136 RegExpStatics
*res
= cx
->regExpStatics();
137 RegExp
*re
= RegExp::extractFrom(obj
);
139 uint32 origFlags
= re
->getFlags();
140 uint32 staticsFlags
= res
->getFlags();
141 if ((origFlags
& staticsFlags
) != staticsFlags
) {
143 * This regex is lacking flags from the statics, so we must recompile with the new
144 * flags instead of increffing.
146 re
= RegExp::create(cx
, re
->getSource(), origFlags
| staticsFlags
);
151 clone
->setPrivate(re
);
152 clone
->zeroRegExpLastIndex();
157 JS_DEFINE_CALLINFO_3(extern, OBJECT
, js_CloneRegExpObject
, CONTEXT
, OBJECT
, OBJECT
, 0,
162 js_ObjectIsRegExp(JSObject
*obj
)
164 return obj
->isRegExp();
172 RegExp::handleYarrError(JSContext
*cx
, int error
)
174 /* Hack: duplicated from yarr/yarr/RegexParser.h */
178 QuantifierOutOfOrder
,
179 QuantifierWithoutAtom
,
181 ParenthesesUnmatched
,
182 ParenthesesTypeInvalid
, /* "(?" with bad next char or end of pattern. */
183 CharacterClassUnmatched
,
184 CharacterClassOutOfOrder
,
190 JS_NOT_REACHED("Precondition violation: an error must have occurred.");
192 #define COMPILE_EMSG(__code, __msg) \
194 JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, __msg); \
196 COMPILE_EMSG(PatternTooLarge
, JSMSG_REGEXP_TOO_COMPLEX
);
197 COMPILE_EMSG(QuantifierOutOfOrder
, JSMSG_BAD_QUANTIFIER
);
198 COMPILE_EMSG(QuantifierWithoutAtom
, JSMSG_BAD_QUANTIFIER
);
199 COMPILE_EMSG(MissingParentheses
, JSMSG_MISSING_PAREN
);
200 COMPILE_EMSG(ParenthesesUnmatched
, JSMSG_UNMATCHED_RIGHT_PAREN
);
201 COMPILE_EMSG(ParenthesesTypeInvalid
, JSMSG_BAD_QUANTIFIER
);
202 COMPILE_EMSG(CharacterClassUnmatched
, JSMSG_BAD_CLASS_RANGE
);
203 COMPILE_EMSG(CharacterClassOutOfOrder
, JSMSG_BAD_CLASS_RANGE
);
204 COMPILE_EMSG(EscapeUnterminated
, JSMSG_TRAILING_SLASH
);
205 COMPILE_EMSG(QuantifierTooLarge
, JSMSG_BAD_QUANTIFIER
);
208 JS_NOT_REACHED("Precondition violation: unknown Yarr error code.");
213 RegExp::handlePCREError(JSContext
*cx
, int error
)
215 #define REPORT(__msg) \
216 JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, __msg); \
219 case 0: JS_NOT_REACHED("Precondition violation: an error must have occurred.");
220 case 1: REPORT(JSMSG_TRAILING_SLASH
);
221 case 2: REPORT(JSMSG_TRAILING_SLASH
);
222 case 3: REPORT(JSMSG_REGEXP_TOO_COMPLEX
);
223 case 4: REPORT(JSMSG_BAD_QUANTIFIER
);
224 case 5: REPORT(JSMSG_BAD_QUANTIFIER
);
225 case 6: REPORT(JSMSG_BAD_CLASS_RANGE
);
226 case 7: REPORT(JSMSG_REGEXP_TOO_COMPLEX
);
227 case 8: REPORT(JSMSG_BAD_CLASS_RANGE
);
228 case 9: REPORT(JSMSG_BAD_QUANTIFIER
);
229 case 10: REPORT(JSMSG_UNMATCHED_RIGHT_PAREN
);
230 case 11: REPORT(JSMSG_REGEXP_TOO_COMPLEX
);
231 case 12: REPORT(JSMSG_UNMATCHED_RIGHT_PAREN
);
232 case 13: REPORT(JSMSG_REGEXP_TOO_COMPLEX
);
233 case 14: REPORT(JSMSG_MISSING_PAREN
);
234 case 15: REPORT(JSMSG_BAD_BACKREF
);
235 case 16: REPORT(JSMSG_REGEXP_TOO_COMPLEX
);
236 case 17: REPORT(JSMSG_REGEXP_TOO_COMPLEX
);
238 JS_NOT_REACHED("Precondition violation: unknown PCRE error code.");
244 RegExp::parseFlags(JSContext
*cx
, JSString
*flagStr
, uint32
&flagsOut
)
248 flagStr
->getCharsAndLength(s
, n
);
250 for (size_t i
= 0; i
< n
; i
++) {
251 #define HANDLE_FLAG(__name) \
253 if (flagsOut & (__name)) \
255 flagsOut |= (__name); \
258 case 'i': HANDLE_FLAG(JSREG_FOLD
); break;
259 case 'g': HANDLE_FLAG(JSREG_GLOB
); break;
260 case 'm': HANDLE_FLAG(JSREG_MULTILINE
); break;
261 case 'y': HANDLE_FLAG(JSREG_STICKY
); break;
266 charBuf
[0] = char(s
[i
]);
268 JS_ReportErrorFlagsAndNumber(cx
, JSREPORT_ERROR
, js_GetErrorMessage
, NULL
,
269 JSMSG_BAD_REGEXP_FLAG
, charBuf
);
279 RegExp::createFlagged(JSContext
*cx
, JSString
*str
, JSString
*opt
)
282 return create(cx
, str
, 0);
284 if (!parseFlags(cx
, opt
, flags
))
286 return create(cx
, str
, flags
);
290 * RegExp instance properties.
292 #define DEFINE_GETTER(name, code) \
294 name(JSContext *cx, JSObject *obj, jsid id, Value *vp) \
296 while (obj->getClass() != &js_RegExpClass) { \
297 obj = obj->getProto(); \
301 RegExp *re = RegExp::extractFrom(obj); \
306 /* lastIndex is stored in the object, re = re silences the compiler warning. */
307 DEFINE_GETTER(lastIndex_getter
, re
= re
; *vp
= obj
->getRegExpLastIndex())
308 DEFINE_GETTER(source_getter
, *vp
= StringValue(re
->getSource()))
309 DEFINE_GETTER(global_getter
, *vp
= BooleanValue(re
->global()))
310 DEFINE_GETTER(ignoreCase_getter
, *vp
= BooleanValue(re
->ignoreCase()))
311 DEFINE_GETTER(multiline_getter
, *vp
= BooleanValue(re
->multiline()))
312 DEFINE_GETTER(sticky_getter
, *vp
= BooleanValue(re
->sticky()))
315 lastIndex_setter(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
317 while (obj
->getClass() != &js_RegExpClass
) {
318 obj
= obj
->getProto();
322 obj
->setRegExpLastIndex(*vp
);
326 static const struct LazyProp
{
330 } lazyRegExpProps
[] = {
331 { js_source_str
, ATOM_OFFSET(source
), source_getter
},
332 { js_global_str
, ATOM_OFFSET(global
), global_getter
},
333 { js_ignoreCase_str
, ATOM_OFFSET(ignoreCase
), ignoreCase_getter
},
334 { js_multiline_str
, ATOM_OFFSET(multiline
), multiline_getter
},
335 { js_sticky_str
, ATOM_OFFSET(sticky
), sticky_getter
}
339 regexp_resolve(JSContext
*cx
, JSObject
*obj
, jsid id
, uint32 flags
, JSObject
**objp
)
341 JS_ASSERT(obj
->isRegExp());
343 if (!JSID_IS_ATOM(id
))
346 if (id
== ATOM_TO_JSID(cx
->runtime
->atomState
.lastIndexAtom
)) {
347 if (!js_DefineNativeProperty(cx
, obj
, id
, UndefinedValue(),
348 lastIndex_getter
, lastIndex_setter
,
349 JSPROP_PERMANENT
| JSPROP_SHARED
,
357 for (size_t i
= 0; i
< JS_ARRAY_LENGTH(lazyRegExpProps
); i
++) {
358 const LazyProp
&lazy
= lazyRegExpProps
[i
];
359 JSAtom
*atom
= OFFSET_TO_ATOM(cx
->runtime
, lazy
.atomOffset
);
360 if (id
== ATOM_TO_JSID(atom
)) {
361 if (!js_DefineNativeProperty(cx
, obj
, id
, UndefinedValue(),
363 JSPROP_PERMANENT
| JSPROP_SHARED
| JSPROP_READONLY
,
376 * RegExp static properties.
378 * RegExp class static properties and their Perl counterparts:
381 * RegExp.multiline $*
382 * RegExp.lastMatch $&
383 * RegExp.lastParen $+
384 * RegExp.leftContext $`
385 * RegExp.rightContext $'
388 #define DEFINE_STATIC_GETTER(name, code) \
390 name(JSContext *cx, JSObject *obj, jsid id, jsval *vp) \
392 RegExpStatics *res = cx->regExpStatics(); \
396 DEFINE_STATIC_GETTER(static_input_getter
, return res
->createPendingInput(cx
, Valueify(vp
)))
397 DEFINE_STATIC_GETTER(static_multiline_getter
, *vp
= BOOLEAN_TO_JSVAL(res
->multiline());
399 DEFINE_STATIC_GETTER(static_lastMatch_getter
, return res
->createLastMatch(cx
, Valueify(vp
)))
400 DEFINE_STATIC_GETTER(static_lastParen_getter
, return res
->createLastParen(cx
, Valueify(vp
)))
401 DEFINE_STATIC_GETTER(static_leftContext_getter
, return res
->createLeftContext(cx
, Valueify(vp
)))
402 DEFINE_STATIC_GETTER(static_rightContext_getter
, return res
->createRightContext(cx
, Valueify(vp
)))
404 DEFINE_STATIC_GETTER(static_paren1_getter
, return res
->createParen(cx
, 0, Valueify(vp
)))
405 DEFINE_STATIC_GETTER(static_paren2_getter
, return res
->createParen(cx
, 1, Valueify(vp
)))
406 DEFINE_STATIC_GETTER(static_paren3_getter
, return res
->createParen(cx
, 2, Valueify(vp
)))
407 DEFINE_STATIC_GETTER(static_paren4_getter
, return res
->createParen(cx
, 3, Valueify(vp
)))
408 DEFINE_STATIC_GETTER(static_paren5_getter
, return res
->createParen(cx
, 4, Valueify(vp
)))
409 DEFINE_STATIC_GETTER(static_paren6_getter
, return res
->createParen(cx
, 5, Valueify(vp
)))
410 DEFINE_STATIC_GETTER(static_paren7_getter
, return res
->createParen(cx
, 6, Valueify(vp
)))
411 DEFINE_STATIC_GETTER(static_paren8_getter
, return res
->createParen(cx
, 7, Valueify(vp
)))
412 DEFINE_STATIC_GETTER(static_paren9_getter
, return res
->createParen(cx
, 8, Valueify(vp
)))
414 #define DEFINE_STATIC_SETTER(name, code) \
416 name(JSContext *cx, JSObject *obj, jsid id, jsval *vp) \
418 RegExpStatics *res = cx->regExpStatics(); \
423 DEFINE_STATIC_SETTER(static_input_setter
,
424 if (!JSVAL_IS_STRING(*vp
) && !JS_ConvertValue(cx
, *vp
, JSTYPE_STRING
, vp
))
426 res
->setPendingInput(JSVAL_TO_STRING(*vp
)))
427 DEFINE_STATIC_SETTER(static_multiline_setter
,
428 if (!JSVAL_IS_BOOLEAN(*vp
) && !JS_ConvertValue(cx
, *vp
, JSTYPE_BOOLEAN
, vp
))
430 res
->setMultiline(!!JSVAL_TO_BOOLEAN(*vp
)))
432 const uint8 REGEXP_STATIC_PROP_ATTRS
= JSPROP_PERMANENT
| JSPROP_SHARED
| JSPROP_ENUMERATE
;
433 const uint8 RO_REGEXP_STATIC_PROP_ATTRS
= REGEXP_STATIC_PROP_ATTRS
| JSPROP_READONLY
;
435 static JSPropertySpec regexp_static_props
[] = {
436 {"input", 0, REGEXP_STATIC_PROP_ATTRS
, static_input_getter
, static_input_setter
},
437 {"multiline", 0, REGEXP_STATIC_PROP_ATTRS
, static_multiline_getter
,
438 static_multiline_setter
},
439 {"lastMatch", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_lastMatch_getter
, NULL
},
440 {"lastParen", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_lastParen_getter
, NULL
},
441 {"leftContext", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_leftContext_getter
, NULL
},
442 {"rightContext", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_rightContext_getter
, NULL
},
443 {"$1", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren1_getter
, NULL
},
444 {"$2", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren2_getter
, NULL
},
445 {"$3", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren3_getter
, NULL
},
446 {"$4", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren4_getter
, NULL
},
447 {"$5", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren5_getter
, NULL
},
448 {"$6", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren6_getter
, NULL
},
449 {"$7", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren7_getter
, NULL
},
450 {"$8", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren8_getter
, NULL
},
451 {"$9", 0, RO_REGEXP_STATIC_PROP_ATTRS
, static_paren9_getter
, NULL
},
456 regexp_finalize(JSContext
*cx
, JSObject
*obj
)
458 RegExp
*re
= RegExp::extractFrom(obj
);
464 /* Forward static prototype. */
466 regexp_exec_sub(JSContext
*cx
, JSObject
*obj
, uintN argc
, Value
*argv
, JSBool test
, Value
*rval
);
469 regexp_call(JSContext
*cx
, uintN argc
, Value
*vp
)
471 return regexp_exec_sub(cx
, &JS_CALLEE(cx
, vp
).toObject(), argc
, JS_ARGV(cx
, vp
), false, vp
);
476 #include "jsxdrapi.h"
479 js_XDRRegExpObject(JSXDRState
*xdr
, JSObject
**objp
)
481 JSString
*source
= 0;
482 uint32 flagsword
= 0;
484 if (xdr
->mode
== JSXDR_ENCODE
) {
486 RegExp
*re
= RegExp::extractFrom(*objp
);
489 source
= re
->getSource();
490 flagsword
= re
->getFlags();
492 if (!JS_XDRString(xdr
, &source
) || !JS_XDRUint32(xdr
, &flagsword
))
494 if (xdr
->mode
== JSXDR_DECODE
) {
495 JSObject
*obj
= NewBuiltinClassInstance(xdr
->cx
, &js_RegExpClass
);
500 RegExp
*re
= RegExp::create(xdr
->cx
, source
, flagsword
);
504 obj
->zeroRegExpLastIndex();
510 #else /* !JS_HAS_XDR */
512 #define js_XDRRegExpObject NULL
514 #endif /* !JS_HAS_XDR */
517 regexp_trace(JSTracer
*trc
, JSObject
*obj
)
519 RegExp
*re
= RegExp::extractFrom(obj
);
520 if (re
&& re
->getSource())
521 MarkString(trc
, re
->getSource(), "source");
525 regexp_enumerate(JSContext
*cx
, JSObject
*obj
)
527 JS_ASSERT(obj
->isRegExp());
530 if (!JS_LookupPropertyById(cx
, obj
, ATOM_TO_JSID(cx
->runtime
->atomState
.lastIndexAtom
), &v
))
533 for (size_t i
= 0; i
< JS_ARRAY_LENGTH(lazyRegExpProps
); i
++) {
534 const LazyProp
&lazy
= lazyRegExpProps
[i
];
535 jsid id
= ATOM_TO_JSID(OFFSET_TO_ATOM(cx
->runtime
, lazy
.atomOffset
));
536 if (!JS_LookupPropertyById(cx
, obj
, id
, &v
))
543 js::Class js_RegExpClass
= {
545 JSCLASS_HAS_PRIVATE
| JSCLASS_NEW_RESOLVE
|
546 JSCLASS_HAS_RESERVED_SLOTS(JSObject::REGEXP_CLASS_RESERVED_SLOTS
) |
547 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp
),
548 PropertyStub
, /* addProperty */
549 PropertyStub
, /* delProperty */
550 PropertyStub
, /* getProperty */
551 PropertyStub
, /* setProperty */
553 reinterpret_cast<JSResolveOp
>(regexp_resolve
),
556 NULL
, /* reserved0 */
557 NULL
, /* checkAccess */
559 NULL
, /* construct */
561 NULL
, /* hasInstance */
562 JS_CLASS_TRACE(regexp_trace
)
566 * RegExp instance methods.
570 js_regexp_toString(JSContext
*cx
, JSObject
*obj
, Value
*vp
)
572 static const jschar empty_regexp_ucstr
[] = {'(', '?', ':', ')', 0};
573 if (!InstanceOf(cx
, obj
, &js_RegExpClass
, vp
+ 2))
575 RegExp
*re
= RegExp::extractFrom(obj
);
577 *vp
= StringValue(cx
->runtime
->emptyString
);
581 const jschar
*source
;
583 re
->getSource()->getCharsAndLength(source
, length
);
585 source
= empty_regexp_ucstr
;
586 length
= JS_ARRAY_LENGTH(empty_regexp_ucstr
) - 1;
589 uint32 nflags
= re
->flagCount();
590 jschar
*chars
= (jschar
*) cx
->malloc((length
+ nflags
+ 1) * sizeof(jschar
));
596 js_strncpy(&chars
[1], source
, length
- 2);
597 chars
[length
- 1] = '/';
600 chars
[length
++] = 'g';
601 if (re
->ignoreCase())
602 chars
[length
++] = 'i';
604 chars
[length
++] = 'm';
606 chars
[length
++] = 'y';
610 JSString
*str
= js_NewString(cx
, chars
, length
);
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 const jschar
*oldChars
;
637 unescaped
->getCharsAndLength(oldChars
, oldLen
);
638 js::Vector
<jschar
, 128> newChars(cx
);
639 for (const jschar
*it
= oldChars
; it
< oldChars
+ oldLen
; ++it
) {
640 if (*it
== '/' && (it
== oldChars
|| it
[-1] != '\\')) {
641 if (!newChars
.length()) {
642 if (!newChars
.reserve(oldLen
+ 1))
644 newChars
.append(oldChars
, size_t(it
- oldChars
));
646 newChars
.append('\\');
649 if (newChars
.length())
650 newChars
.append(*it
);
653 if (newChars
.length()) {
654 size_t len
= newChars
.length();
655 jschar
*chars
= newChars
.extractRawBuffer();
658 JSString
*escaped
= js_NewString(cx
, chars
, len
);
667 regexp_compile_sub_tail(JSContext
*cx
, JSObject
*obj
, Value
*rval
, JSString
*str
, uint32 flags
= 0)
669 flags
|= cx
->regExpStatics()->getFlags();
670 RegExp
*re
= RegExp::create(cx
, str
, flags
);
673 SwapObjectRegExp(cx
, obj
, *re
);
674 *rval
= ObjectValue(*obj
);
679 regexp_compile_sub(JSContext
*cx
, JSObject
*obj
, uintN argc
, Value
*argv
, Value
*rval
)
681 if (!InstanceOf(cx
, obj
, &js_RegExpClass
, argv
))
685 return regexp_compile_sub_tail(cx
, obj
, rval
, cx
->runtime
->emptyString
);
687 Value sourceValue
= argv
[0];
688 if (sourceValue
.isObject() && sourceValue
.toObject().getClass() == &js_RegExpClass
) {
690 * If we get passed in a RegExp object we construct a new
691 * RegExp that is a duplicate of it by re-compiling the
692 * original source code. ECMA requires that it be an error
693 * here if the flags are specified. (We must use the flags
694 * from the original RegExp also).
696 JSObject
&sourceObj
= sourceValue
.toObject();
697 if (argc
>= 2 && !argv
[1].isUndefined()) {
698 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_NEWREGEXP_FLAGGED
);
703 RegExp
*re
= RegExp::extractFrom(&sourceObj
);
706 clone
= RegExp::clone(cx
, *re
);
710 SwapObjectRegExp(cx
, obj
, *clone
);
711 *rval
= ObjectValue(*obj
);
715 /* Coerce to string and compile. */
716 JSString
*sourceStr
= js_ValueToString(cx
, sourceValue
);
719 argv
[0] = StringValue(sourceStr
);
721 if (argc
> 1 && !argv
[1].isUndefined()) {
722 JSString
*flagStr
= js_ValueToString(cx
, argv
[1]);
725 argv
[1] = StringValue(flagStr
);
726 if (!RegExp::parseFlags(cx
, flagStr
, flags
))
730 JSString
*escapedSourceStr
= EscapeNakedForwardSlashes(cx
, sourceStr
);
731 if (!escapedSourceStr
)
733 argv
[0] = StringValue(escapedSourceStr
);
735 return regexp_compile_sub_tail(cx
, obj
, rval
, escapedSourceStr
, flags
);
739 regexp_compile(JSContext
*cx
, uintN argc
, Value
*vp
)
741 JSObject
*obj
= JS_THIS_OBJECT(cx
, Jsvalify(vp
));
742 return obj
&& regexp_compile_sub(cx
, obj
, argc
, vp
+ 2, vp
);
746 regexp_exec_sub(JSContext
*cx
, JSObject
*obj
, uintN argc
, Value
*argv
, JSBool test
, Value
*rval
)
748 bool ok
= InstanceOf(cx
, obj
, &js_RegExpClass
, argv
);
752 RegExp
*re
= RegExp::extractFrom(obj
);
756 /* NB: we must reach out: after this paragraph, in order to drop re. */
759 if (re
->global() || re
->sticky()) {
760 const Value v
= obj
->getRegExpLastIndex();
762 lastIndex
= v
.toInt32();
765 lastIndex
= v
.toDouble();
766 else if (!ValueToNumber(cx
, v
, &lastIndex
))
768 lastIndex
= js_DoubleToInteger(lastIndex
);
774 /* Now that obj is unlocked, it's safe to (potentially) grab the GC lock. */
775 RegExpStatics
*res
= cx
->regExpStatics();
778 str
= js_ValueToString(cx
, argv
[0]);
783 argv
[0] = StringValue(str
);
785 /* Need to grab input from statics. */
786 str
= res
->getPendingInput();
788 const char *sourceBytes
= js_GetStringBytes(cx
, re
->getSource());
790 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_NO_INPUT
, sourceBytes
,
791 re
->global() ? "g" : "",
792 re
->ignoreCase() ? "i" : "",
793 re
->multiline() ? "m" : "",
794 re
->sticky() ? "y" : "");
801 if (lastIndex
< 0 || str
->length() < lastIndex
) {
802 obj
->zeroRegExpLastIndex();
805 size_t lastIndexInt
= (size_t) lastIndex
;
806 ok
= re
->execute(cx
, res
, str
, &lastIndexInt
, !!test
, rval
);
807 if (ok
&& (re
->global() || (!rval
->isNull() && re
->sticky()))) {
809 obj
->zeroRegExpLastIndex();
811 obj
->setRegExpLastIndex(lastIndexInt
);
821 js_regexp_exec(JSContext
*cx
, uintN argc
, Value
*vp
)
823 return regexp_exec_sub(cx
, JS_THIS_OBJECT(cx
, Jsvalify(vp
)), argc
, vp
+ 2, JS_FALSE
, vp
);
827 js_regexp_test(JSContext
*cx
, uintN argc
, Value
*vp
)
829 if (!regexp_exec_sub(cx
, JS_THIS_OBJECT(cx
, Jsvalify(vp
)), argc
, vp
+ 2, JS_TRUE
, vp
))
832 vp
->setBoolean(false);
836 static JSFunctionSpec regexp_methods
[] = {
838 JS_FN(js_toSource_str
, regexp_toString
, 0,0),
840 JS_FN(js_toString_str
, regexp_toString
, 0,0),
841 JS_FN("compile", regexp_compile
, 2,0),
842 JS_FN("exec", js_regexp_exec
, 1,0),
843 JS_FN("test", js_regexp_test
, 1,0),
848 regexp_construct(JSContext
*cx
, uintN argc
, Value
*vp
)
850 Value
*argv
= JS_ARGV(cx
, vp
);
851 if (!IsConstructing(vp
)) {
853 * If first arg is regexp and no flags are given, just return the arg.
854 * (regexp_compile_sub detects the regexp + flags case and throws a
855 * TypeError.) See 15.10.3.1.
857 if (argc
>= 1 && argv
[0].isObject() && argv
[0].toObject().isRegExp() &&
858 (argc
== 1 || argv
[1].isUndefined()))
865 /* Otherwise, replace obj with a new RegExp object. */
866 JSObject
*obj
= NewBuiltinClassInstance(cx
, &js_RegExpClass
);
870 return regexp_compile_sub(cx
, obj
, argc
, argv
, vp
);
873 /* Similar to regexp_compile_sub_tail. */
875 InitRegExpClassCompile(JSContext
*cx
, JSObject
*obj
)
877 RegExp
*re
= RegExp::create(cx
, cx
->runtime
->emptyString
, 0);
880 SwapObjectRegExp(cx
, obj
, *re
);
885 js_InitRegExpClass(JSContext
*cx
, JSObject
*obj
)
887 JSObject
*proto
= js_InitClass(cx
, obj
, NULL
, &js_RegExpClass
, regexp_construct
, 1,
888 NULL
, regexp_methods
, regexp_static_props
, NULL
);
892 JSObject
*ctor
= JS_GetConstructor(cx
, proto
);
896 /* Give RegExp.prototype private data so it matches the empty string. */
897 if (!JS_AliasProperty(cx
, ctor
, "input", "$_") ||
898 !JS_AliasProperty(cx
, ctor
, "multiline", "$*") ||
899 !JS_AliasProperty(cx
, ctor
, "lastMatch", "$&") ||
900 !JS_AliasProperty(cx
, ctor
, "lastParen", "$+") ||
901 !JS_AliasProperty(cx
, ctor
, "leftContext", "$`") ||
902 !JS_AliasProperty(cx
, ctor
, "rightContext", "$'") ||
903 !InitRegExpClassCompile(cx
, proto
)) {