js/src/builtin/RegExp.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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/. */
     7 #include "builtin/RegExp.h"
     9 #include "jscntxt.h"
    11 #include "vm/RegExpStatics.h"
    12 #include "vm/StringBuffer.h"
    14 #include "jsobjinlines.h"
    16 using namespace js;
    17 using namespace js::types;
    19 using mozilla::ArrayLength;
    21 bool
    22 js::CreateRegExpMatchResult(JSContext *cx, HandleString input, const MatchPairs &matches,
    23                             MutableHandleValue rval)
    24 {
    25     JS_ASSERT(input);
    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      */
    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;
    42     size_t numPairs = matches.length();
    43     JS_ASSERT(numPairs > 0);
    45     RootedObject arr(cx, NewDenseAllocatedArrayWithTemplate(cx, numPairs, templateObject));
    46     if (!arr)
    47         return false;
    49     /* Store a Value for each pair. */
    50     for (size_t i = 0; i < numPairs; i++) {
    51         const MatchPair &pair = matches[i];
    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     }
    66     /* Set the |index| property. (TemplateObject positions it in slot 0) */
    67     arr->nativeSetSlot(0, Int32Value(matches[0].start));
    69     /* Set the |input| property. (TemplateObject positions it in slot 1) */
    70     arr->nativeSetSlot(1, StringValue(input));
    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
    84     rval.setObject(*arr);
    85     return true;
    86 }
    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;
    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     }
   111     return status;
   112 }
   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;
   124     ScopedMatchPairs matches(&cx->tempLifoAlloc());
   125     MatchConduit conduit(&matches);
   127     RegExpRunStatus status =
   128         ExecuteRegExpImpl(cx, res, *shared, input_, chars, length, lastIndex, conduit);
   130     if (status == RegExpRunStatus_Error)
   131         return false;
   133     if (status == RegExpRunStatus_Success_NotFound) {
   134         /* ExecuteRegExp() previously returned an array or null. */
   135         rval.setNull();
   136         return true;
   137     }
   139     if (test) {
   140         /* Forbid an array, as an optimization. */
   141         rval.setBoolean(true);
   142         return true;
   143     }
   145     RootedString input(cx, input_);
   146     if (!input) {
   147         input = js_NewStringCopyN<CanGC>(cx, chars, length);
   148         if (!input)
   149             return false;
   150     }
   152     return CreateRegExpMatchResult(cx, input, matches, rval);
   153 }
   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();
   162     JS::Anchor<JSString *> anchor(unescaped);
   164     /* We may never need to use |sb|. Start using it lazily. */
   165     StringBuffer sb(cx);
   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         }
   180         if (!sb.empty() && !sb.append(*it))
   181             return nullptr;
   182     }
   184     return sb.empty() ? (JSAtom *)unescaped : sb.finishAtom();
   185 }
   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     }
   211     RootedValue sourceValue(cx, args[0]);
   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());
   225         if (args.hasDefined(1)) {
   226             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NEWREGEXP_FLAGGED);
   227             return false;
   228         }
   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;
   240             flags = g->getFlags();
   241         }
   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;
   251         Rooted<JSAtom*> sourceAtom(cx, &v.toString()->asAtom());
   252         RegExpObject *reobj = builder.build(sourceAtom, flags);
   253         if (!reobj)
   254             return false;
   256         args.rval().setObject(*reobj);
   257         return true;
   258     }
   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     }
   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     }
   280     RootedAtom escapedSourceStr(cx, EscapeNakedForwardSlashes(cx, source));
   281     if (!escapedSourceStr)
   282         return false;
   284     if (!js::RegExpShared::checkSyntax(cx, nullptr, escapedSourceStr))
   285         return false;
   287     RegExpStatics *res = cx->global()->getRegExpStatics();
   288     RegExpObject *reobj = builder.build(escapedSourceStr, RegExpFlag(flags | res->getFlags()));
   289     if (!reobj)
   290         return false;
   292     args.rval().setObject(*reobj);
   293     return true;
   294 }
   296 MOZ_ALWAYS_INLINE bool
   297 IsRegExp(HandleValue v)
   298 {
   299     return v.isObject() && v.toObject().is<RegExpObject>();
   300 }
   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 }
   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 }
   317 static bool
   318 regexp_construct(JSContext *cx, unsigned argc, Value *vp)
   319 {
   320     CallArgs args = CallArgsFromVp(argc, vp);
   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     }
   337     RegExpObjectBuilder builder(cx);
   338     return CompileRegExpObject(cx, builder, args);
   339 }
   341 MOZ_ALWAYS_INLINE bool
   342 regexp_toString_impl(JSContext *cx, CallArgs args)
   343 {
   344     JS_ASSERT(IsRegExp(args.thisv()));
   346     JSString *str = args.thisv().toObject().as<RegExpObject>().toString(cx);
   347     if (!str)
   348         return false;
   350     args.rval().setString(str);
   351     return true;
   352 }
   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 }
   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 };
   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  */
   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     }
   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()))
   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()))
   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     }
   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();
   427     RootedString str(cx, ToString<CanGC>(cx, args.get(0)));
   428     if (!str)
   429         return false;
   431     res->setPendingInput(str);
   432     args.rval().setString(str);
   433     return true;
   434 }
   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();
   442     bool b = ToBoolean(args.get(0));
   443     res->setMultiline(cx, b);
   444     args.rval().setBoolean(b);
   445     return true;
   446 }
   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 };
   475 JSObject *
   476 js_InitRegExpClass(JSContext *cx, HandleObject obj)
   477 {
   478     JS_ASSERT(obj->isNative());
   480     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
   482     RootedObject proto(cx, global->createBlankPrototype(cx, &RegExpObject::class_));
   483     if (!proto)
   484         return nullptr;
   485     proto->setPrivate(nullptr);
   487     HandlePropertyName empty = cx->names().empty;
   488     RegExpObjectBuilder builder(cx, &proto->as<RegExpObject>());
   489     if (!builder.build(empty, RegExpFlag(0)))
   490         return nullptr;
   492     if (!DefinePropertiesAndBrand(cx, proto, nullptr, regexp_methods))
   493         return nullptr;
   495     RootedFunction ctor(cx);
   496     ctor = global->createConstructor(cx, regexp_construct, cx->names().RegExp, 2);
   497     if (!ctor)
   498         return nullptr;
   500     if (!LinkConstructorAndPrototype(cx, ctor, proto))
   501         return nullptr;
   503     /* Add static properties to the RegExp constructor. */
   504     if (!JS_DefineProperties(cx, ctor, regexp_static_props))
   505         return nullptr;
   507     if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_RegExp, ctor, proto))
   508         return nullptr;
   510     return proto;
   511 }
   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>());
   520     RegExpGuard re(cx);
   521     if (!reobj->getShared(cx, &re))
   522         return RegExpRunStatus_Error;
   524     RegExpStatics *res = (staticsUpdate == UpdateRegExpStatics)
   525                          ? cx->global()->getRegExpStatics()
   526                          : nullptr;
   528     /* Step 3. */
   529     Rooted<JSLinearString*> input(cx, string->ensureLinear(cx));
   530     if (!input)
   531         return RegExpRunStatus_Error;
   533     /* Step 4. */
   534     RootedValue lastIndex(cx, reobj->getLastIndex());
   535     size_t length = input->length();
   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;
   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         }
   553         i = int(d);
   554     }
   556     /* Steps 6-7 (with sticky extension). */
   557     if (!re->global() && !re->sticky())
   558         i = 0;
   560     /* Step 9a. */
   561     if (i < 0 || size_t(i) > length) {
   562         reobj->zeroLastIndex();
   563         return RegExpRunStatus_Success_NotFound;
   564     }
   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);
   572     if (status == RegExpRunStatus_Error)
   573         return RegExpRunStatus_Error;
   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);
   581     return status;
   582 }
   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());
   591     /* Step 2. */
   592     RootedString string(cx, ToString<CanGC>(cx, args.get(0)));
   593     if (!string)
   594         return RegExpRunStatus_Error;
   596     return ExecuteRegExp(cx, regexp, string, matches, UpdateRegExpStatics);
   597 }
   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);
   608     RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, conduit, staticsUpdate);
   610     if (status == RegExpRunStatus_Error)
   611         return false;
   613     if (status == RegExpRunStatus_Success_NotFound) {
   614         rval.setNull();
   615         return true;
   616     }
   618     return CreateRegExpMatchResult(cx, string, matches, rval);
   619 }
   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;
   629     return regexp_exec_impl(cx, regexp, string, UpdateRegExpStatics, args.rval());
   630 }
   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 }
   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 }
   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());
   654     RootedObject regexp(cx, &args[0].toObject());
   655     RootedString string(cx, args[1].toString());
   657     return regexp_exec_impl(cx, regexp, string, DontUpdateRegExpStatics, args.rval());
   658 }
   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 }
   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 }
   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 }
   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());
   697     RootedObject regexp(cx, &args[0].toObject());
   698     RootedString string(cx, args[1].toString());
   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