js/src/builtin/RegExp.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:86ec51e5fd1e
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "builtin/RegExp.h"
8
9 #include "jscntxt.h"
10
11 #include "vm/RegExpStatics.h"
12 #include "vm/StringBuffer.h"
13
14 #include "jsobjinlines.h"
15
16 using namespace js;
17 using namespace js::types;
18
19 using mozilla::ArrayLength;
20
21 bool
22 js::CreateRegExpMatchResult(JSContext *cx, HandleString input, const MatchPairs &matches,
23 MutableHandleValue rval)
24 {
25 JS_ASSERT(input);
26
27 /*
28 * Create the (slow) result array for a match.
29 *
30 * Array contents:
31 * 0: matched string
32 * 1..pairCount-1: paren matches
33 * input: input string
34 * index: start index for the match
35 */
36
37 /* Get the templateObject that defines the shape and type of the output object */
38 JSObject *templateObject = cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx);
39 if (!templateObject)
40 return false;
41
42 size_t numPairs = matches.length();
43 JS_ASSERT(numPairs > 0);
44
45 RootedObject arr(cx, NewDenseAllocatedArrayWithTemplate(cx, numPairs, templateObject));
46 if (!arr)
47 return false;
48
49 /* Store a Value for each pair. */
50 for (size_t i = 0; i < numPairs; i++) {
51 const MatchPair &pair = matches[i];
52
53 if (pair.isUndefined()) {
54 JS_ASSERT(i != 0); /* Since we had a match, first pair must be present. */
55 arr->setDenseInitializedLength(i + 1);
56 arr->initDenseElement(i, UndefinedValue());
57 } else {
58 JSLinearString *str = js_NewDependentString(cx, input, pair.start, pair.length());
59 if (!str)
60 return false;
61 arr->setDenseInitializedLength(i + 1);
62 arr->initDenseElement(i, StringValue(str));
63 }
64 }
65
66 /* Set the |index| property. (TemplateObject positions it in slot 0) */
67 arr->nativeSetSlot(0, Int32Value(matches[0].start));
68
69 /* Set the |input| property. (TemplateObject positions it in slot 1) */
70 arr->nativeSetSlot(1, StringValue(input));
71
72 #ifdef DEBUG
73 RootedValue test(cx);
74 RootedId id(cx, NameToId(cx->names().index));
75 if (!baseops::GetProperty(cx, arr, id, &test))
76 return false;
77 JS_ASSERT(test == arr->nativeGetSlot(0));
78 id = NameToId(cx->names().input);
79 if (!baseops::GetProperty(cx, arr, id, &test))
80 return false;
81 JS_ASSERT(test == arr->nativeGetSlot(1));
82 #endif
83
84 rval.setObject(*arr);
85 return true;
86 }
87
88 static RegExpRunStatus
89 ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, RegExpShared &re,
90 Handle<JSLinearString*> input, const jschar *chars, size_t length,
91 size_t *lastIndex, MatchConduit &matches)
92 {
93 RegExpRunStatus status;
94
95 /* Switch between MatchOnly and IncludeSubpatterns modes. */
96 if (matches.isPair) {
97 size_t lastIndex_orig = *lastIndex;
98 /* Only one MatchPair slot provided: execute short-circuiting regexp. */
99 status = re.executeMatchOnly(cx, chars, length, lastIndex, *matches.u.pair);
100 if (status == RegExpRunStatus_Success && res)
101 res->updateLazily(cx, input, &re, lastIndex_orig);
102 } else {
103 /* Vector of MatchPairs provided: execute full regexp. */
104 status = re.execute(cx, chars, length, lastIndex, *matches.u.pairs);
105 if (status == RegExpRunStatus_Success && res) {
106 if (!res->updateFromMatchPairs(cx, input, *matches.u.pairs))
107 return RegExpRunStatus_Error;
108 }
109 }
110
111 return status;
112 }
113
114 /* Legacy ExecuteRegExp behavior is baked into the JSAPI. */
115 bool
116 js::ExecuteRegExpLegacy(JSContext *cx, RegExpStatics *res, RegExpObject &reobj,
117 Handle<JSLinearString*> input_, const jschar *chars, size_t length,
118 size_t *lastIndex, bool test, MutableHandleValue rval)
119 {
120 RegExpGuard shared(cx);
121 if (!reobj.getShared(cx, &shared))
122 return false;
123
124 ScopedMatchPairs matches(&cx->tempLifoAlloc());
125 MatchConduit conduit(&matches);
126
127 RegExpRunStatus status =
128 ExecuteRegExpImpl(cx, res, *shared, input_, chars, length, lastIndex, conduit);
129
130 if (status == RegExpRunStatus_Error)
131 return false;
132
133 if (status == RegExpRunStatus_Success_NotFound) {
134 /* ExecuteRegExp() previously returned an array or null. */
135 rval.setNull();
136 return true;
137 }
138
139 if (test) {
140 /* Forbid an array, as an optimization. */
141 rval.setBoolean(true);
142 return true;
143 }
144
145 RootedString input(cx, input_);
146 if (!input) {
147 input = js_NewStringCopyN<CanGC>(cx, chars, length);
148 if (!input)
149 return false;
150 }
151
152 return CreateRegExpMatchResult(cx, input, matches, rval);
153 }
154
155 /* Note: returns the original if no escaping need be performed. */
156 static JSAtom *
157 EscapeNakedForwardSlashes(JSContext *cx, HandleAtom unescaped)
158 {
159 size_t oldLen = unescaped->length();
160 const jschar *oldChars = unescaped->chars();
161
162 JS::Anchor<JSString *> anchor(unescaped);
163
164 /* We may never need to use |sb|. Start using it lazily. */
165 StringBuffer sb(cx);
166
167 for (const jschar *it = oldChars; it < oldChars + oldLen; ++it) {
168 if (*it == '/' && (it == oldChars || it[-1] != '\\')) {
169 /* There's a forward slash that needs escaping. */
170 if (sb.empty()) {
171 /* This is the first one we've seen, copy everything up to this point. */
172 if (!sb.reserve(oldLen + 1))
173 return nullptr;
174 sb.infallibleAppend(oldChars, size_t(it - oldChars));
175 }
176 if (!sb.append('\\'))
177 return nullptr;
178 }
179
180 if (!sb.empty() && !sb.append(*it))
181 return nullptr;
182 }
183
184 return sb.empty() ? (JSAtom *)unescaped : sb.finishAtom();
185 }
186
187 /*
188 * Compile a new |RegExpShared| for the |RegExpObject|.
189 *
190 * Per ECMAv5 15.10.4.1, we act on combinations of (pattern, flags) as
191 * arguments:
192 *
193 * RegExp, undefined => flags := pattern.flags
194 * RegExp, _ => throw TypeError
195 * _ => pattern := ToString(pattern) if defined(pattern) else ''
196 * flags := ToString(flags) if defined(flags) else ''
197 */
198 static bool
199 CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder, CallArgs args)
200 {
201 if (args.length() == 0) {
202 RegExpStatics *res = cx->global()->getRegExpStatics();
203 Rooted<JSAtom*> empty(cx, cx->runtime()->emptyString);
204 RegExpObject *reobj = builder.build(empty, res->getFlags());
205 if (!reobj)
206 return false;
207 args.rval().setObject(*reobj);
208 return true;
209 }
210
211 RootedValue sourceValue(cx, args[0]);
212
213 /*
214 * If we get passed in an object whose internal [[Class]] property is
215 * "RegExp", return a new object with the same source/flags.
216 */
217 if (IsObjectWithClass(sourceValue, ESClass_RegExp, cx)) {
218 /*
219 * Beware, sourceObj may be a (transparent) proxy to a RegExp, so only
220 * use generic (proxyable) operations on sourceObj that do not assume
221 * sourceObj.is<RegExpObject>().
222 */
223 RootedObject sourceObj(cx, &sourceValue.toObject());
224
225 if (args.hasDefined(1)) {
226 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NEWREGEXP_FLAGGED);
227 return false;
228 }
229
230 /*
231 * Only extract the 'flags' out of sourceObj; do not reuse the
232 * RegExpShared since it may be from a different compartment.
233 */
234 RegExpFlag flags;
235 {
236 RegExpGuard g(cx);
237 if (!RegExpToShared(cx, sourceObj, &g))
238 return false;
239
240 flags = g->getFlags();
241 }
242
243 /*
244 * 'toSource' is a permanent read-only property, so this is equivalent
245 * to executing RegExpObject::getSource on the unwrapped object.
246 */
247 RootedValue v(cx);
248 if (!JSObject::getProperty(cx, sourceObj, sourceObj, cx->names().source, &v))
249 return false;
250
251 Rooted<JSAtom*> sourceAtom(cx, &v.toString()->asAtom());
252 RegExpObject *reobj = builder.build(sourceAtom, flags);
253 if (!reobj)
254 return false;
255
256 args.rval().setObject(*reobj);
257 return true;
258 }
259
260 RootedAtom source(cx);
261 if (sourceValue.isUndefined()) {
262 source = cx->runtime()->emptyString;
263 } else {
264 /* Coerce to string and compile. */
265 source = ToAtom<CanGC>(cx, sourceValue);
266 if (!source)
267 return false;
268 }
269
270 RegExpFlag flags = RegExpFlag(0);
271 if (args.hasDefined(1)) {
272 RootedString flagStr(cx, ToString<CanGC>(cx, args[1]));
273 if (!flagStr)
274 return false;
275 args[1].setString(flagStr);
276 if (!ParseRegExpFlags(cx, flagStr, &flags))
277 return false;
278 }
279
280 RootedAtom escapedSourceStr(cx, EscapeNakedForwardSlashes(cx, source));
281 if (!escapedSourceStr)
282 return false;
283
284 if (!js::RegExpShared::checkSyntax(cx, nullptr, escapedSourceStr))
285 return false;
286
287 RegExpStatics *res = cx->global()->getRegExpStatics();
288 RegExpObject *reobj = builder.build(escapedSourceStr, RegExpFlag(flags | res->getFlags()));
289 if (!reobj)
290 return false;
291
292 args.rval().setObject(*reobj);
293 return true;
294 }
295
296 MOZ_ALWAYS_INLINE bool
297 IsRegExp(HandleValue v)
298 {
299 return v.isObject() && v.toObject().is<RegExpObject>();
300 }
301
302 MOZ_ALWAYS_INLINE bool
303 regexp_compile_impl(JSContext *cx, CallArgs args)
304 {
305 JS_ASSERT(IsRegExp(args.thisv()));
306 RegExpObjectBuilder builder(cx, &args.thisv().toObject().as<RegExpObject>());
307 return CompileRegExpObject(cx, builder, args);
308 }
309
310 static bool
311 regexp_compile(JSContext *cx, unsigned argc, Value *vp)
312 {
313 CallArgs args = CallArgsFromVp(argc, vp);
314 return CallNonGenericMethod<IsRegExp, regexp_compile_impl>(cx, args);
315 }
316
317 static bool
318 regexp_construct(JSContext *cx, unsigned argc, Value *vp)
319 {
320 CallArgs args = CallArgsFromVp(argc, vp);
321
322 if (!args.isConstructing()) {
323 /*
324 * If first arg is regexp and no flags are given, just return the arg.
325 * Otherwise, delegate to the standard constructor.
326 * See ECMAv5 15.10.3.1.
327 */
328 if (args.hasDefined(0) &&
329 IsObjectWithClass(args[0], ESClass_RegExp, cx) &&
330 !args.hasDefined(1))
331 {
332 args.rval().set(args[0]);
333 return true;
334 }
335 }
336
337 RegExpObjectBuilder builder(cx);
338 return CompileRegExpObject(cx, builder, args);
339 }
340
341 MOZ_ALWAYS_INLINE bool
342 regexp_toString_impl(JSContext *cx, CallArgs args)
343 {
344 JS_ASSERT(IsRegExp(args.thisv()));
345
346 JSString *str = args.thisv().toObject().as<RegExpObject>().toString(cx);
347 if (!str)
348 return false;
349
350 args.rval().setString(str);
351 return true;
352 }
353
354 static bool
355 regexp_toString(JSContext *cx, unsigned argc, Value *vp)
356 {
357 CallArgs args = CallArgsFromVp(argc, vp);
358 return CallNonGenericMethod<IsRegExp, regexp_toString_impl>(cx, args);
359 }
360
361 static const JSFunctionSpec regexp_methods[] = {
362 #if JS_HAS_TOSOURCE
363 JS_FN(js_toSource_str, regexp_toString, 0,0),
364 #endif
365 JS_FN(js_toString_str, regexp_toString, 0,0),
366 JS_FN("compile", regexp_compile, 2,0),
367 JS_FN("exec", regexp_exec, 1,0),
368 JS_FN("test", regexp_test, 1,0),
369 JS_FS_END
370 };
371
372 /*
373 * RegExp static properties.
374 *
375 * RegExp class static properties and their Perl counterparts:
376 *
377 * RegExp.input $_
378 * RegExp.multiline $*
379 * RegExp.lastMatch $&
380 * RegExp.lastParen $+
381 * RegExp.leftContext $`
382 * RegExp.rightContext $'
383 */
384
385 #define DEFINE_STATIC_GETTER(name, code) \
386 static bool \
387 name(JSContext *cx, unsigned argc, Value *vp) \
388 { \
389 CallArgs args = CallArgsFromVp(argc, vp); \
390 RegExpStatics *res = cx->global()->getRegExpStatics(); \
391 code; \
392 }
393
394 DEFINE_STATIC_GETTER(static_input_getter, return res->createPendingInput(cx, args.rval()))
395 DEFINE_STATIC_GETTER(static_multiline_getter, args.rval().setBoolean(res->multiline());
396 return true)
397 DEFINE_STATIC_GETTER(static_lastMatch_getter, return res->createLastMatch(cx, args.rval()))
398 DEFINE_STATIC_GETTER(static_lastParen_getter, return res->createLastParen(cx, args.rval()))
399 DEFINE_STATIC_GETTER(static_leftContext_getter, return res->createLeftContext(cx, args.rval()))
400 DEFINE_STATIC_GETTER(static_rightContext_getter, return res->createRightContext(cx, args.rval()))
401
402 DEFINE_STATIC_GETTER(static_paren1_getter, return res->createParen(cx, 1, args.rval()))
403 DEFINE_STATIC_GETTER(static_paren2_getter, return res->createParen(cx, 2, args.rval()))
404 DEFINE_STATIC_GETTER(static_paren3_getter, return res->createParen(cx, 3, args.rval()))
405 DEFINE_STATIC_GETTER(static_paren4_getter, return res->createParen(cx, 4, args.rval()))
406 DEFINE_STATIC_GETTER(static_paren5_getter, return res->createParen(cx, 5, args.rval()))
407 DEFINE_STATIC_GETTER(static_paren6_getter, return res->createParen(cx, 6, args.rval()))
408 DEFINE_STATIC_GETTER(static_paren7_getter, return res->createParen(cx, 7, args.rval()))
409 DEFINE_STATIC_GETTER(static_paren8_getter, return res->createParen(cx, 8, args.rval()))
410 DEFINE_STATIC_GETTER(static_paren9_getter, return res->createParen(cx, 9, args.rval()))
411
412 #define DEFINE_STATIC_SETTER(name, code) \
413 static bool \
414 name(JSContext *cx, unsigned argc, Value *vp) \
415 { \
416 RegExpStatics *res = cx->global()->getRegExpStatics(); \
417 code; \
418 return true; \
419 }
420
421 static bool
422 static_input_setter(JSContext *cx, unsigned argc, Value *vp)
423 {
424 CallArgs args = CallArgsFromVp(argc, vp);
425 RegExpStatics *res = cx->global()->getRegExpStatics();
426
427 RootedString str(cx, ToString<CanGC>(cx, args.get(0)));
428 if (!str)
429 return false;
430
431 res->setPendingInput(str);
432 args.rval().setString(str);
433 return true;
434 }
435
436 static bool
437 static_multiline_setter(JSContext *cx, unsigned argc, Value *vp)
438 {
439 CallArgs args = CallArgsFromVp(argc, vp);
440 RegExpStatics *res = cx->global()->getRegExpStatics();
441
442 bool b = ToBoolean(args.get(0));
443 res->setMultiline(cx, b);
444 args.rval().setBoolean(b);
445 return true;
446 }
447
448 static const JSPropertySpec regexp_static_props[] = {
449 JS_PSGS("input", static_input_getter, static_input_setter,
450 JSPROP_PERMANENT | JSPROP_ENUMERATE),
451 JS_PSGS("multiline", static_multiline_getter, static_multiline_setter,
452 JSPROP_PERMANENT | JSPROP_ENUMERATE),
453 JS_PSG("lastMatch", static_lastMatch_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
454 JS_PSG("lastParen", static_lastParen_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
455 JS_PSG("leftContext", static_leftContext_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
456 JS_PSG("rightContext", static_rightContext_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
457 JS_PSG("$1", static_paren1_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
458 JS_PSG("$2", static_paren2_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
459 JS_PSG("$3", static_paren3_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
460 JS_PSG("$4", static_paren4_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
461 JS_PSG("$5", static_paren5_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
462 JS_PSG("$6", static_paren6_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
463 JS_PSG("$7", static_paren7_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
464 JS_PSG("$8", static_paren8_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
465 JS_PSG("$9", static_paren9_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
466 JS_PSGS("$_", static_input_getter, static_input_setter, JSPROP_PERMANENT),
467 JS_PSGS("$*", static_multiline_getter, static_multiline_setter, JSPROP_PERMANENT),
468 JS_PSG("$&", static_lastMatch_getter, JSPROP_PERMANENT),
469 JS_PSG("$+", static_lastParen_getter, JSPROP_PERMANENT),
470 JS_PSG("$`", static_leftContext_getter, JSPROP_PERMANENT),
471 JS_PSG("$'", static_rightContext_getter, JSPROP_PERMANENT),
472 JS_PS_END
473 };
474
475 JSObject *
476 js_InitRegExpClass(JSContext *cx, HandleObject obj)
477 {
478 JS_ASSERT(obj->isNative());
479
480 Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
481
482 RootedObject proto(cx, global->createBlankPrototype(cx, &RegExpObject::class_));
483 if (!proto)
484 return nullptr;
485 proto->setPrivate(nullptr);
486
487 HandlePropertyName empty = cx->names().empty;
488 RegExpObjectBuilder builder(cx, &proto->as<RegExpObject>());
489 if (!builder.build(empty, RegExpFlag(0)))
490 return nullptr;
491
492 if (!DefinePropertiesAndBrand(cx, proto, nullptr, regexp_methods))
493 return nullptr;
494
495 RootedFunction ctor(cx);
496 ctor = global->createConstructor(cx, regexp_construct, cx->names().RegExp, 2);
497 if (!ctor)
498 return nullptr;
499
500 if (!LinkConstructorAndPrototype(cx, ctor, proto))
501 return nullptr;
502
503 /* Add static properties to the RegExp constructor. */
504 if (!JS_DefineProperties(cx, ctor, regexp_static_props))
505 return nullptr;
506
507 if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_RegExp, ctor, proto))
508 return nullptr;
509
510 return proto;
511 }
512
513 RegExpRunStatus
514 js::ExecuteRegExp(JSContext *cx, HandleObject regexp, HandleString string,
515 MatchConduit &matches, RegExpStaticsUpdate staticsUpdate)
516 {
517 /* Step 1 (b) was performed by CallNonGenericMethod. */
518 Rooted<RegExpObject*> reobj(cx, &regexp->as<RegExpObject>());
519
520 RegExpGuard re(cx);
521 if (!reobj->getShared(cx, &re))
522 return RegExpRunStatus_Error;
523
524 RegExpStatics *res = (staticsUpdate == UpdateRegExpStatics)
525 ? cx->global()->getRegExpStatics()
526 : nullptr;
527
528 /* Step 3. */
529 Rooted<JSLinearString*> input(cx, string->ensureLinear(cx));
530 if (!input)
531 return RegExpRunStatus_Error;
532
533 /* Step 4. */
534 RootedValue lastIndex(cx, reobj->getLastIndex());
535 size_t length = input->length();
536
537 /* Step 5. */
538 int i;
539 if (lastIndex.isInt32()) {
540 /* Aggressively avoid doubles. */
541 i = lastIndex.toInt32();
542 } else {
543 double d;
544 if (!ToInteger(cx, lastIndex, &d))
545 return RegExpRunStatus_Error;
546
547 /* Inlined steps 6, 7, 9a with doubles to detect failure case. */
548 if ((re->global() || re->sticky()) && (d < 0 || d > length)) {
549 reobj->zeroLastIndex();
550 return RegExpRunStatus_Success_NotFound;
551 }
552
553 i = int(d);
554 }
555
556 /* Steps 6-7 (with sticky extension). */
557 if (!re->global() && !re->sticky())
558 i = 0;
559
560 /* Step 9a. */
561 if (i < 0 || size_t(i) > length) {
562 reobj->zeroLastIndex();
563 return RegExpRunStatus_Success_NotFound;
564 }
565
566 /* Steps 8-21. */
567 const jschar *chars = input->chars();
568 size_t lastIndexInt(i);
569 RegExpRunStatus status =
570 ExecuteRegExpImpl(cx, res, *re, input, chars, length, &lastIndexInt, matches);
571
572 if (status == RegExpRunStatus_Error)
573 return RegExpRunStatus_Error;
574
575 /* Steps 9a and 11 (with sticky extension). */
576 if (status == RegExpRunStatus_Success_NotFound)
577 reobj->zeroLastIndex();
578 else if (re->global() || re->sticky())
579 reobj->setLastIndex(lastIndexInt);
580
581 return status;
582 }
583
584 /* ES5 15.10.6.2 (and 15.10.6.3, which calls 15.10.6.2). */
585 static RegExpRunStatus
586 ExecuteRegExp(JSContext *cx, CallArgs args, MatchConduit &matches)
587 {
588 /* Step 1 (a) was performed by CallNonGenericMethod. */
589 RootedObject regexp(cx, &args.thisv().toObject());
590
591 /* Step 2. */
592 RootedString string(cx, ToString<CanGC>(cx, args.get(0)));
593 if (!string)
594 return RegExpRunStatus_Error;
595
596 return ExecuteRegExp(cx, regexp, string, matches, UpdateRegExpStatics);
597 }
598
599 /* ES5 15.10.6.2. */
600 static bool
601 regexp_exec_impl(JSContext *cx, HandleObject regexp, HandleString string,
602 RegExpStaticsUpdate staticsUpdate, MutableHandleValue rval)
603 {
604 /* Execute regular expression and gather matches. */
605 ScopedMatchPairs matches(&cx->tempLifoAlloc());
606 MatchConduit conduit(&matches);
607
608 RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, conduit, staticsUpdate);
609
610 if (status == RegExpRunStatus_Error)
611 return false;
612
613 if (status == RegExpRunStatus_Success_NotFound) {
614 rval.setNull();
615 return true;
616 }
617
618 return CreateRegExpMatchResult(cx, string, matches, rval);
619 }
620
621 static bool
622 regexp_exec_impl(JSContext *cx, CallArgs args)
623 {
624 RootedObject regexp(cx, &args.thisv().toObject());
625 RootedString string(cx, ToString<CanGC>(cx, args.get(0)));
626 if (!string)
627 return false;
628
629 return regexp_exec_impl(cx, regexp, string, UpdateRegExpStatics, args.rval());
630 }
631
632 bool
633 js::regexp_exec(JSContext *cx, unsigned argc, Value *vp)
634 {
635 CallArgs args = CallArgsFromVp(argc, vp);
636 return CallNonGenericMethod(cx, IsRegExp, regexp_exec_impl, args);
637 }
638
639 /* Separate interface for use by IonMonkey. */
640 bool
641 js::regexp_exec_raw(JSContext *cx, HandleObject regexp, HandleString input, MutableHandleValue output)
642 {
643 return regexp_exec_impl(cx, regexp, input, UpdateRegExpStatics, output);
644 }
645
646 bool
647 js::regexp_exec_no_statics(JSContext *cx, unsigned argc, Value *vp)
648 {
649 CallArgs args = CallArgsFromVp(argc, vp);
650 JS_ASSERT(args.length() == 2);
651 JS_ASSERT(IsRegExp(args[0]));
652 JS_ASSERT(args[1].isString());
653
654 RootedObject regexp(cx, &args[0].toObject());
655 RootedString string(cx, args[1].toString());
656
657 return regexp_exec_impl(cx, regexp, string, DontUpdateRegExpStatics, args.rval());
658 }
659
660 /* ES5 15.10.6.3. */
661 static bool
662 regexp_test_impl(JSContext *cx, CallArgs args)
663 {
664 MatchPair match;
665 MatchConduit conduit(&match);
666 RegExpRunStatus status = ExecuteRegExp(cx, args, conduit);
667 args.rval().setBoolean(status == RegExpRunStatus_Success);
668 return status != RegExpRunStatus_Error;
669 }
670
671 /* Separate interface for use by IonMonkey. */
672 bool
673 js::regexp_test_raw(JSContext *cx, HandleObject regexp, HandleString input, bool *result)
674 {
675 MatchPair match;
676 MatchConduit conduit(&match);
677 RegExpRunStatus status = ExecuteRegExp(cx, regexp, input, conduit, UpdateRegExpStatics);
678 *result = (status == RegExpRunStatus_Success);
679 return status != RegExpRunStatus_Error;
680 }
681
682 bool
683 js::regexp_test(JSContext *cx, unsigned argc, Value *vp)
684 {
685 CallArgs args = CallArgsFromVp(argc, vp);
686 return CallNonGenericMethod(cx, IsRegExp, regexp_test_impl, args);
687 }
688
689 bool
690 js::regexp_test_no_statics(JSContext *cx, unsigned argc, Value *vp)
691 {
692 CallArgs args = CallArgsFromVp(argc, vp);
693 JS_ASSERT(args.length() == 2);
694 JS_ASSERT(IsRegExp(args[0]));
695 JS_ASSERT(args[1].isString());
696
697 RootedObject regexp(cx, &args[0].toObject());
698 RootedString string(cx, args[1].toString());
699
700 MatchPair match;
701 MatchConduit conduit(&match);
702 RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, conduit, DontUpdateRegExpStatics);
703 args.rval().setBoolean(status == RegExpRunStatus_Success);
704 return status != RegExpRunStatus_Error;
705 }

mercurial