michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "builtin/RegExp.h" michael@0: michael@0: #include "jscntxt.h" michael@0: michael@0: #include "vm/RegExpStatics.h" michael@0: #include "vm/StringBuffer.h" michael@0: michael@0: #include "jsobjinlines.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::types; michael@0: michael@0: using mozilla::ArrayLength; michael@0: michael@0: bool michael@0: js::CreateRegExpMatchResult(JSContext *cx, HandleString input, const MatchPairs &matches, michael@0: MutableHandleValue rval) michael@0: { michael@0: JS_ASSERT(input); michael@0: michael@0: /* michael@0: * Create the (slow) result array for a match. michael@0: * michael@0: * Array contents: michael@0: * 0: matched string michael@0: * 1..pairCount-1: paren matches michael@0: * input: input string michael@0: * index: start index for the match michael@0: */ michael@0: michael@0: /* Get the templateObject that defines the shape and type of the output object */ michael@0: JSObject *templateObject = cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx); michael@0: if (!templateObject) michael@0: return false; michael@0: michael@0: size_t numPairs = matches.length(); michael@0: JS_ASSERT(numPairs > 0); michael@0: michael@0: RootedObject arr(cx, NewDenseAllocatedArrayWithTemplate(cx, numPairs, templateObject)); michael@0: if (!arr) michael@0: return false; michael@0: michael@0: /* Store a Value for each pair. */ michael@0: for (size_t i = 0; i < numPairs; i++) { michael@0: const MatchPair &pair = matches[i]; michael@0: michael@0: if (pair.isUndefined()) { michael@0: JS_ASSERT(i != 0); /* Since we had a match, first pair must be present. */ michael@0: arr->setDenseInitializedLength(i + 1); michael@0: arr->initDenseElement(i, UndefinedValue()); michael@0: } else { michael@0: JSLinearString *str = js_NewDependentString(cx, input, pair.start, pair.length()); michael@0: if (!str) michael@0: return false; michael@0: arr->setDenseInitializedLength(i + 1); michael@0: arr->initDenseElement(i, StringValue(str)); michael@0: } michael@0: } michael@0: michael@0: /* Set the |index| property. (TemplateObject positions it in slot 0) */ michael@0: arr->nativeSetSlot(0, Int32Value(matches[0].start)); michael@0: michael@0: /* Set the |input| property. (TemplateObject positions it in slot 1) */ michael@0: arr->nativeSetSlot(1, StringValue(input)); michael@0: michael@0: #ifdef DEBUG michael@0: RootedValue test(cx); michael@0: RootedId id(cx, NameToId(cx->names().index)); michael@0: if (!baseops::GetProperty(cx, arr, id, &test)) michael@0: return false; michael@0: JS_ASSERT(test == arr->nativeGetSlot(0)); michael@0: id = NameToId(cx->names().input); michael@0: if (!baseops::GetProperty(cx, arr, id, &test)) michael@0: return false; michael@0: JS_ASSERT(test == arr->nativeGetSlot(1)); michael@0: #endif michael@0: michael@0: rval.setObject(*arr); michael@0: return true; michael@0: } michael@0: michael@0: static RegExpRunStatus michael@0: ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, RegExpShared &re, michael@0: Handle input, const jschar *chars, size_t length, michael@0: size_t *lastIndex, MatchConduit &matches) michael@0: { michael@0: RegExpRunStatus status; michael@0: michael@0: /* Switch between MatchOnly and IncludeSubpatterns modes. */ michael@0: if (matches.isPair) { michael@0: size_t lastIndex_orig = *lastIndex; michael@0: /* Only one MatchPair slot provided: execute short-circuiting regexp. */ michael@0: status = re.executeMatchOnly(cx, chars, length, lastIndex, *matches.u.pair); michael@0: if (status == RegExpRunStatus_Success && res) michael@0: res->updateLazily(cx, input, &re, lastIndex_orig); michael@0: } else { michael@0: /* Vector of MatchPairs provided: execute full regexp. */ michael@0: status = re.execute(cx, chars, length, lastIndex, *matches.u.pairs); michael@0: if (status == RegExpRunStatus_Success && res) { michael@0: if (!res->updateFromMatchPairs(cx, input, *matches.u.pairs)) michael@0: return RegExpRunStatus_Error; michael@0: } michael@0: } michael@0: michael@0: return status; michael@0: } michael@0: michael@0: /* Legacy ExecuteRegExp behavior is baked into the JSAPI. */ michael@0: bool michael@0: js::ExecuteRegExpLegacy(JSContext *cx, RegExpStatics *res, RegExpObject &reobj, michael@0: Handle input_, const jschar *chars, size_t length, michael@0: size_t *lastIndex, bool test, MutableHandleValue rval) michael@0: { michael@0: RegExpGuard shared(cx); michael@0: if (!reobj.getShared(cx, &shared)) michael@0: return false; michael@0: michael@0: ScopedMatchPairs matches(&cx->tempLifoAlloc()); michael@0: MatchConduit conduit(&matches); michael@0: michael@0: RegExpRunStatus status = michael@0: ExecuteRegExpImpl(cx, res, *shared, input_, chars, length, lastIndex, conduit); michael@0: michael@0: if (status == RegExpRunStatus_Error) michael@0: return false; michael@0: michael@0: if (status == RegExpRunStatus_Success_NotFound) { michael@0: /* ExecuteRegExp() previously returned an array or null. */ michael@0: rval.setNull(); michael@0: return true; michael@0: } michael@0: michael@0: if (test) { michael@0: /* Forbid an array, as an optimization. */ michael@0: rval.setBoolean(true); michael@0: return true; michael@0: } michael@0: michael@0: RootedString input(cx, input_); michael@0: if (!input) { michael@0: input = js_NewStringCopyN(cx, chars, length); michael@0: if (!input) michael@0: return false; michael@0: } michael@0: michael@0: return CreateRegExpMatchResult(cx, input, matches, rval); michael@0: } michael@0: michael@0: /* Note: returns the original if no escaping need be performed. */ michael@0: static JSAtom * michael@0: EscapeNakedForwardSlashes(JSContext *cx, HandleAtom unescaped) michael@0: { michael@0: size_t oldLen = unescaped->length(); michael@0: const jschar *oldChars = unescaped->chars(); michael@0: michael@0: JS::Anchor anchor(unescaped); michael@0: michael@0: /* We may never need to use |sb|. Start using it lazily. */ michael@0: StringBuffer sb(cx); michael@0: michael@0: for (const jschar *it = oldChars; it < oldChars + oldLen; ++it) { michael@0: if (*it == '/' && (it == oldChars || it[-1] != '\\')) { michael@0: /* There's a forward slash that needs escaping. */ michael@0: if (sb.empty()) { michael@0: /* This is the first one we've seen, copy everything up to this point. */ michael@0: if (!sb.reserve(oldLen + 1)) michael@0: return nullptr; michael@0: sb.infallibleAppend(oldChars, size_t(it - oldChars)); michael@0: } michael@0: if (!sb.append('\\')) michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!sb.empty() && !sb.append(*it)) michael@0: return nullptr; michael@0: } michael@0: michael@0: return sb.empty() ? (JSAtom *)unescaped : sb.finishAtom(); michael@0: } michael@0: michael@0: /* michael@0: * Compile a new |RegExpShared| for the |RegExpObject|. michael@0: * michael@0: * Per ECMAv5 15.10.4.1, we act on combinations of (pattern, flags) as michael@0: * arguments: michael@0: * michael@0: * RegExp, undefined => flags := pattern.flags michael@0: * RegExp, _ => throw TypeError michael@0: * _ => pattern := ToString(pattern) if defined(pattern) else '' michael@0: * flags := ToString(flags) if defined(flags) else '' michael@0: */ michael@0: static bool michael@0: CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder, CallArgs args) michael@0: { michael@0: if (args.length() == 0) { michael@0: RegExpStatics *res = cx->global()->getRegExpStatics(); michael@0: Rooted empty(cx, cx->runtime()->emptyString); michael@0: RegExpObject *reobj = builder.build(empty, res->getFlags()); michael@0: if (!reobj) michael@0: return false; michael@0: args.rval().setObject(*reobj); michael@0: return true; michael@0: } michael@0: michael@0: RootedValue sourceValue(cx, args[0]); michael@0: michael@0: /* michael@0: * If we get passed in an object whose internal [[Class]] property is michael@0: * "RegExp", return a new object with the same source/flags. michael@0: */ michael@0: if (IsObjectWithClass(sourceValue, ESClass_RegExp, cx)) { michael@0: /* michael@0: * Beware, sourceObj may be a (transparent) proxy to a RegExp, so only michael@0: * use generic (proxyable) operations on sourceObj that do not assume michael@0: * sourceObj.is(). michael@0: */ michael@0: RootedObject sourceObj(cx, &sourceValue.toObject()); michael@0: michael@0: if (args.hasDefined(1)) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NEWREGEXP_FLAGGED); michael@0: return false; michael@0: } michael@0: michael@0: /* michael@0: * Only extract the 'flags' out of sourceObj; do not reuse the michael@0: * RegExpShared since it may be from a different compartment. michael@0: */ michael@0: RegExpFlag flags; michael@0: { michael@0: RegExpGuard g(cx); michael@0: if (!RegExpToShared(cx, sourceObj, &g)) michael@0: return false; michael@0: michael@0: flags = g->getFlags(); michael@0: } michael@0: michael@0: /* michael@0: * 'toSource' is a permanent read-only property, so this is equivalent michael@0: * to executing RegExpObject::getSource on the unwrapped object. michael@0: */ michael@0: RootedValue v(cx); michael@0: if (!JSObject::getProperty(cx, sourceObj, sourceObj, cx->names().source, &v)) michael@0: return false; michael@0: michael@0: Rooted sourceAtom(cx, &v.toString()->asAtom()); michael@0: RegExpObject *reobj = builder.build(sourceAtom, flags); michael@0: if (!reobj) michael@0: return false; michael@0: michael@0: args.rval().setObject(*reobj); michael@0: return true; michael@0: } michael@0: michael@0: RootedAtom source(cx); michael@0: if (sourceValue.isUndefined()) { michael@0: source = cx->runtime()->emptyString; michael@0: } else { michael@0: /* Coerce to string and compile. */ michael@0: source = ToAtom(cx, sourceValue); michael@0: if (!source) michael@0: return false; michael@0: } michael@0: michael@0: RegExpFlag flags = RegExpFlag(0); michael@0: if (args.hasDefined(1)) { michael@0: RootedString flagStr(cx, ToString(cx, args[1])); michael@0: if (!flagStr) michael@0: return false; michael@0: args[1].setString(flagStr); michael@0: if (!ParseRegExpFlags(cx, flagStr, &flags)) michael@0: return false; michael@0: } michael@0: michael@0: RootedAtom escapedSourceStr(cx, EscapeNakedForwardSlashes(cx, source)); michael@0: if (!escapedSourceStr) michael@0: return false; michael@0: michael@0: if (!js::RegExpShared::checkSyntax(cx, nullptr, escapedSourceStr)) michael@0: return false; michael@0: michael@0: RegExpStatics *res = cx->global()->getRegExpStatics(); michael@0: RegExpObject *reobj = builder.build(escapedSourceStr, RegExpFlag(flags | res->getFlags())); michael@0: if (!reobj) michael@0: return false; michael@0: michael@0: args.rval().setObject(*reobj); michael@0: return true; michael@0: } michael@0: michael@0: MOZ_ALWAYS_INLINE bool michael@0: IsRegExp(HandleValue v) michael@0: { michael@0: return v.isObject() && v.toObject().is(); michael@0: } michael@0: michael@0: MOZ_ALWAYS_INLINE bool michael@0: regexp_compile_impl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(IsRegExp(args.thisv())); michael@0: RegExpObjectBuilder builder(cx, &args.thisv().toObject().as()); michael@0: return CompileRegExpObject(cx, builder, args); michael@0: } michael@0: michael@0: static bool michael@0: regexp_compile(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: static bool michael@0: regexp_construct(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: if (!args.isConstructing()) { michael@0: /* michael@0: * If first arg is regexp and no flags are given, just return the arg. michael@0: * Otherwise, delegate to the standard constructor. michael@0: * See ECMAv5 15.10.3.1. michael@0: */ michael@0: if (args.hasDefined(0) && michael@0: IsObjectWithClass(args[0], ESClass_RegExp, cx) && michael@0: !args.hasDefined(1)) michael@0: { michael@0: args.rval().set(args[0]); michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: RegExpObjectBuilder builder(cx); michael@0: return CompileRegExpObject(cx, builder, args); michael@0: } michael@0: michael@0: MOZ_ALWAYS_INLINE bool michael@0: regexp_toString_impl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(IsRegExp(args.thisv())); michael@0: michael@0: JSString *str = args.thisv().toObject().as().toString(cx); michael@0: if (!str) michael@0: return false; michael@0: michael@0: args.rval().setString(str); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: regexp_toString(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: static const JSFunctionSpec regexp_methods[] = { michael@0: #if JS_HAS_TOSOURCE michael@0: JS_FN(js_toSource_str, regexp_toString, 0,0), michael@0: #endif michael@0: JS_FN(js_toString_str, regexp_toString, 0,0), michael@0: JS_FN("compile", regexp_compile, 2,0), michael@0: JS_FN("exec", regexp_exec, 1,0), michael@0: JS_FN("test", regexp_test, 1,0), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: /* michael@0: * RegExp static properties. michael@0: * michael@0: * RegExp class static properties and their Perl counterparts: michael@0: * michael@0: * RegExp.input $_ michael@0: * RegExp.multiline $* michael@0: * RegExp.lastMatch $& michael@0: * RegExp.lastParen $+ michael@0: * RegExp.leftContext $` michael@0: * RegExp.rightContext $' michael@0: */ michael@0: michael@0: #define DEFINE_STATIC_GETTER(name, code) \ michael@0: static bool \ michael@0: name(JSContext *cx, unsigned argc, Value *vp) \ michael@0: { \ michael@0: CallArgs args = CallArgsFromVp(argc, vp); \ michael@0: RegExpStatics *res = cx->global()->getRegExpStatics(); \ michael@0: code; \ michael@0: } michael@0: michael@0: DEFINE_STATIC_GETTER(static_input_getter, return res->createPendingInput(cx, args.rval())) michael@0: DEFINE_STATIC_GETTER(static_multiline_getter, args.rval().setBoolean(res->multiline()); michael@0: return true) michael@0: DEFINE_STATIC_GETTER(static_lastMatch_getter, return res->createLastMatch(cx, args.rval())) michael@0: DEFINE_STATIC_GETTER(static_lastParen_getter, return res->createLastParen(cx, args.rval())) michael@0: DEFINE_STATIC_GETTER(static_leftContext_getter, return res->createLeftContext(cx, args.rval())) michael@0: DEFINE_STATIC_GETTER(static_rightContext_getter, return res->createRightContext(cx, args.rval())) michael@0: michael@0: DEFINE_STATIC_GETTER(static_paren1_getter, return res->createParen(cx, 1, args.rval())) michael@0: DEFINE_STATIC_GETTER(static_paren2_getter, return res->createParen(cx, 2, args.rval())) michael@0: DEFINE_STATIC_GETTER(static_paren3_getter, return res->createParen(cx, 3, args.rval())) michael@0: DEFINE_STATIC_GETTER(static_paren4_getter, return res->createParen(cx, 4, args.rval())) michael@0: DEFINE_STATIC_GETTER(static_paren5_getter, return res->createParen(cx, 5, args.rval())) michael@0: DEFINE_STATIC_GETTER(static_paren6_getter, return res->createParen(cx, 6, args.rval())) michael@0: DEFINE_STATIC_GETTER(static_paren7_getter, return res->createParen(cx, 7, args.rval())) michael@0: DEFINE_STATIC_GETTER(static_paren8_getter, return res->createParen(cx, 8, args.rval())) michael@0: DEFINE_STATIC_GETTER(static_paren9_getter, return res->createParen(cx, 9, args.rval())) michael@0: michael@0: #define DEFINE_STATIC_SETTER(name, code) \ michael@0: static bool \ michael@0: name(JSContext *cx, unsigned argc, Value *vp) \ michael@0: { \ michael@0: RegExpStatics *res = cx->global()->getRegExpStatics(); \ michael@0: code; \ michael@0: return true; \ michael@0: } michael@0: michael@0: static bool michael@0: static_input_setter(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: RegExpStatics *res = cx->global()->getRegExpStatics(); michael@0: michael@0: RootedString str(cx, ToString(cx, args.get(0))); michael@0: if (!str) michael@0: return false; michael@0: michael@0: res->setPendingInput(str); michael@0: args.rval().setString(str); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: static_multiline_setter(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: RegExpStatics *res = cx->global()->getRegExpStatics(); michael@0: michael@0: bool b = ToBoolean(args.get(0)); michael@0: res->setMultiline(cx, b); michael@0: args.rval().setBoolean(b); michael@0: return true; michael@0: } michael@0: michael@0: static const JSPropertySpec regexp_static_props[] = { michael@0: JS_PSGS("input", static_input_getter, static_input_setter, michael@0: JSPROP_PERMANENT | JSPROP_ENUMERATE), michael@0: JS_PSGS("multiline", static_multiline_getter, static_multiline_setter, michael@0: JSPROP_PERMANENT | JSPROP_ENUMERATE), michael@0: JS_PSG("lastMatch", static_lastMatch_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE), michael@0: JS_PSG("lastParen", static_lastParen_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE), michael@0: JS_PSG("leftContext", static_leftContext_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE), michael@0: JS_PSG("rightContext", static_rightContext_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE), michael@0: JS_PSG("$1", static_paren1_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE), michael@0: JS_PSG("$2", static_paren2_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE), michael@0: JS_PSG("$3", static_paren3_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE), michael@0: JS_PSG("$4", static_paren4_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE), michael@0: JS_PSG("$5", static_paren5_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE), michael@0: JS_PSG("$6", static_paren6_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE), michael@0: JS_PSG("$7", static_paren7_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE), michael@0: JS_PSG("$8", static_paren8_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE), michael@0: JS_PSG("$9", static_paren9_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE), michael@0: JS_PSGS("$_", static_input_getter, static_input_setter, JSPROP_PERMANENT), michael@0: JS_PSGS("$*", static_multiline_getter, static_multiline_setter, JSPROP_PERMANENT), michael@0: JS_PSG("$&", static_lastMatch_getter, JSPROP_PERMANENT), michael@0: JS_PSG("$+", static_lastParen_getter, JSPROP_PERMANENT), michael@0: JS_PSG("$`", static_leftContext_getter, JSPROP_PERMANENT), michael@0: JS_PSG("$'", static_rightContext_getter, JSPROP_PERMANENT), michael@0: JS_PS_END michael@0: }; michael@0: michael@0: JSObject * michael@0: js_InitRegExpClass(JSContext *cx, HandleObject obj) michael@0: { michael@0: JS_ASSERT(obj->isNative()); michael@0: michael@0: Rooted global(cx, &obj->as()); michael@0: michael@0: RootedObject proto(cx, global->createBlankPrototype(cx, &RegExpObject::class_)); michael@0: if (!proto) michael@0: return nullptr; michael@0: proto->setPrivate(nullptr); michael@0: michael@0: HandlePropertyName empty = cx->names().empty; michael@0: RegExpObjectBuilder builder(cx, &proto->as()); michael@0: if (!builder.build(empty, RegExpFlag(0))) michael@0: return nullptr; michael@0: michael@0: if (!DefinePropertiesAndBrand(cx, proto, nullptr, regexp_methods)) michael@0: return nullptr; michael@0: michael@0: RootedFunction ctor(cx); michael@0: ctor = global->createConstructor(cx, regexp_construct, cx->names().RegExp, 2); michael@0: if (!ctor) michael@0: return nullptr; michael@0: michael@0: if (!LinkConstructorAndPrototype(cx, ctor, proto)) michael@0: return nullptr; michael@0: michael@0: /* Add static properties to the RegExp constructor. */ michael@0: if (!JS_DefineProperties(cx, ctor, regexp_static_props)) michael@0: return nullptr; michael@0: michael@0: if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_RegExp, ctor, proto)) michael@0: return nullptr; michael@0: michael@0: return proto; michael@0: } michael@0: michael@0: RegExpRunStatus michael@0: js::ExecuteRegExp(JSContext *cx, HandleObject regexp, HandleString string, michael@0: MatchConduit &matches, RegExpStaticsUpdate staticsUpdate) michael@0: { michael@0: /* Step 1 (b) was performed by CallNonGenericMethod. */ michael@0: Rooted reobj(cx, ®exp->as()); michael@0: michael@0: RegExpGuard re(cx); michael@0: if (!reobj->getShared(cx, &re)) michael@0: return RegExpRunStatus_Error; michael@0: michael@0: RegExpStatics *res = (staticsUpdate == UpdateRegExpStatics) michael@0: ? cx->global()->getRegExpStatics() michael@0: : nullptr; michael@0: michael@0: /* Step 3. */ michael@0: Rooted input(cx, string->ensureLinear(cx)); michael@0: if (!input) michael@0: return RegExpRunStatus_Error; michael@0: michael@0: /* Step 4. */ michael@0: RootedValue lastIndex(cx, reobj->getLastIndex()); michael@0: size_t length = input->length(); michael@0: michael@0: /* Step 5. */ michael@0: int i; michael@0: if (lastIndex.isInt32()) { michael@0: /* Aggressively avoid doubles. */ michael@0: i = lastIndex.toInt32(); michael@0: } else { michael@0: double d; michael@0: if (!ToInteger(cx, lastIndex, &d)) michael@0: return RegExpRunStatus_Error; michael@0: michael@0: /* Inlined steps 6, 7, 9a with doubles to detect failure case. */ michael@0: if ((re->global() || re->sticky()) && (d < 0 || d > length)) { michael@0: reobj->zeroLastIndex(); michael@0: return RegExpRunStatus_Success_NotFound; michael@0: } michael@0: michael@0: i = int(d); michael@0: } michael@0: michael@0: /* Steps 6-7 (with sticky extension). */ michael@0: if (!re->global() && !re->sticky()) michael@0: i = 0; michael@0: michael@0: /* Step 9a. */ michael@0: if (i < 0 || size_t(i) > length) { michael@0: reobj->zeroLastIndex(); michael@0: return RegExpRunStatus_Success_NotFound; michael@0: } michael@0: michael@0: /* Steps 8-21. */ michael@0: const jschar *chars = input->chars(); michael@0: size_t lastIndexInt(i); michael@0: RegExpRunStatus status = michael@0: ExecuteRegExpImpl(cx, res, *re, input, chars, length, &lastIndexInt, matches); michael@0: michael@0: if (status == RegExpRunStatus_Error) michael@0: return RegExpRunStatus_Error; michael@0: michael@0: /* Steps 9a and 11 (with sticky extension). */ michael@0: if (status == RegExpRunStatus_Success_NotFound) michael@0: reobj->zeroLastIndex(); michael@0: else if (re->global() || re->sticky()) michael@0: reobj->setLastIndex(lastIndexInt); michael@0: michael@0: return status; michael@0: } michael@0: michael@0: /* ES5 15.10.6.2 (and 15.10.6.3, which calls 15.10.6.2). */ michael@0: static RegExpRunStatus michael@0: ExecuteRegExp(JSContext *cx, CallArgs args, MatchConduit &matches) michael@0: { michael@0: /* Step 1 (a) was performed by CallNonGenericMethod. */ michael@0: RootedObject regexp(cx, &args.thisv().toObject()); michael@0: michael@0: /* Step 2. */ michael@0: RootedString string(cx, ToString(cx, args.get(0))); michael@0: if (!string) michael@0: return RegExpRunStatus_Error; michael@0: michael@0: return ExecuteRegExp(cx, regexp, string, matches, UpdateRegExpStatics); michael@0: } michael@0: michael@0: /* ES5 15.10.6.2. */ michael@0: static bool michael@0: regexp_exec_impl(JSContext *cx, HandleObject regexp, HandleString string, michael@0: RegExpStaticsUpdate staticsUpdate, MutableHandleValue rval) michael@0: { michael@0: /* Execute regular expression and gather matches. */ michael@0: ScopedMatchPairs matches(&cx->tempLifoAlloc()); michael@0: MatchConduit conduit(&matches); michael@0: michael@0: RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, conduit, staticsUpdate); michael@0: michael@0: if (status == RegExpRunStatus_Error) michael@0: return false; michael@0: michael@0: if (status == RegExpRunStatus_Success_NotFound) { michael@0: rval.setNull(); michael@0: return true; michael@0: } michael@0: michael@0: return CreateRegExpMatchResult(cx, string, matches, rval); michael@0: } michael@0: michael@0: static bool michael@0: regexp_exec_impl(JSContext *cx, CallArgs args) michael@0: { michael@0: RootedObject regexp(cx, &args.thisv().toObject()); michael@0: RootedString string(cx, ToString(cx, args.get(0))); michael@0: if (!string) michael@0: return false; michael@0: michael@0: return regexp_exec_impl(cx, regexp, string, UpdateRegExpStatics, args.rval()); michael@0: } michael@0: michael@0: bool michael@0: js::regexp_exec(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, IsRegExp, regexp_exec_impl, args); michael@0: } michael@0: michael@0: /* Separate interface for use by IonMonkey. */ michael@0: bool michael@0: js::regexp_exec_raw(JSContext *cx, HandleObject regexp, HandleString input, MutableHandleValue output) michael@0: { michael@0: return regexp_exec_impl(cx, regexp, input, UpdateRegExpStatics, output); michael@0: } michael@0: michael@0: bool michael@0: js::regexp_exec_no_statics(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 2); michael@0: JS_ASSERT(IsRegExp(args[0])); michael@0: JS_ASSERT(args[1].isString()); michael@0: michael@0: RootedObject regexp(cx, &args[0].toObject()); michael@0: RootedString string(cx, args[1].toString()); michael@0: michael@0: return regexp_exec_impl(cx, regexp, string, DontUpdateRegExpStatics, args.rval()); michael@0: } michael@0: michael@0: /* ES5 15.10.6.3. */ michael@0: static bool michael@0: regexp_test_impl(JSContext *cx, CallArgs args) michael@0: { michael@0: MatchPair match; michael@0: MatchConduit conduit(&match); michael@0: RegExpRunStatus status = ExecuteRegExp(cx, args, conduit); michael@0: args.rval().setBoolean(status == RegExpRunStatus_Success); michael@0: return status != RegExpRunStatus_Error; michael@0: } michael@0: michael@0: /* Separate interface for use by IonMonkey. */ michael@0: bool michael@0: js::regexp_test_raw(JSContext *cx, HandleObject regexp, HandleString input, bool *result) michael@0: { michael@0: MatchPair match; michael@0: MatchConduit conduit(&match); michael@0: RegExpRunStatus status = ExecuteRegExp(cx, regexp, input, conduit, UpdateRegExpStatics); michael@0: *result = (status == RegExpRunStatus_Success); michael@0: return status != RegExpRunStatus_Error; michael@0: } michael@0: michael@0: bool michael@0: js::regexp_test(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, IsRegExp, regexp_test_impl, args); michael@0: } michael@0: michael@0: bool michael@0: js::regexp_test_no_statics(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JS_ASSERT(args.length() == 2); michael@0: JS_ASSERT(IsRegExp(args[0])); michael@0: JS_ASSERT(args[1].isString()); michael@0: michael@0: RootedObject regexp(cx, &args[0].toObject()); michael@0: RootedString string(cx, args[1].toString()); michael@0: michael@0: MatchPair match; michael@0: MatchConduit conduit(&match); michael@0: RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, conduit, DontUpdateRegExpStatics); michael@0: args.rval().setBoolean(status == RegExpRunStatus_Success); michael@0: return status != RegExpRunStatus_Error; michael@0: }