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.

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

mercurial