1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/builtin/RegExp.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,705 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "builtin/RegExp.h" 1.11 + 1.12 +#include "jscntxt.h" 1.13 + 1.14 +#include "vm/RegExpStatics.h" 1.15 +#include "vm/StringBuffer.h" 1.16 + 1.17 +#include "jsobjinlines.h" 1.18 + 1.19 +using namespace js; 1.20 +using namespace js::types; 1.21 + 1.22 +using mozilla::ArrayLength; 1.23 + 1.24 +bool 1.25 +js::CreateRegExpMatchResult(JSContext *cx, HandleString input, const MatchPairs &matches, 1.26 + MutableHandleValue rval) 1.27 +{ 1.28 + JS_ASSERT(input); 1.29 + 1.30 + /* 1.31 + * Create the (slow) result array for a match. 1.32 + * 1.33 + * Array contents: 1.34 + * 0: matched string 1.35 + * 1..pairCount-1: paren matches 1.36 + * input: input string 1.37 + * index: start index for the match 1.38 + */ 1.39 + 1.40 + /* Get the templateObject that defines the shape and type of the output object */ 1.41 + JSObject *templateObject = cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx); 1.42 + if (!templateObject) 1.43 + return false; 1.44 + 1.45 + size_t numPairs = matches.length(); 1.46 + JS_ASSERT(numPairs > 0); 1.47 + 1.48 + RootedObject arr(cx, NewDenseAllocatedArrayWithTemplate(cx, numPairs, templateObject)); 1.49 + if (!arr) 1.50 + return false; 1.51 + 1.52 + /* Store a Value for each pair. */ 1.53 + for (size_t i = 0; i < numPairs; i++) { 1.54 + const MatchPair &pair = matches[i]; 1.55 + 1.56 + if (pair.isUndefined()) { 1.57 + JS_ASSERT(i != 0); /* Since we had a match, first pair must be present. */ 1.58 + arr->setDenseInitializedLength(i + 1); 1.59 + arr->initDenseElement(i, UndefinedValue()); 1.60 + } else { 1.61 + JSLinearString *str = js_NewDependentString(cx, input, pair.start, pair.length()); 1.62 + if (!str) 1.63 + return false; 1.64 + arr->setDenseInitializedLength(i + 1); 1.65 + arr->initDenseElement(i, StringValue(str)); 1.66 + } 1.67 + } 1.68 + 1.69 + /* Set the |index| property. (TemplateObject positions it in slot 0) */ 1.70 + arr->nativeSetSlot(0, Int32Value(matches[0].start)); 1.71 + 1.72 + /* Set the |input| property. (TemplateObject positions it in slot 1) */ 1.73 + arr->nativeSetSlot(1, StringValue(input)); 1.74 + 1.75 +#ifdef DEBUG 1.76 + RootedValue test(cx); 1.77 + RootedId id(cx, NameToId(cx->names().index)); 1.78 + if (!baseops::GetProperty(cx, arr, id, &test)) 1.79 + return false; 1.80 + JS_ASSERT(test == arr->nativeGetSlot(0)); 1.81 + id = NameToId(cx->names().input); 1.82 + if (!baseops::GetProperty(cx, arr, id, &test)) 1.83 + return false; 1.84 + JS_ASSERT(test == arr->nativeGetSlot(1)); 1.85 +#endif 1.86 + 1.87 + rval.setObject(*arr); 1.88 + return true; 1.89 +} 1.90 + 1.91 +static RegExpRunStatus 1.92 +ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, RegExpShared &re, 1.93 + Handle<JSLinearString*> input, const jschar *chars, size_t length, 1.94 + size_t *lastIndex, MatchConduit &matches) 1.95 +{ 1.96 + RegExpRunStatus status; 1.97 + 1.98 + /* Switch between MatchOnly and IncludeSubpatterns modes. */ 1.99 + if (matches.isPair) { 1.100 + size_t lastIndex_orig = *lastIndex; 1.101 + /* Only one MatchPair slot provided: execute short-circuiting regexp. */ 1.102 + status = re.executeMatchOnly(cx, chars, length, lastIndex, *matches.u.pair); 1.103 + if (status == RegExpRunStatus_Success && res) 1.104 + res->updateLazily(cx, input, &re, lastIndex_orig); 1.105 + } else { 1.106 + /* Vector of MatchPairs provided: execute full regexp. */ 1.107 + status = re.execute(cx, chars, length, lastIndex, *matches.u.pairs); 1.108 + if (status == RegExpRunStatus_Success && res) { 1.109 + if (!res->updateFromMatchPairs(cx, input, *matches.u.pairs)) 1.110 + return RegExpRunStatus_Error; 1.111 + } 1.112 + } 1.113 + 1.114 + return status; 1.115 +} 1.116 + 1.117 +/* Legacy ExecuteRegExp behavior is baked into the JSAPI. */ 1.118 +bool 1.119 +js::ExecuteRegExpLegacy(JSContext *cx, RegExpStatics *res, RegExpObject &reobj, 1.120 + Handle<JSLinearString*> input_, const jschar *chars, size_t length, 1.121 + size_t *lastIndex, bool test, MutableHandleValue rval) 1.122 +{ 1.123 + RegExpGuard shared(cx); 1.124 + if (!reobj.getShared(cx, &shared)) 1.125 + return false; 1.126 + 1.127 + ScopedMatchPairs matches(&cx->tempLifoAlloc()); 1.128 + MatchConduit conduit(&matches); 1.129 + 1.130 + RegExpRunStatus status = 1.131 + ExecuteRegExpImpl(cx, res, *shared, input_, chars, length, lastIndex, conduit); 1.132 + 1.133 + if (status == RegExpRunStatus_Error) 1.134 + return false; 1.135 + 1.136 + if (status == RegExpRunStatus_Success_NotFound) { 1.137 + /* ExecuteRegExp() previously returned an array or null. */ 1.138 + rval.setNull(); 1.139 + return true; 1.140 + } 1.141 + 1.142 + if (test) { 1.143 + /* Forbid an array, as an optimization. */ 1.144 + rval.setBoolean(true); 1.145 + return true; 1.146 + } 1.147 + 1.148 + RootedString input(cx, input_); 1.149 + if (!input) { 1.150 + input = js_NewStringCopyN<CanGC>(cx, chars, length); 1.151 + if (!input) 1.152 + return false; 1.153 + } 1.154 + 1.155 + return CreateRegExpMatchResult(cx, input, matches, rval); 1.156 +} 1.157 + 1.158 +/* Note: returns the original if no escaping need be performed. */ 1.159 +static JSAtom * 1.160 +EscapeNakedForwardSlashes(JSContext *cx, HandleAtom unescaped) 1.161 +{ 1.162 + size_t oldLen = unescaped->length(); 1.163 + const jschar *oldChars = unescaped->chars(); 1.164 + 1.165 + JS::Anchor<JSString *> anchor(unescaped); 1.166 + 1.167 + /* We may never need to use |sb|. Start using it lazily. */ 1.168 + StringBuffer sb(cx); 1.169 + 1.170 + for (const jschar *it = oldChars; it < oldChars + oldLen; ++it) { 1.171 + if (*it == '/' && (it == oldChars || it[-1] != '\\')) { 1.172 + /* There's a forward slash that needs escaping. */ 1.173 + if (sb.empty()) { 1.174 + /* This is the first one we've seen, copy everything up to this point. */ 1.175 + if (!sb.reserve(oldLen + 1)) 1.176 + return nullptr; 1.177 + sb.infallibleAppend(oldChars, size_t(it - oldChars)); 1.178 + } 1.179 + if (!sb.append('\\')) 1.180 + return nullptr; 1.181 + } 1.182 + 1.183 + if (!sb.empty() && !sb.append(*it)) 1.184 + return nullptr; 1.185 + } 1.186 + 1.187 + return sb.empty() ? (JSAtom *)unescaped : sb.finishAtom(); 1.188 +} 1.189 + 1.190 +/* 1.191 + * Compile a new |RegExpShared| for the |RegExpObject|. 1.192 + * 1.193 + * Per ECMAv5 15.10.4.1, we act on combinations of (pattern, flags) as 1.194 + * arguments: 1.195 + * 1.196 + * RegExp, undefined => flags := pattern.flags 1.197 + * RegExp, _ => throw TypeError 1.198 + * _ => pattern := ToString(pattern) if defined(pattern) else '' 1.199 + * flags := ToString(flags) if defined(flags) else '' 1.200 + */ 1.201 +static bool 1.202 +CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder, CallArgs args) 1.203 +{ 1.204 + if (args.length() == 0) { 1.205 + RegExpStatics *res = cx->global()->getRegExpStatics(); 1.206 + Rooted<JSAtom*> empty(cx, cx->runtime()->emptyString); 1.207 + RegExpObject *reobj = builder.build(empty, res->getFlags()); 1.208 + if (!reobj) 1.209 + return false; 1.210 + args.rval().setObject(*reobj); 1.211 + return true; 1.212 + } 1.213 + 1.214 + RootedValue sourceValue(cx, args[0]); 1.215 + 1.216 + /* 1.217 + * If we get passed in an object whose internal [[Class]] property is 1.218 + * "RegExp", return a new object with the same source/flags. 1.219 + */ 1.220 + if (IsObjectWithClass(sourceValue, ESClass_RegExp, cx)) { 1.221 + /* 1.222 + * Beware, sourceObj may be a (transparent) proxy to a RegExp, so only 1.223 + * use generic (proxyable) operations on sourceObj that do not assume 1.224 + * sourceObj.is<RegExpObject>(). 1.225 + */ 1.226 + RootedObject sourceObj(cx, &sourceValue.toObject()); 1.227 + 1.228 + if (args.hasDefined(1)) { 1.229 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NEWREGEXP_FLAGGED); 1.230 + return false; 1.231 + } 1.232 + 1.233 + /* 1.234 + * Only extract the 'flags' out of sourceObj; do not reuse the 1.235 + * RegExpShared since it may be from a different compartment. 1.236 + */ 1.237 + RegExpFlag flags; 1.238 + { 1.239 + RegExpGuard g(cx); 1.240 + if (!RegExpToShared(cx, sourceObj, &g)) 1.241 + return false; 1.242 + 1.243 + flags = g->getFlags(); 1.244 + } 1.245 + 1.246 + /* 1.247 + * 'toSource' is a permanent read-only property, so this is equivalent 1.248 + * to executing RegExpObject::getSource on the unwrapped object. 1.249 + */ 1.250 + RootedValue v(cx); 1.251 + if (!JSObject::getProperty(cx, sourceObj, sourceObj, cx->names().source, &v)) 1.252 + return false; 1.253 + 1.254 + Rooted<JSAtom*> sourceAtom(cx, &v.toString()->asAtom()); 1.255 + RegExpObject *reobj = builder.build(sourceAtom, flags); 1.256 + if (!reobj) 1.257 + return false; 1.258 + 1.259 + args.rval().setObject(*reobj); 1.260 + return true; 1.261 + } 1.262 + 1.263 + RootedAtom source(cx); 1.264 + if (sourceValue.isUndefined()) { 1.265 + source = cx->runtime()->emptyString; 1.266 + } else { 1.267 + /* Coerce to string and compile. */ 1.268 + source = ToAtom<CanGC>(cx, sourceValue); 1.269 + if (!source) 1.270 + return false; 1.271 + } 1.272 + 1.273 + RegExpFlag flags = RegExpFlag(0); 1.274 + if (args.hasDefined(1)) { 1.275 + RootedString flagStr(cx, ToString<CanGC>(cx, args[1])); 1.276 + if (!flagStr) 1.277 + return false; 1.278 + args[1].setString(flagStr); 1.279 + if (!ParseRegExpFlags(cx, flagStr, &flags)) 1.280 + return false; 1.281 + } 1.282 + 1.283 + RootedAtom escapedSourceStr(cx, EscapeNakedForwardSlashes(cx, source)); 1.284 + if (!escapedSourceStr) 1.285 + return false; 1.286 + 1.287 + if (!js::RegExpShared::checkSyntax(cx, nullptr, escapedSourceStr)) 1.288 + return false; 1.289 + 1.290 + RegExpStatics *res = cx->global()->getRegExpStatics(); 1.291 + RegExpObject *reobj = builder.build(escapedSourceStr, RegExpFlag(flags | res->getFlags())); 1.292 + if (!reobj) 1.293 + return false; 1.294 + 1.295 + args.rval().setObject(*reobj); 1.296 + return true; 1.297 +} 1.298 + 1.299 +MOZ_ALWAYS_INLINE bool 1.300 +IsRegExp(HandleValue v) 1.301 +{ 1.302 + return v.isObject() && v.toObject().is<RegExpObject>(); 1.303 +} 1.304 + 1.305 +MOZ_ALWAYS_INLINE bool 1.306 +regexp_compile_impl(JSContext *cx, CallArgs args) 1.307 +{ 1.308 + JS_ASSERT(IsRegExp(args.thisv())); 1.309 + RegExpObjectBuilder builder(cx, &args.thisv().toObject().as<RegExpObject>()); 1.310 + return CompileRegExpObject(cx, builder, args); 1.311 +} 1.312 + 1.313 +static bool 1.314 +regexp_compile(JSContext *cx, unsigned argc, Value *vp) 1.315 +{ 1.316 + CallArgs args = CallArgsFromVp(argc, vp); 1.317 + return CallNonGenericMethod<IsRegExp, regexp_compile_impl>(cx, args); 1.318 +} 1.319 + 1.320 +static bool 1.321 +regexp_construct(JSContext *cx, unsigned argc, Value *vp) 1.322 +{ 1.323 + CallArgs args = CallArgsFromVp(argc, vp); 1.324 + 1.325 + if (!args.isConstructing()) { 1.326 + /* 1.327 + * If first arg is regexp and no flags are given, just return the arg. 1.328 + * Otherwise, delegate to the standard constructor. 1.329 + * See ECMAv5 15.10.3.1. 1.330 + */ 1.331 + if (args.hasDefined(0) && 1.332 + IsObjectWithClass(args[0], ESClass_RegExp, cx) && 1.333 + !args.hasDefined(1)) 1.334 + { 1.335 + args.rval().set(args[0]); 1.336 + return true; 1.337 + } 1.338 + } 1.339 + 1.340 + RegExpObjectBuilder builder(cx); 1.341 + return CompileRegExpObject(cx, builder, args); 1.342 +} 1.343 + 1.344 +MOZ_ALWAYS_INLINE bool 1.345 +regexp_toString_impl(JSContext *cx, CallArgs args) 1.346 +{ 1.347 + JS_ASSERT(IsRegExp(args.thisv())); 1.348 + 1.349 + JSString *str = args.thisv().toObject().as<RegExpObject>().toString(cx); 1.350 + if (!str) 1.351 + return false; 1.352 + 1.353 + args.rval().setString(str); 1.354 + return true; 1.355 +} 1.356 + 1.357 +static bool 1.358 +regexp_toString(JSContext *cx, unsigned argc, Value *vp) 1.359 +{ 1.360 + CallArgs args = CallArgsFromVp(argc, vp); 1.361 + return CallNonGenericMethod<IsRegExp, regexp_toString_impl>(cx, args); 1.362 +} 1.363 + 1.364 +static const JSFunctionSpec regexp_methods[] = { 1.365 +#if JS_HAS_TOSOURCE 1.366 + JS_FN(js_toSource_str, regexp_toString, 0,0), 1.367 +#endif 1.368 + JS_FN(js_toString_str, regexp_toString, 0,0), 1.369 + JS_FN("compile", regexp_compile, 2,0), 1.370 + JS_FN("exec", regexp_exec, 1,0), 1.371 + JS_FN("test", regexp_test, 1,0), 1.372 + JS_FS_END 1.373 +}; 1.374 + 1.375 +/* 1.376 + * RegExp static properties. 1.377 + * 1.378 + * RegExp class static properties and their Perl counterparts: 1.379 + * 1.380 + * RegExp.input $_ 1.381 + * RegExp.multiline $* 1.382 + * RegExp.lastMatch $& 1.383 + * RegExp.lastParen $+ 1.384 + * RegExp.leftContext $` 1.385 + * RegExp.rightContext $' 1.386 + */ 1.387 + 1.388 +#define DEFINE_STATIC_GETTER(name, code) \ 1.389 + static bool \ 1.390 + name(JSContext *cx, unsigned argc, Value *vp) \ 1.391 + { \ 1.392 + CallArgs args = CallArgsFromVp(argc, vp); \ 1.393 + RegExpStatics *res = cx->global()->getRegExpStatics(); \ 1.394 + code; \ 1.395 + } 1.396 + 1.397 +DEFINE_STATIC_GETTER(static_input_getter, return res->createPendingInput(cx, args.rval())) 1.398 +DEFINE_STATIC_GETTER(static_multiline_getter, args.rval().setBoolean(res->multiline()); 1.399 + return true) 1.400 +DEFINE_STATIC_GETTER(static_lastMatch_getter, return res->createLastMatch(cx, args.rval())) 1.401 +DEFINE_STATIC_GETTER(static_lastParen_getter, return res->createLastParen(cx, args.rval())) 1.402 +DEFINE_STATIC_GETTER(static_leftContext_getter, return res->createLeftContext(cx, args.rval())) 1.403 +DEFINE_STATIC_GETTER(static_rightContext_getter, return res->createRightContext(cx, args.rval())) 1.404 + 1.405 +DEFINE_STATIC_GETTER(static_paren1_getter, return res->createParen(cx, 1, args.rval())) 1.406 +DEFINE_STATIC_GETTER(static_paren2_getter, return res->createParen(cx, 2, args.rval())) 1.407 +DEFINE_STATIC_GETTER(static_paren3_getter, return res->createParen(cx, 3, args.rval())) 1.408 +DEFINE_STATIC_GETTER(static_paren4_getter, return res->createParen(cx, 4, args.rval())) 1.409 +DEFINE_STATIC_GETTER(static_paren5_getter, return res->createParen(cx, 5, args.rval())) 1.410 +DEFINE_STATIC_GETTER(static_paren6_getter, return res->createParen(cx, 6, args.rval())) 1.411 +DEFINE_STATIC_GETTER(static_paren7_getter, return res->createParen(cx, 7, args.rval())) 1.412 +DEFINE_STATIC_GETTER(static_paren8_getter, return res->createParen(cx, 8, args.rval())) 1.413 +DEFINE_STATIC_GETTER(static_paren9_getter, return res->createParen(cx, 9, args.rval())) 1.414 + 1.415 +#define DEFINE_STATIC_SETTER(name, code) \ 1.416 + static bool \ 1.417 + name(JSContext *cx, unsigned argc, Value *vp) \ 1.418 + { \ 1.419 + RegExpStatics *res = cx->global()->getRegExpStatics(); \ 1.420 + code; \ 1.421 + return true; \ 1.422 + } 1.423 + 1.424 +static bool 1.425 +static_input_setter(JSContext *cx, unsigned argc, Value *vp) 1.426 +{ 1.427 + CallArgs args = CallArgsFromVp(argc, vp); 1.428 + RegExpStatics *res = cx->global()->getRegExpStatics(); 1.429 + 1.430 + RootedString str(cx, ToString<CanGC>(cx, args.get(0))); 1.431 + if (!str) 1.432 + return false; 1.433 + 1.434 + res->setPendingInput(str); 1.435 + args.rval().setString(str); 1.436 + return true; 1.437 +} 1.438 + 1.439 +static bool 1.440 +static_multiline_setter(JSContext *cx, unsigned argc, Value *vp) 1.441 +{ 1.442 + CallArgs args = CallArgsFromVp(argc, vp); 1.443 + RegExpStatics *res = cx->global()->getRegExpStatics(); 1.444 + 1.445 + bool b = ToBoolean(args.get(0)); 1.446 + res->setMultiline(cx, b); 1.447 + args.rval().setBoolean(b); 1.448 + return true; 1.449 +} 1.450 + 1.451 +static const JSPropertySpec regexp_static_props[] = { 1.452 + JS_PSGS("input", static_input_getter, static_input_setter, 1.453 + JSPROP_PERMANENT | JSPROP_ENUMERATE), 1.454 + JS_PSGS("multiline", static_multiline_getter, static_multiline_setter, 1.455 + JSPROP_PERMANENT | JSPROP_ENUMERATE), 1.456 + JS_PSG("lastMatch", static_lastMatch_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE), 1.457 + JS_PSG("lastParen", static_lastParen_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE), 1.458 + JS_PSG("leftContext", static_leftContext_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE), 1.459 + JS_PSG("rightContext", static_rightContext_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE), 1.460 + JS_PSG("$1", static_paren1_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE), 1.461 + JS_PSG("$2", static_paren2_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE), 1.462 + JS_PSG("$3", static_paren3_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE), 1.463 + JS_PSG("$4", static_paren4_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE), 1.464 + JS_PSG("$5", static_paren5_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE), 1.465 + JS_PSG("$6", static_paren6_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE), 1.466 + JS_PSG("$7", static_paren7_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE), 1.467 + JS_PSG("$8", static_paren8_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE), 1.468 + JS_PSG("$9", static_paren9_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE), 1.469 + JS_PSGS("$_", static_input_getter, static_input_setter, JSPROP_PERMANENT), 1.470 + JS_PSGS("$*", static_multiline_getter, static_multiline_setter, JSPROP_PERMANENT), 1.471 + JS_PSG("$&", static_lastMatch_getter, JSPROP_PERMANENT), 1.472 + JS_PSG("$+", static_lastParen_getter, JSPROP_PERMANENT), 1.473 + JS_PSG("$`", static_leftContext_getter, JSPROP_PERMANENT), 1.474 + JS_PSG("$'", static_rightContext_getter, JSPROP_PERMANENT), 1.475 + JS_PS_END 1.476 +}; 1.477 + 1.478 +JSObject * 1.479 +js_InitRegExpClass(JSContext *cx, HandleObject obj) 1.480 +{ 1.481 + JS_ASSERT(obj->isNative()); 1.482 + 1.483 + Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>()); 1.484 + 1.485 + RootedObject proto(cx, global->createBlankPrototype(cx, &RegExpObject::class_)); 1.486 + if (!proto) 1.487 + return nullptr; 1.488 + proto->setPrivate(nullptr); 1.489 + 1.490 + HandlePropertyName empty = cx->names().empty; 1.491 + RegExpObjectBuilder builder(cx, &proto->as<RegExpObject>()); 1.492 + if (!builder.build(empty, RegExpFlag(0))) 1.493 + return nullptr; 1.494 + 1.495 + if (!DefinePropertiesAndBrand(cx, proto, nullptr, regexp_methods)) 1.496 + return nullptr; 1.497 + 1.498 + RootedFunction ctor(cx); 1.499 + ctor = global->createConstructor(cx, regexp_construct, cx->names().RegExp, 2); 1.500 + if (!ctor) 1.501 + return nullptr; 1.502 + 1.503 + if (!LinkConstructorAndPrototype(cx, ctor, proto)) 1.504 + return nullptr; 1.505 + 1.506 + /* Add static properties to the RegExp constructor. */ 1.507 + if (!JS_DefineProperties(cx, ctor, regexp_static_props)) 1.508 + return nullptr; 1.509 + 1.510 + if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_RegExp, ctor, proto)) 1.511 + return nullptr; 1.512 + 1.513 + return proto; 1.514 +} 1.515 + 1.516 +RegExpRunStatus 1.517 +js::ExecuteRegExp(JSContext *cx, HandleObject regexp, HandleString string, 1.518 + MatchConduit &matches, RegExpStaticsUpdate staticsUpdate) 1.519 +{ 1.520 + /* Step 1 (b) was performed by CallNonGenericMethod. */ 1.521 + Rooted<RegExpObject*> reobj(cx, ®exp->as<RegExpObject>()); 1.522 + 1.523 + RegExpGuard re(cx); 1.524 + if (!reobj->getShared(cx, &re)) 1.525 + return RegExpRunStatus_Error; 1.526 + 1.527 + RegExpStatics *res = (staticsUpdate == UpdateRegExpStatics) 1.528 + ? cx->global()->getRegExpStatics() 1.529 + : nullptr; 1.530 + 1.531 + /* Step 3. */ 1.532 + Rooted<JSLinearString*> input(cx, string->ensureLinear(cx)); 1.533 + if (!input) 1.534 + return RegExpRunStatus_Error; 1.535 + 1.536 + /* Step 4. */ 1.537 + RootedValue lastIndex(cx, reobj->getLastIndex()); 1.538 + size_t length = input->length(); 1.539 + 1.540 + /* Step 5. */ 1.541 + int i; 1.542 + if (lastIndex.isInt32()) { 1.543 + /* Aggressively avoid doubles. */ 1.544 + i = lastIndex.toInt32(); 1.545 + } else { 1.546 + double d; 1.547 + if (!ToInteger(cx, lastIndex, &d)) 1.548 + return RegExpRunStatus_Error; 1.549 + 1.550 + /* Inlined steps 6, 7, 9a with doubles to detect failure case. */ 1.551 + if ((re->global() || re->sticky()) && (d < 0 || d > length)) { 1.552 + reobj->zeroLastIndex(); 1.553 + return RegExpRunStatus_Success_NotFound; 1.554 + } 1.555 + 1.556 + i = int(d); 1.557 + } 1.558 + 1.559 + /* Steps 6-7 (with sticky extension). */ 1.560 + if (!re->global() && !re->sticky()) 1.561 + i = 0; 1.562 + 1.563 + /* Step 9a. */ 1.564 + if (i < 0 || size_t(i) > length) { 1.565 + reobj->zeroLastIndex(); 1.566 + return RegExpRunStatus_Success_NotFound; 1.567 + } 1.568 + 1.569 + /* Steps 8-21. */ 1.570 + const jschar *chars = input->chars(); 1.571 + size_t lastIndexInt(i); 1.572 + RegExpRunStatus status = 1.573 + ExecuteRegExpImpl(cx, res, *re, input, chars, length, &lastIndexInt, matches); 1.574 + 1.575 + if (status == RegExpRunStatus_Error) 1.576 + return RegExpRunStatus_Error; 1.577 + 1.578 + /* Steps 9a and 11 (with sticky extension). */ 1.579 + if (status == RegExpRunStatus_Success_NotFound) 1.580 + reobj->zeroLastIndex(); 1.581 + else if (re->global() || re->sticky()) 1.582 + reobj->setLastIndex(lastIndexInt); 1.583 + 1.584 + return status; 1.585 +} 1.586 + 1.587 +/* ES5 15.10.6.2 (and 15.10.6.3, which calls 15.10.6.2). */ 1.588 +static RegExpRunStatus 1.589 +ExecuteRegExp(JSContext *cx, CallArgs args, MatchConduit &matches) 1.590 +{ 1.591 + /* Step 1 (a) was performed by CallNonGenericMethod. */ 1.592 + RootedObject regexp(cx, &args.thisv().toObject()); 1.593 + 1.594 + /* Step 2. */ 1.595 + RootedString string(cx, ToString<CanGC>(cx, args.get(0))); 1.596 + if (!string) 1.597 + return RegExpRunStatus_Error; 1.598 + 1.599 + return ExecuteRegExp(cx, regexp, string, matches, UpdateRegExpStatics); 1.600 +} 1.601 + 1.602 +/* ES5 15.10.6.2. */ 1.603 +static bool 1.604 +regexp_exec_impl(JSContext *cx, HandleObject regexp, HandleString string, 1.605 + RegExpStaticsUpdate staticsUpdate, MutableHandleValue rval) 1.606 +{ 1.607 + /* Execute regular expression and gather matches. */ 1.608 + ScopedMatchPairs matches(&cx->tempLifoAlloc()); 1.609 + MatchConduit conduit(&matches); 1.610 + 1.611 + RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, conduit, staticsUpdate); 1.612 + 1.613 + if (status == RegExpRunStatus_Error) 1.614 + return false; 1.615 + 1.616 + if (status == RegExpRunStatus_Success_NotFound) { 1.617 + rval.setNull(); 1.618 + return true; 1.619 + } 1.620 + 1.621 + return CreateRegExpMatchResult(cx, string, matches, rval); 1.622 +} 1.623 + 1.624 +static bool 1.625 +regexp_exec_impl(JSContext *cx, CallArgs args) 1.626 +{ 1.627 + RootedObject regexp(cx, &args.thisv().toObject()); 1.628 + RootedString string(cx, ToString<CanGC>(cx, args.get(0))); 1.629 + if (!string) 1.630 + return false; 1.631 + 1.632 + return regexp_exec_impl(cx, regexp, string, UpdateRegExpStatics, args.rval()); 1.633 +} 1.634 + 1.635 +bool 1.636 +js::regexp_exec(JSContext *cx, unsigned argc, Value *vp) 1.637 +{ 1.638 + CallArgs args = CallArgsFromVp(argc, vp); 1.639 + return CallNonGenericMethod(cx, IsRegExp, regexp_exec_impl, args); 1.640 +} 1.641 + 1.642 +/* Separate interface for use by IonMonkey. */ 1.643 +bool 1.644 +js::regexp_exec_raw(JSContext *cx, HandleObject regexp, HandleString input, MutableHandleValue output) 1.645 +{ 1.646 + return regexp_exec_impl(cx, regexp, input, UpdateRegExpStatics, output); 1.647 +} 1.648 + 1.649 +bool 1.650 +js::regexp_exec_no_statics(JSContext *cx, unsigned argc, Value *vp) 1.651 +{ 1.652 + CallArgs args = CallArgsFromVp(argc, vp); 1.653 + JS_ASSERT(args.length() == 2); 1.654 + JS_ASSERT(IsRegExp(args[0])); 1.655 + JS_ASSERT(args[1].isString()); 1.656 + 1.657 + RootedObject regexp(cx, &args[0].toObject()); 1.658 + RootedString string(cx, args[1].toString()); 1.659 + 1.660 + return regexp_exec_impl(cx, regexp, string, DontUpdateRegExpStatics, args.rval()); 1.661 +} 1.662 + 1.663 +/* ES5 15.10.6.3. */ 1.664 +static bool 1.665 +regexp_test_impl(JSContext *cx, CallArgs args) 1.666 +{ 1.667 + MatchPair match; 1.668 + MatchConduit conduit(&match); 1.669 + RegExpRunStatus status = ExecuteRegExp(cx, args, conduit); 1.670 + args.rval().setBoolean(status == RegExpRunStatus_Success); 1.671 + return status != RegExpRunStatus_Error; 1.672 +} 1.673 + 1.674 +/* Separate interface for use by IonMonkey. */ 1.675 +bool 1.676 +js::regexp_test_raw(JSContext *cx, HandleObject regexp, HandleString input, bool *result) 1.677 +{ 1.678 + MatchPair match; 1.679 + MatchConduit conduit(&match); 1.680 + RegExpRunStatus status = ExecuteRegExp(cx, regexp, input, conduit, UpdateRegExpStatics); 1.681 + *result = (status == RegExpRunStatus_Success); 1.682 + return status != RegExpRunStatus_Error; 1.683 +} 1.684 + 1.685 +bool 1.686 +js::regexp_test(JSContext *cx, unsigned argc, Value *vp) 1.687 +{ 1.688 + CallArgs args = CallArgsFromVp(argc, vp); 1.689 + return CallNonGenericMethod(cx, IsRegExp, regexp_test_impl, args); 1.690 +} 1.691 + 1.692 +bool 1.693 +js::regexp_test_no_statics(JSContext *cx, unsigned argc, Value *vp) 1.694 +{ 1.695 + CallArgs args = CallArgsFromVp(argc, vp); 1.696 + JS_ASSERT(args.length() == 2); 1.697 + JS_ASSERT(IsRegExp(args[0])); 1.698 + JS_ASSERT(args[1].isString()); 1.699 + 1.700 + RootedObject regexp(cx, &args[0].toObject()); 1.701 + RootedString string(cx, args[1].toString()); 1.702 + 1.703 + MatchPair match; 1.704 + MatchConduit conduit(&match); 1.705 + RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, conduit, DontUpdateRegExpStatics); 1.706 + args.rval().setBoolean(status == RegExpRunStatus_Success); 1.707 + return status != RegExpRunStatus_Error; 1.708 +}