LCOV - code coverage report
Current view: top level - js/src/builtin - RegExp.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 254 784 32.4 %
Date: 2017-07-14 16:53:18 Functions: 25 76 32.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "builtin/RegExp.h"
       8             : 
       9             : #include "mozilla/CheckedInt.h"
      10             : #include "mozilla/TypeTraits.h"
      11             : 
      12             : #include "jscntxt.h"
      13             : 
      14             : #include "frontend/TokenStream.h"
      15             : #include "irregexp/RegExpParser.h"
      16             : #include "jit/InlinableNatives.h"
      17             : #include "vm/RegExpStatics.h"
      18             : #include "vm/SelfHosting.h"
      19             : #include "vm/StringBuffer.h"
      20             : #include "vm/Unicode.h"
      21             : 
      22             : #include "jsobjinlines.h"
      23             : 
      24             : #include "vm/NativeObject-inl.h"
      25             : 
      26             : using namespace js;
      27             : using namespace js::unicode;
      28             : 
      29             : using mozilla::ArrayLength;
      30             : using mozilla::CheckedInt;
      31             : using mozilla::Maybe;
      32             : 
      33             : /*
      34             :  * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
      35             :  * steps 3, 16-25.
      36             :  */
      37             : bool
      38          57 : js::CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs& matches,
      39             :                             MutableHandleValue rval)
      40             : {
      41          57 :     MOZ_ASSERT(input);
      42             : 
      43             :     /*
      44             :      * Create the (slow) result array for a match.
      45             :      *
      46             :      * Array contents:
      47             :      *  0:              matched string
      48             :      *  1..pairCount-1: paren matches
      49             :      *  input:          input string
      50             :      *  index:          start index for the match
      51             :      */
      52             : 
      53             :     /* Get the templateObject that defines the shape and type of the output object */
      54          57 :     JSObject* templateObject = cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx);
      55          57 :     if (!templateObject)
      56           0 :         return false;
      57             : 
      58          57 :     size_t numPairs = matches.length();
      59          57 :     MOZ_ASSERT(numPairs > 0);
      60             : 
      61             :     /* Step 17. */
      62         114 :     RootedArrayObject arr(cx, NewDenseFullyAllocatedArrayWithTemplate(cx, numPairs, templateObject));
      63          57 :     if (!arr)
      64           0 :         return false;
      65             : 
      66             :     /* Steps 22-24.
      67             :      * Store a Value for each pair. */
      68         189 :     for (size_t i = 0; i < numPairs; i++) {
      69         132 :         const MatchPair& pair = matches[i];
      70             : 
      71         132 :         if (pair.isUndefined()) {
      72          14 :             MOZ_ASSERT(i != 0); /* Since we had a match, first pair must be present. */
      73          14 :             arr->setDenseInitializedLength(i + 1);
      74          14 :             arr->initDenseElement(i, UndefinedValue());
      75             :         } else {
      76         118 :             JSLinearString* str = NewDependentString(cx, input, pair.start, pair.length());
      77         118 :             if (!str)
      78           0 :                 return false;
      79         118 :             arr->setDenseInitializedLength(i + 1);
      80         118 :             arr->initDenseElement(i, StringValue(str));
      81             :         }
      82             :     }
      83             : 
      84             :     /* Step 20 (reordered).
      85             :      * Set the |index| property. (TemplateObject positions it in slot 0) */
      86          57 :     arr->setSlot(0, Int32Value(matches[0].start));
      87             : 
      88             :     /* Step 21 (reordered).
      89             :      * Set the |input| property. (TemplateObject positions it in slot 1) */
      90          57 :     arr->setSlot(1, StringValue(input));
      91             : 
      92             : #ifdef DEBUG
      93         114 :     RootedValue test(cx);
      94         114 :     RootedId id(cx, NameToId(cx->names().index));
      95          57 :     if (!NativeGetProperty(cx, arr, id, &test))
      96           0 :         return false;
      97          57 :     MOZ_ASSERT(test == arr->getSlot(0));
      98          57 :     id = NameToId(cx->names().input);
      99          57 :     if (!NativeGetProperty(cx, arr, id, &test))
     100           0 :         return false;
     101          57 :     MOZ_ASSERT(test == arr->getSlot(1));
     102             : #endif
     103             : 
     104             :     /* Step 25. */
     105          57 :     rval.setObject(*arr);
     106          57 :     return true;
     107             : }
     108             : 
     109             : static int32_t
     110           3 : CreateRegExpSearchResult(JSContext* cx, const MatchPairs& matches)
     111             : {
     112             :     /* Fit the start and limit of match into a int32_t. */
     113           3 :     uint32_t position = matches[0].start;
     114           3 :     uint32_t lastIndex = matches[0].limit;
     115           3 :     MOZ_ASSERT(position < 0x8000);
     116           3 :     MOZ_ASSERT(lastIndex < 0x8000);
     117           3 :     return position | (lastIndex << 15);
     118             : }
     119             : 
     120             : /*
     121             :  * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
     122             :  * steps 3, 9-14, except 12.a.i, 12.c.i.1.
     123             :  */
     124             : static RegExpRunStatus
     125         258 : ExecuteRegExpImpl(JSContext* cx, RegExpStatics* res, MutableHandleRegExpShared re,
     126             :                   HandleLinearString input, size_t searchIndex, MatchPairs* matches,
     127             :                   size_t* endIndex)
     128             : {
     129         258 :     RegExpRunStatus status = RegExpShared::execute(cx, re, input, searchIndex, matches, endIndex);
     130             : 
     131             :     /* Out of spec: Update RegExpStatics. */
     132         258 :     if (status == RegExpRunStatus_Success && res) {
     133         122 :         if (matches) {
     134          60 :             if (!res->updateFromMatchPairs(cx, input, *matches))
     135           0 :                 return RegExpRunStatus_Error;
     136             :         } else {
     137          62 :             res->updateLazily(cx, input, re, searchIndex);
     138             :         }
     139             :     }
     140         258 :     return status;
     141             : }
     142             : 
     143             : /* Legacy ExecuteRegExp behavior is baked into the JSAPI. */
     144             : bool
     145           0 : js::ExecuteRegExpLegacy(JSContext* cx, RegExpStatics* res, Handle<RegExpObject*> reobj,
     146             :                         HandleLinearString input, size_t* lastIndex, bool test,
     147             :                         MutableHandleValue rval)
     148             : {
     149           0 :     RootedRegExpShared shared(cx, RegExpObject::getShared(cx, reobj));
     150           0 :     if (!shared)
     151           0 :         return false;
     152             : 
     153           0 :     ScopedMatchPairs matches(&cx->tempLifoAlloc());
     154             : 
     155           0 :     RegExpRunStatus status = ExecuteRegExpImpl(cx, res, &shared, input, *lastIndex,
     156           0 :                                                &matches, nullptr);
     157           0 :     if (status == RegExpRunStatus_Error)
     158           0 :         return false;
     159             : 
     160           0 :     if (status == RegExpRunStatus_Success_NotFound) {
     161             :         /* ExecuteRegExp() previously returned an array or null. */
     162           0 :         rval.setNull();
     163           0 :         return true;
     164             :     }
     165             : 
     166           0 :     *lastIndex = matches[0].limit;
     167             : 
     168           0 :     if (test) {
     169             :         /* Forbid an array, as an optimization. */
     170           0 :         rval.setBoolean(true);
     171           0 :         return true;
     172             :     }
     173             : 
     174           0 :     return CreateRegExpMatchResult(cx, input, matches, rval);
     175             : }
     176             : 
     177             : static bool
     178           2 : CheckPatternSyntax(JSContext* cx, HandleAtom pattern, RegExpFlag flags)
     179             : {
     180           4 :     CompileOptions options(cx);
     181           4 :     frontend::TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
     182           4 :     return irregexp::ParsePatternSyntax(dummyTokenStream, cx->tempLifoAlloc(), pattern,
     183           8 :                                         flags & UnicodeFlag);
     184             : }
     185             : 
     186             : enum RegExpSharedUse {
     187             :     UseRegExpShared,
     188             :     DontUseRegExpShared
     189             : };
     190             : 
     191             : /*
     192             :  * ES 2016 draft Mar 25, 2016 21.2.3.2.2.
     193             :  *
     194             :  * Steps 14-15 set |obj|'s "lastIndex" property to zero.  Some of
     195             :  * RegExpInitialize's callers have a fresh RegExp not yet exposed to script:
     196             :  * in these cases zeroing "lastIndex" is infallible.  But others have a RegExp
     197             :  * whose "lastIndex" property might have been made non-writable: here, zeroing
     198             :  * "lastIndex" can fail.  We efficiently solve this problem by completely
     199             :  * removing "lastIndex" zeroing from the provided function.
     200             :  *
     201             :  * CALLERS MUST HANDLE "lastIndex" ZEROING THEMSELVES!
     202             :  *
     203             :  * Because this function only ever returns a user-provided |obj| in the spec,
     204             :  * we omit it and just return the usual success/failure.
     205             :  */
     206             : static bool
     207           2 : RegExpInitializeIgnoringLastIndex(JSContext* cx, Handle<RegExpObject*> obj,
     208             :                                   HandleValue patternValue, HandleValue flagsValue,
     209             :                                   RegExpSharedUse sharedUse = DontUseRegExpShared)
     210             : {
     211           4 :     RootedAtom pattern(cx);
     212           2 :     if (patternValue.isUndefined()) {
     213             :         /* Step 1. */
     214           0 :         pattern = cx->names().empty;
     215             :     } else {
     216             :         /* Step 2. */
     217           2 :         pattern = ToAtom<CanGC>(cx, patternValue);
     218           2 :         if (!pattern)
     219           0 :             return false;
     220             :     }
     221             : 
     222             :     /* Step 3. */
     223           2 :     RegExpFlag flags = RegExpFlag(0);
     224           2 :     if (!flagsValue.isUndefined()) {
     225             :         /* Step 4. */
     226           2 :         RootedString flagStr(cx, ToString<CanGC>(cx, flagsValue));
     227           1 :         if (!flagStr)
     228           0 :             return false;
     229             : 
     230             :         /* Step 5. */
     231           1 :         if (!ParseRegExpFlags(cx, flagStr, &flags))
     232           0 :             return false;
     233             :     }
     234             : 
     235           2 :     if (sharedUse == UseRegExpShared) {
     236             :         /* Steps 7-8. */
     237           0 :         RegExpShared* re = cx->zone()->regExps.get(cx, pattern, flags);
     238           0 :         if (!re)
     239           0 :             return false;
     240             : 
     241             :         /* Steps 9-12. */
     242           0 :         obj->initIgnoringLastIndex(pattern, flags);
     243             : 
     244           0 :         obj->setShared(*re);
     245             :     } else {
     246             :         /* Steps 7-8. */
     247           2 :         if (!CheckPatternSyntax(cx, pattern, flags))
     248           0 :             return false;
     249             : 
     250             :         /* Steps 9-12. */
     251           2 :         obj->initIgnoringLastIndex(pattern, flags);
     252             :     }
     253             : 
     254           2 :     return true;
     255             : }
     256             : 
     257             : /* ES 2016 draft Mar 25, 2016 21.2.3.2.3. */
     258             : bool
     259           0 : js::RegExpCreate(JSContext* cx, HandleValue patternValue, HandleValue flagsValue,
     260             :                  MutableHandleValue rval)
     261             : {
     262             :     /* Step 1. */
     263           0 :     Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx, GenericObject));
     264           0 :     if (!regexp)
     265           0 :          return false;
     266             : 
     267             :     /* Step 2. */
     268           0 :     if (!RegExpInitializeIgnoringLastIndex(cx, regexp, patternValue, flagsValue, UseRegExpShared))
     269           0 :         return false;
     270           0 :     regexp->zeroLastIndex(cx);
     271             : 
     272           0 :     rval.setObject(*regexp);
     273           0 :     return true;
     274             : }
     275             : 
     276             : MOZ_ALWAYS_INLINE bool
     277         258 : IsRegExpObject(HandleValue v)
     278             : {
     279         258 :     return v.isObject() && v.toObject().is<RegExpObject>();
     280             : }
     281             : 
     282             : /* ES6 draft rc3 7.2.8. */
     283             : bool
     284         570 : js::IsRegExp(JSContext* cx, HandleValue value, bool* result)
     285             : {
     286             :     /* Step 1. */
     287         570 :     if (!value.isObject()) {
     288         570 :         *result = false;
     289         570 :         return true;
     290             :     }
     291           0 :     RootedObject obj(cx, &value.toObject());
     292             : 
     293             :     /* Steps 2-3. */
     294           0 :     RootedValue isRegExp(cx);
     295           0 :     RootedId matchId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().match));
     296           0 :     if (!GetProperty(cx, obj, obj, matchId, &isRegExp))
     297           0 :         return false;
     298             : 
     299             :     /* Step 4. */
     300           0 :     if (!isRegExp.isUndefined()) {
     301           0 :         *result = ToBoolean(isRegExp);
     302           0 :         return true;
     303             :     }
     304             : 
     305             :     /* Steps 5-6. */
     306             :     ESClass cls;
     307           0 :     if (!GetClassOfValue(cx, value, &cls))
     308           0 :         return false;
     309             : 
     310           0 :     *result = cls == ESClass::RegExp;
     311           0 :     return true;
     312             : }
     313             : 
     314             : /* ES6 B.2.5.1. */
     315             : MOZ_ALWAYS_INLINE bool
     316           0 : regexp_compile_impl(JSContext* cx, const CallArgs& args)
     317             : {
     318           0 :     MOZ_ASSERT(IsRegExpObject(args.thisv()));
     319             : 
     320           0 :     Rooted<RegExpObject*> regexp(cx, &args.thisv().toObject().as<RegExpObject>());
     321             : 
     322             :     // Step 3.
     323           0 :     RootedValue patternValue(cx, args.get(0));
     324             :     ESClass cls;
     325           0 :     if (!GetClassOfValue(cx, patternValue, &cls))
     326           0 :         return false;
     327           0 :     if (cls == ESClass::RegExp) {
     328             :         // Step 3a.
     329           0 :         if (args.hasDefined(1)) {
     330           0 :             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NEWREGEXP_FLAGGED);
     331           0 :             return false;
     332             :         }
     333             : 
     334             :         // Beware!  |patternObj| might be a proxy into another compartment, so
     335             :         // don't assume |patternObj.is<RegExpObject>()|.  For the same reason,
     336             :         // don't reuse the RegExpShared below.
     337           0 :         RootedObject patternObj(cx, &patternValue.toObject());
     338             : 
     339           0 :         RootedAtom sourceAtom(cx);
     340             :         RegExpFlag flags;
     341             :         {
     342             :             // Step 3b.
     343           0 :             RegExpShared* shared = RegExpToShared(cx, patternObj);
     344           0 :             if (!shared)
     345           0 :                 return false;
     346             : 
     347           0 :             sourceAtom = shared->getSource();
     348           0 :             flags = shared->getFlags();
     349             :         }
     350             : 
     351             :         // Step 5, minus lastIndex zeroing.
     352           0 :         regexp->initIgnoringLastIndex(sourceAtom, flags);
     353             :     } else {
     354             :         // Step 4.
     355           0 :         RootedValue P(cx, patternValue);
     356           0 :         RootedValue F(cx, args.get(1));
     357             : 
     358             :         // Step 5, minus lastIndex zeroing.
     359           0 :         if (!RegExpInitializeIgnoringLastIndex(cx, regexp, P, F))
     360           0 :             return false;
     361             :     }
     362             : 
     363             :     // The final niggling bit of step 5.
     364             :     //
     365             :     // |regexp| is user-exposed, but if its "lastIndex" property hasn't been
     366             :     // made non-writable, we can still use a fast path to zero it.
     367           0 :     if (regexp->lookupPure(cx->names().lastIndex)->writable()) {
     368           0 :         regexp->zeroLastIndex(cx);
     369             :     } else {
     370           0 :         RootedValue zero(cx, Int32Value(0));
     371           0 :         if (!SetProperty(cx, regexp, cx->names().lastIndex, zero))
     372           0 :             return false;
     373             :     }
     374             : 
     375           0 :     args.rval().setObject(*regexp);
     376           0 :     return true;
     377             : }
     378             : 
     379             : static bool
     380           0 : regexp_compile(JSContext* cx, unsigned argc, Value* vp)
     381             : {
     382           0 :     CallArgs args = CallArgsFromVp(argc, vp);
     383             : 
     384             :     /* Steps 1-2. */
     385           0 :     return CallNonGenericMethod<IsRegExpObject, regexp_compile_impl>(cx, args);
     386             : }
     387             : 
     388             : /*
     389             :  * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.3.1.
     390             :  */
     391             : bool
     392           2 : js::regexp_construct(JSContext* cx, unsigned argc, Value* vp)
     393             : {
     394           2 :     CallArgs args = CallArgsFromVp(argc, vp);
     395             : 
     396             :     // Steps 1.
     397             :     bool patternIsRegExp;
     398           2 :     if (!IsRegExp(cx, args.get(0), &patternIsRegExp))
     399           0 :         return false;
     400             : 
     401             :     // We can delay step 3 and step 4a until later, during
     402             :     // GetPrototypeFromBuiltinConstructor calls. Accessing the new.target
     403             :     // and the callee from the stack is unobservable.
     404           2 :     if (!args.isConstructing()) {
     405             :         // Step 3.b.
     406           0 :         if (patternIsRegExp && !args.hasDefined(1)) {
     407           0 :             RootedObject patternObj(cx, &args[0].toObject());
     408             : 
     409             :             // Step 3.b.i.
     410           0 :             RootedValue patternConstructor(cx);
     411           0 :             if (!GetProperty(cx, patternObj, patternObj, cx->names().constructor, &patternConstructor))
     412           0 :                 return false;
     413             : 
     414             :             // Step 3.b.ii.
     415           0 :             if (patternConstructor.isObject() && patternConstructor.toObject() == args.callee()) {
     416           0 :                 args.rval().set(args[0]);
     417           0 :                 return true;
     418             :             }
     419             :         }
     420             :     }
     421             : 
     422           4 :     RootedValue patternValue(cx, args.get(0));
     423             : 
     424             :     // Step 4.
     425             :     ESClass cls;
     426           2 :     if (!GetClassOfValue(cx, patternValue, &cls))
     427           0 :         return false;
     428           2 :     if (cls == ESClass::RegExp) {
     429             :         // Beware!  |patternObj| might be a proxy into another compartment, so
     430             :         // don't assume |patternObj.is<RegExpObject>()|.  For the same reason,
     431             :         // don't reuse the RegExpShared below.
     432           0 :         RootedObject patternObj(cx, &patternValue.toObject());
     433             : 
     434           0 :         RootedAtom sourceAtom(cx);
     435             :         RegExpFlag flags;
     436             :         {
     437             :             // Step 4.a.
     438           0 :             RegExpShared* shared = RegExpToShared(cx, patternObj);
     439           0 :             if (!shared)
     440           0 :                 return false;
     441           0 :             sourceAtom = shared->getSource();
     442             : 
     443             :             // Step 4.b.
     444             :             // Get original flags in all cases, to compare with passed flags.
     445           0 :             flags = shared->getFlags();
     446             :         }
     447             : 
     448             :         // Step 7.
     449           0 :         RootedObject proto(cx);
     450           0 :         if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
     451           0 :             return false;
     452             : 
     453           0 :         Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx, GenericObject, proto));
     454           0 :         if (!regexp)
     455           0 :             return false;
     456             : 
     457             :         // Step 8.
     458           0 :         if (args.hasDefined(1)) {
     459             :             // Step 4.c / 21.2.3.2.2 RegExpInitialize step 4.
     460           0 :             RegExpFlag flagsArg = RegExpFlag(0);
     461           0 :             RootedString flagStr(cx, ToString<CanGC>(cx, args[1]));
     462           0 :             if (!flagStr)
     463           0 :                 return false;
     464           0 :             if (!ParseRegExpFlags(cx, flagStr, &flagsArg))
     465           0 :                 return false;
     466             : 
     467           0 :             if (!(flags & UnicodeFlag) && flagsArg & UnicodeFlag) {
     468             :                 // Have to check syntax again when adding 'u' flag.
     469             : 
     470             :                 // ES 2017 draft rev 9b49a888e9dfe2667008a01b2754c3662059ae56
     471             :                 // 21.2.3.2.2 step 7.
     472           0 :                 if (!CheckPatternSyntax(cx, sourceAtom, flagsArg))
     473           0 :                     return false;
     474             :             }
     475           0 :             flags = flagsArg;
     476             :         }
     477             : 
     478           0 :         regexp->initAndZeroLastIndex(sourceAtom, flags, cx);
     479             : 
     480           0 :         args.rval().setObject(*regexp);
     481           0 :         return true;
     482             :     }
     483             : 
     484           4 :     RootedValue P(cx);
     485           4 :     RootedValue F(cx);
     486             : 
     487             :     // Step 5.
     488           2 :     if (patternIsRegExp) {
     489           0 :         RootedObject patternObj(cx, &patternValue.toObject());
     490             : 
     491             :         // Step 5.a.
     492           0 :         if (!GetProperty(cx, patternObj, patternObj, cx->names().source, &P))
     493           0 :             return false;
     494             : 
     495             :         // Step 5.b.
     496           0 :         F = args.get(1);
     497           0 :         if (F.isUndefined()) {
     498           0 :             if (!GetProperty(cx, patternObj, patternObj, cx->names().flags, &F))
     499           0 :                 return false;
     500             :         }
     501             :     } else {
     502             :         // Steps 6.a-b.
     503           2 :         P = patternValue;
     504           2 :         F = args.get(1);
     505             :     }
     506             : 
     507             :     // Step 7.
     508           4 :     RootedObject proto(cx);
     509           2 :     if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
     510           0 :         return false;
     511             : 
     512           4 :     Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx, GenericObject, proto));
     513           2 :     if (!regexp)
     514           0 :         return false;
     515             : 
     516             :     // Step 8.
     517           2 :     if (!RegExpInitializeIgnoringLastIndex(cx, regexp, P, F))
     518           0 :         return false;
     519           2 :     regexp->zeroLastIndex(cx);
     520             : 
     521           2 :     args.rval().setObject(*regexp);
     522           2 :     return true;
     523             : }
     524             : 
     525             : /*
     526             :  * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.3.1
     527             :  * steps 4, 7-8.
     528             :  */
     529             : bool
     530           0 : js::regexp_construct_raw_flags(JSContext* cx, unsigned argc, Value* vp)
     531             : {
     532           0 :     CallArgs args = CallArgsFromVp(argc, vp);
     533           0 :     MOZ_ASSERT(args.length() == 2);
     534           0 :     MOZ_ASSERT(!args.isConstructing());
     535             : 
     536             :     // Step 4.a.
     537           0 :     RootedAtom sourceAtom(cx, AtomizeString(cx, args[0].toString()));
     538           0 :     if (!sourceAtom)
     539           0 :         return false;
     540             : 
     541             :     // Step 4.c.
     542           0 :     int32_t flags = int32_t(args[1].toNumber());
     543             : 
     544             :     // Step 7.
     545           0 :     RegExpObject* regexp = RegExpAlloc(cx, GenericObject);
     546           0 :     if (!regexp)
     547           0 :         return false;
     548             : 
     549             :     // Step 8.
     550           0 :     regexp->initAndZeroLastIndex(sourceAtom, RegExpFlag(flags), cx);
     551           0 :     args.rval().setObject(*regexp);
     552           0 :     return true;
     553             : }
     554             : 
     555             : MOZ_ALWAYS_INLINE bool
     556           0 : IsRegExpPrototype(HandleValue v)
     557             : {
     558           0 :     if (IsRegExpObject(v) || !v.isObject())
     559           0 :         return false;
     560             : 
     561             :     // Note: The prototype shares its JSClass with instances.
     562           0 :     return StandardProtoKeyOrNull(&v.toObject()) == JSProto_RegExp;
     563             : }
     564             : 
     565             : // ES 2017 draft 21.2.5.4.
     566             : MOZ_ALWAYS_INLINE bool
     567           0 : regexp_global_impl(JSContext* cx, const CallArgs& args)
     568             : {
     569           0 :     MOZ_ASSERT(IsRegExpObject(args.thisv()));
     570             : 
     571             :     // Steps 4-6.
     572           0 :     RegExpObject* reObj = &args.thisv().toObject().as<RegExpObject>();
     573           0 :     args.rval().setBoolean(reObj->global());
     574           0 :     return true;
     575             : }
     576             : 
     577             : bool
     578           0 : js::regexp_global(JSContext* cx, unsigned argc, JS::Value* vp)
     579             : {
     580           0 :     CallArgs args = CallArgsFromVp(argc, vp);
     581             : 
     582             :     // Step 3.a.
     583           0 :     if (IsRegExpPrototype(args.thisv())) {
     584           0 :         args.rval().setUndefined();
     585           0 :         return true;
     586             :     }
     587             : 
     588             :     // Steps 1-3.
     589           0 :     return CallNonGenericMethod<IsRegExpObject, regexp_global_impl>(cx, args);
     590             : }
     591             : 
     592             : // ES 2017 draft 21.2.5.5.
     593             : MOZ_ALWAYS_INLINE bool
     594           0 : regexp_ignoreCase_impl(JSContext* cx, const CallArgs& args)
     595             : {
     596           0 :     MOZ_ASSERT(IsRegExpObject(args.thisv()));
     597             : 
     598             :     // Steps 4-6.
     599           0 :     RegExpObject* reObj = &args.thisv().toObject().as<RegExpObject>();
     600           0 :     args.rval().setBoolean(reObj->ignoreCase());
     601           0 :     return true;
     602             : }
     603             : 
     604             : bool
     605           0 : js::regexp_ignoreCase(JSContext* cx, unsigned argc, JS::Value* vp)
     606             : {
     607           0 :     CallArgs args = CallArgsFromVp(argc, vp);
     608             : 
     609             :     // Step 3.a.
     610           0 :     if (IsRegExpPrototype(args.thisv())) {
     611           0 :         args.rval().setUndefined();
     612           0 :         return true;
     613             :     }
     614             : 
     615             :     // Steps 1-3.
     616           0 :     return CallNonGenericMethod<IsRegExpObject, regexp_ignoreCase_impl>(cx, args);
     617             : }
     618             : 
     619             : // ES 2017 draft 21.2.5.7.
     620             : MOZ_ALWAYS_INLINE bool
     621           0 : regexp_multiline_impl(JSContext* cx, const CallArgs& args)
     622             : {
     623           0 :     MOZ_ASSERT(IsRegExpObject(args.thisv()));
     624             : 
     625             :     // Steps 4-6.
     626           0 :     RegExpObject* reObj = &args.thisv().toObject().as<RegExpObject>();
     627           0 :     args.rval().setBoolean(reObj->multiline());
     628           0 :     return true;
     629             : }
     630             : 
     631             : bool
     632           0 : js::regexp_multiline(JSContext* cx, unsigned argc, JS::Value* vp)
     633             : {
     634           0 :     CallArgs args = CallArgsFromVp(argc, vp);
     635             : 
     636             :     // Step 3.a.
     637           0 :     if (IsRegExpPrototype(args.thisv())) {
     638           0 :         args.rval().setUndefined();
     639           0 :         return true;
     640             :     }
     641             : 
     642             :     // Steps 1-3.
     643           0 :     return CallNonGenericMethod<IsRegExpObject, regexp_multiline_impl>(cx, args);
     644             : }
     645             : 
     646             : // ES 2017 draft 21.2.5.10.
     647             : MOZ_ALWAYS_INLINE bool
     648           0 : regexp_source_impl(JSContext* cx, const CallArgs& args)
     649             : {
     650           0 :     MOZ_ASSERT(IsRegExpObject(args.thisv()));
     651             : 
     652             :     // Step 5.
     653           0 :     RegExpObject* reObj = &args.thisv().toObject().as<RegExpObject>();
     654           0 :     RootedAtom src(cx, reObj->getSource());
     655           0 :     if (!src)
     656           0 :         return false;
     657             : 
     658             :     // Step 7.
     659           0 :     JSString* str = EscapeRegExpPattern(cx, src);
     660           0 :     if (!str)
     661           0 :         return false;
     662             : 
     663           0 :     args.rval().setString(str);
     664           0 :     return true;
     665             : }
     666             : 
     667             : static bool
     668           0 : regexp_source(JSContext* cx, unsigned argc, JS::Value* vp)
     669             : {
     670           0 :     CallArgs args = CallArgsFromVp(argc, vp);
     671             : 
     672             :     // Step 3.a.
     673           0 :     if (IsRegExpPrototype(args.thisv())) {
     674           0 :         args.rval().setString(cx->names().emptyRegExp);
     675           0 :         return true;
     676             :     }
     677             : 
     678             :     // Steps 1-4.
     679           0 :     return CallNonGenericMethod<IsRegExpObject, regexp_source_impl>(cx, args);
     680             : }
     681             : 
     682             : // ES 2017 draft 21.2.5.12.
     683             : MOZ_ALWAYS_INLINE bool
     684           0 : regexp_sticky_impl(JSContext* cx, const CallArgs& args)
     685             : {
     686           0 :     MOZ_ASSERT(IsRegExpObject(args.thisv()));
     687             : 
     688             :     // Steps 4-6.
     689           0 :     RegExpObject* reObj = &args.thisv().toObject().as<RegExpObject>();
     690           0 :     args.rval().setBoolean(reObj->sticky());
     691           0 :     return true;
     692             : }
     693             : 
     694             : bool
     695           0 : js::regexp_sticky(JSContext* cx, unsigned argc, JS::Value* vp)
     696             : {
     697           0 :     CallArgs args = CallArgsFromVp(argc, vp);
     698             : 
     699             :     // Step 3.a.
     700           0 :     if (IsRegExpPrototype(args.thisv())) {
     701           0 :         args.rval().setUndefined();
     702           0 :         return true;
     703             :     }
     704             : 
     705             :     // Steps 1-3.
     706           0 :     return CallNonGenericMethod<IsRegExpObject, regexp_sticky_impl>(cx, args);
     707             : }
     708             : 
     709             : // ES 2017 draft 21.2.5.15.
     710             : MOZ_ALWAYS_INLINE bool
     711           0 : regexp_unicode_impl(JSContext* cx, const CallArgs& args)
     712             : {
     713           0 :     MOZ_ASSERT(IsRegExpObject(args.thisv()));
     714             : 
     715             :     // Steps 4-6.
     716           0 :     RegExpObject* reObj = &args.thisv().toObject().as<RegExpObject>();
     717           0 :     args.rval().setBoolean(reObj->unicode());
     718           0 :     return true;
     719             : }
     720             : 
     721             : bool
     722           0 : js::regexp_unicode(JSContext* cx, unsigned argc, JS::Value* vp)
     723             : {
     724           0 :     CallArgs args = CallArgsFromVp(argc, vp);
     725             : 
     726             :     // Step 3.a.
     727           0 :     if (IsRegExpPrototype(args.thisv())) {
     728           0 :         args.rval().setUndefined();
     729           0 :         return true;
     730             :     }
     731             : 
     732             :     // Steps 1-3.
     733           0 :     return CallNonGenericMethod<IsRegExpObject, regexp_unicode_impl>(cx, args);
     734             : }
     735             : 
     736             : const JSPropertySpec js::regexp_properties[] = {
     737             :     JS_SELF_HOSTED_GET("flags", "RegExpFlagsGetter", 0),
     738             :     JS_PSG("global", regexp_global, 0),
     739             :     JS_PSG("ignoreCase", regexp_ignoreCase, 0),
     740             :     JS_PSG("multiline", regexp_multiline, 0),
     741             :     JS_PSG("source", regexp_source, 0),
     742             :     JS_PSG("sticky", regexp_sticky, 0),
     743             :     JS_PSG("unicode", regexp_unicode, 0),
     744             :     JS_PS_END
     745             : };
     746             : 
     747             : const JSFunctionSpec js::regexp_methods[] = {
     748             : #if JS_HAS_TOSOURCE
     749             :     JS_SELF_HOSTED_FN(js_toSource_str, "RegExpToString", 0, 0),
     750             : #endif
     751             :     JS_SELF_HOSTED_FN(js_toString_str, "RegExpToString", 0, 0),
     752             :     JS_FN("compile",        regexp_compile,     2,0),
     753             :     JS_SELF_HOSTED_FN("exec", "RegExp_prototype_Exec", 1,0),
     754             :     JS_SELF_HOSTED_FN("test", "RegExpTest" ,    1,0),
     755             :     JS_SELF_HOSTED_SYM_FN(match, "RegExpMatch", 1,0),
     756             :     JS_SELF_HOSTED_SYM_FN(replace, "RegExpReplace", 2,0),
     757             :     JS_SELF_HOSTED_SYM_FN(search, "RegExpSearch", 1,0),
     758             :     JS_SELF_HOSTED_SYM_FN(split, "RegExpSplit", 2,0),
     759             :     JS_FS_END
     760             : };
     761             : 
     762             : #define STATIC_PAREN_GETTER_CODE(parenNum)                                      \
     763             :     if (!res->createParen(cx, parenNum, args.rval()))                           \
     764             :         return false;                                                           \
     765             :     if (args.rval().isUndefined())                                              \
     766             :         args.rval().setString(cx->runtime()->emptyString);                      \
     767             :     return true
     768             : 
     769             : /*
     770             :  * RegExp static properties.
     771             :  *
     772             :  * RegExp class static properties and their Perl counterparts:
     773             :  *
     774             :  *  RegExp.input                $_
     775             :  *  RegExp.lastMatch            $&
     776             :  *  RegExp.lastParen            $+
     777             :  *  RegExp.leftContext          $`
     778             :  *  RegExp.rightContext         $'
     779             :  */
     780             : 
     781             : #define DEFINE_STATIC_GETTER(name, code)                                        \
     782             :     static bool                                                                 \
     783             :     name(JSContext* cx, unsigned argc, Value* vp)                               \
     784             :     {                                                                           \
     785             :         CallArgs args = CallArgsFromVp(argc, vp);                               \
     786             :         RegExpStatics* res = GlobalObject::getRegExpStatics(cx, cx->global());  \
     787             :         if (!res)                                                               \
     788             :             return false;                                                       \
     789             :         code;                                                                   \
     790             :     }
     791             : 
     792           0 : DEFINE_STATIC_GETTER(static_input_getter,        return res->createPendingInput(cx, args.rval()))
     793           0 : DEFINE_STATIC_GETTER(static_lastMatch_getter,    return res->createLastMatch(cx, args.rval()))
     794           0 : DEFINE_STATIC_GETTER(static_lastParen_getter,    return res->createLastParen(cx, args.rval()))
     795           0 : DEFINE_STATIC_GETTER(static_leftContext_getter,  return res->createLeftContext(cx, args.rval()))
     796           6 : DEFINE_STATIC_GETTER(static_rightContext_getter, return res->createRightContext(cx, args.rval()))
     797             : 
     798           0 : DEFINE_STATIC_GETTER(static_paren1_getter,       STATIC_PAREN_GETTER_CODE(1))
     799           0 : DEFINE_STATIC_GETTER(static_paren2_getter,       STATIC_PAREN_GETTER_CODE(2))
     800           0 : DEFINE_STATIC_GETTER(static_paren3_getter,       STATIC_PAREN_GETTER_CODE(3))
     801           0 : DEFINE_STATIC_GETTER(static_paren4_getter,       STATIC_PAREN_GETTER_CODE(4))
     802           0 : DEFINE_STATIC_GETTER(static_paren5_getter,       STATIC_PAREN_GETTER_CODE(5))
     803           0 : DEFINE_STATIC_GETTER(static_paren6_getter,       STATIC_PAREN_GETTER_CODE(6))
     804           0 : DEFINE_STATIC_GETTER(static_paren7_getter,       STATIC_PAREN_GETTER_CODE(7))
     805           0 : DEFINE_STATIC_GETTER(static_paren8_getter,       STATIC_PAREN_GETTER_CODE(8))
     806           0 : DEFINE_STATIC_GETTER(static_paren9_getter,       STATIC_PAREN_GETTER_CODE(9))
     807             : 
     808             : #define DEFINE_STATIC_SETTER(name, code)                                        \
     809             :     static bool                                                                 \
     810             :     name(JSContext* cx, unsigned argc, Value* vp)                               \
     811             :     {                                                                           \
     812             :         RegExpStatics* res = GlobalObject::getRegExpStatics(cx, cx->global());  \
     813             :         if (!res)                                                               \
     814             :             return false;                                                       \
     815             :         code;                                                                   \
     816             :         return true;                                                            \
     817             :     }
     818             : 
     819             : static bool
     820           0 : static_input_setter(JSContext* cx, unsigned argc, Value* vp)
     821             : {
     822           0 :     CallArgs args = CallArgsFromVp(argc, vp);
     823           0 :     RegExpStatics* res = GlobalObject::getRegExpStatics(cx, cx->global());
     824           0 :     if (!res)
     825           0 :         return false;
     826             : 
     827           0 :     RootedString str(cx, ToString<CanGC>(cx, args.get(0)));
     828           0 :     if (!str)
     829           0 :         return false;
     830             : 
     831           0 :     res->setPendingInput(str);
     832           0 :     args.rval().setString(str);
     833           0 :     return true;
     834             : }
     835             : 
     836             : const JSPropertySpec js::regexp_static_props[] = {
     837             :     JS_PSGS("input", static_input_getter, static_input_setter,
     838             :             JSPROP_PERMANENT | JSPROP_ENUMERATE),
     839             :     JS_PSG("lastMatch", static_lastMatch_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
     840             :     JS_PSG("lastParen", static_lastParen_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
     841             :     JS_PSG("leftContext",  static_leftContext_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
     842             :     JS_PSG("rightContext", static_rightContext_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
     843             :     JS_PSG("$1", static_paren1_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
     844             :     JS_PSG("$2", static_paren2_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
     845             :     JS_PSG("$3", static_paren3_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
     846             :     JS_PSG("$4", static_paren4_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
     847             :     JS_PSG("$5", static_paren5_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
     848             :     JS_PSG("$6", static_paren6_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
     849             :     JS_PSG("$7", static_paren7_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
     850             :     JS_PSG("$8", static_paren8_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
     851             :     JS_PSG("$9", static_paren9_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
     852             :     JS_PSGS("$_", static_input_getter, static_input_setter, JSPROP_PERMANENT),
     853             :     JS_PSG("$&", static_lastMatch_getter, JSPROP_PERMANENT),
     854             :     JS_PSG("$+", static_lastParen_getter, JSPROP_PERMANENT),
     855             :     JS_PSG("$`", static_leftContext_getter, JSPROP_PERMANENT),
     856             :     JS_PSG("$'", static_rightContext_getter, JSPROP_PERMANENT),
     857             :     JS_SELF_HOSTED_SYM_GET(species, "RegExpSpecies", 0),
     858             :     JS_PS_END
     859             : };
     860             : 
     861             : template <typename CharT>
     862             : static bool
     863           0 : IsTrailSurrogateWithLeadSurrogateImpl(JSContext* cx, HandleLinearString input, size_t index)
     864             : {
     865           0 :     JS::AutoCheckCannotGC nogc;
     866           0 :     MOZ_ASSERT(index > 0 && index < input->length());
     867           0 :     const CharT* inputChars = input->chars<CharT>(nogc);
     868             : 
     869           0 :     return unicode::IsTrailSurrogate(inputChars[index]) &&
     870           0 :            unicode::IsLeadSurrogate(inputChars[index - 1]);
     871             : }
     872             : 
     873             : static bool
     874           0 : IsTrailSurrogateWithLeadSurrogate(JSContext* cx, HandleLinearString input, int32_t index)
     875             : {
     876           0 :     if (index <= 0 || size_t(index) >= input->length())
     877           0 :         return false;
     878             : 
     879           0 :     return input->hasLatin1Chars()
     880           0 :            ? IsTrailSurrogateWithLeadSurrogateImpl<Latin1Char>(cx, input, index)
     881           0 :            : IsTrailSurrogateWithLeadSurrogateImpl<char16_t>(cx, input, index);
     882             : }
     883             : 
     884             : /*
     885             :  * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
     886             :  * steps 3, 9-14, except 12.a.i, 12.c.i.1.
     887             :  */
     888             : static RegExpRunStatus
     889         258 : ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
     890             :               int32_t lastIndex,
     891             :               MatchPairs* matches, size_t* endIndex, RegExpStaticsUpdate staticsUpdate)
     892             : {
     893             :     /*
     894             :      * WARNING: Despite the presence of spec step comment numbers, this
     895             :      *          algorithm isn't consistent with any ES6 version, draft or
     896             :      *          otherwise.  YOU HAVE BEEN WARNED.
     897             :      */
     898             : 
     899             :     /* Steps 1-2 performed by the caller. */
     900         516 :     Rooted<RegExpObject*> reobj(cx, &regexp->as<RegExpObject>());
     901             : 
     902         516 :     RootedRegExpShared re(cx, RegExpObject::getShared(cx, reobj));
     903         258 :     if (!re)
     904           0 :         return RegExpRunStatus_Error;
     905             : 
     906             :     RegExpStatics* res;
     907         258 :     if (staticsUpdate == UpdateRegExpStatics) {
     908         258 :         res = GlobalObject::getRegExpStatics(cx, cx->global());
     909         258 :         if (!res)
     910           0 :             return RegExpRunStatus_Error;
     911             :     } else {
     912           0 :         res = nullptr;
     913             :     }
     914             : 
     915         516 :     RootedLinearString input(cx, string->ensureLinear(cx));
     916         258 :     if (!input)
     917           0 :         return RegExpRunStatus_Error;
     918             : 
     919             :     /* Handled by caller */
     920         258 :     MOZ_ASSERT(lastIndex >= 0 && size_t(lastIndex) <= input->length());
     921             : 
     922             :     /* Steps 4-8 performed by the caller. */
     923             : 
     924             :     /* Step 10. */
     925         258 :     if (reobj->unicode()) {
     926             :         /*
     927             :          * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad
     928             :          * 21.2.2.2 step 2.
     929             :          *   Let listIndex be the index into Input of the character that was
     930             :          *   obtained from element index of str.
     931             :          *
     932             :          * In the spec, pattern match is performed with decoded Unicode code
     933             :          * points, but our implementation performs it with UTF-16 encoded
     934             :          * string.  In step 2, we should decrement lastIndex (index) if it
     935             :          * points the trail surrogate that has corresponding lead surrogate.
     936             :          *
     937             :          *   var r = /\uD83D\uDC38/ug;
     938             :          *   r.lastIndex = 1;
     939             :          *   var str = "\uD83D\uDC38";
     940             :          *   var result = r.exec(str); // pattern match starts from index 0
     941             :          *   print(result.index);      // prints 0
     942             :          *
     943             :          * Note: this doesn't match the current spec text and result in
     944             :          * different values for `result.index` under certain conditions.
     945             :          * However, the spec will change to match our implementation's
     946             :          * behavior. See https://github.com/tc39/ecma262/issues/128.
     947             :          */
     948           0 :         if (IsTrailSurrogateWithLeadSurrogate(cx, input, lastIndex))
     949           0 :             lastIndex--;
     950             :     }
     951             : 
     952             :     /* Steps 3, 11-14, except 12.a.i, 12.c.i.1. */
     953         258 :     RegExpRunStatus status = ExecuteRegExpImpl(cx, res, &re, input, lastIndex, matches, endIndex);
     954         258 :     if (status == RegExpRunStatus_Error)
     955           0 :         return RegExpRunStatus_Error;
     956             : 
     957             :     /* Steps 12.a.i, 12.c.i.i, 15 are done by Self-hosted function. */
     958             : 
     959         258 :     return status;
     960             : }
     961             : 
     962             : /*
     963             :  * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
     964             :  * steps 3, 9-25, except 12.a.i, 12.c.i.1, 15.
     965             :  */
     966             : static bool
     967         100 : RegExpMatcherImpl(JSContext* cx, HandleObject regexp, HandleString string,
     968             :                   int32_t lastIndex, RegExpStaticsUpdate staticsUpdate, MutableHandleValue rval)
     969             : {
     970             :     /* Execute regular expression and gather matches. */
     971         200 :     ScopedMatchPairs matches(&cx->tempLifoAlloc());
     972             : 
     973             :     /* Steps 3, 9-14, except 12.a.i, 12.c.i.1. */
     974             :     RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex,
     975         100 :                                            &matches, nullptr, staticsUpdate);
     976         100 :     if (status == RegExpRunStatus_Error)
     977           0 :         return false;
     978             : 
     979             :     /* Steps 12.a, 12.c. */
     980         100 :     if (status == RegExpRunStatus_Success_NotFound) {
     981          43 :         rval.setNull();
     982          43 :         return true;
     983             :     }
     984             : 
     985             :     /* Steps 16-25 */
     986          57 :     return CreateRegExpMatchResult(cx, string, matches, rval);
     987             : }
     988             : 
     989             : /*
     990             :  * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
     991             :  * steps 3, 9-25, except 12.a.i, 12.c.i.1, 15.
     992             :  */
     993             : bool
     994         100 : js::RegExpMatcher(JSContext* cx, unsigned argc, Value* vp)
     995             : {
     996         100 :     CallArgs args = CallArgsFromVp(argc, vp);
     997         100 :     MOZ_ASSERT(args.length() == 3);
     998         100 :     MOZ_ASSERT(IsRegExpObject(args[0]));
     999         100 :     MOZ_ASSERT(args[1].isString());
    1000         100 :     MOZ_ASSERT(args[2].isNumber());
    1001             : 
    1002         200 :     RootedObject regexp(cx, &args[0].toObject());
    1003         200 :     RootedString string(cx, args[1].toString());
    1004             : 
    1005             :     int32_t lastIndex;
    1006         100 :     MOZ_ALWAYS_TRUE(ToInt32(cx, args[2], &lastIndex));
    1007             : 
    1008             :     /* Steps 3, 9-25, except 12.a.i, 12.c.i.1, 15. */
    1009         200 :     return RegExpMatcherImpl(cx, regexp, string, lastIndex,
    1010         200 :                              UpdateRegExpStatics, args.rval());
    1011             : }
    1012             : 
    1013             : /*
    1014             :  * Separate interface for use by IonMonkey.
    1015             :  * This code cannot re-enter Ion code.
    1016             :  */
    1017             : bool
    1018           0 : js::RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
    1019             :                      int32_t lastIndex,
    1020             :                      MatchPairs* maybeMatches, MutableHandleValue output)
    1021             : {
    1022           0 :     MOZ_ASSERT(lastIndex >= 0);
    1023             : 
    1024             :     // The MatchPairs will always be passed in, but RegExp execution was
    1025             :     // successful only if the pairs have actually been filled in.
    1026           0 :     if (maybeMatches && maybeMatches->pairsRaw()[0] >= 0)
    1027           0 :         return CreateRegExpMatchResult(cx, input, *maybeMatches, output);
    1028             :     return RegExpMatcherImpl(cx, regexp, input, lastIndex,
    1029           0 :                              UpdateRegExpStatics, output);
    1030             : }
    1031             : 
    1032             : /*
    1033             :  * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
    1034             :  * steps 3, 9-25, except 12.a.i, 12.c.i.1, 15.
    1035             :  * This code is inlined in CodeGenerator.cpp generateRegExpSearcherStub,
    1036             :  * changes to this code need to get reflected in there too.
    1037             :  */
    1038             : static bool
    1039          24 : RegExpSearcherImpl(JSContext* cx, HandleObject regexp, HandleString string,
    1040             :                    int32_t lastIndex, RegExpStaticsUpdate staticsUpdate, int32_t* result)
    1041             : {
    1042             :     /* Execute regular expression and gather matches. */
    1043          48 :     ScopedMatchPairs matches(&cx->tempLifoAlloc());
    1044             : 
    1045             :     /* Steps 3, 9-14, except 12.a.i, 12.c.i.1. */
    1046             :     RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex,
    1047          24 :                                            &matches, nullptr, staticsUpdate);
    1048          24 :     if (status == RegExpRunStatus_Error)
    1049           0 :         return false;
    1050             : 
    1051             :     /* Steps 12.a, 12.c. */
    1052          24 :     if (status == RegExpRunStatus_Success_NotFound) {
    1053          21 :         *result = -1;
    1054          21 :         return true;
    1055             :     }
    1056             : 
    1057             :     /* Steps 16-25 */
    1058           3 :     *result = CreateRegExpSearchResult(cx, matches);
    1059           3 :     return true;
    1060             : }
    1061             : 
    1062             : /*
    1063             :  * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
    1064             :  * steps 3, 9-25, except 12.a.i, 12.c.i.1, 15.
    1065             :  */
    1066             : bool
    1067          24 : js::RegExpSearcher(JSContext* cx, unsigned argc, Value* vp)
    1068             : {
    1069          24 :     CallArgs args = CallArgsFromVp(argc, vp);
    1070          24 :     MOZ_ASSERT(args.length() == 3);
    1071          24 :     MOZ_ASSERT(IsRegExpObject(args[0]));
    1072          24 :     MOZ_ASSERT(args[1].isString());
    1073          24 :     MOZ_ASSERT(args[2].isNumber());
    1074             : 
    1075          48 :     RootedObject regexp(cx, &args[0].toObject());
    1076          48 :     RootedString string(cx, args[1].toString());
    1077             : 
    1078             :     int32_t lastIndex;
    1079          24 :     MOZ_ALWAYS_TRUE(ToInt32(cx, args[2], &lastIndex));
    1080             : 
    1081             :     /* Steps 3, 9-25, except 12.a.i, 12.c.i.1, 15. */
    1082          24 :     int32_t result = 0;
    1083          24 :     if (!RegExpSearcherImpl(cx, regexp, string, lastIndex, UpdateRegExpStatics, &result))
    1084           0 :         return false;
    1085             : 
    1086          24 :     args.rval().setInt32(result);
    1087          24 :     return true;
    1088             : }
    1089             : 
    1090             : /*
    1091             :  * Separate interface for use by IonMonkey.
    1092             :  * This code cannot re-enter Ion code.
    1093             :  */
    1094             : bool
    1095           0 : js::RegExpSearcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
    1096             :                       int32_t lastIndex, MatchPairs* maybeMatches, int32_t* result)
    1097             : {
    1098           0 :     MOZ_ASSERT(lastIndex >= 0);
    1099             : 
    1100             :     // The MatchPairs will always be passed in, but RegExp execution was
    1101             :     // successful only if the pairs have actually been filled in.
    1102           0 :     if (maybeMatches && maybeMatches->pairsRaw()[0] >= 0) {
    1103           0 :         *result = CreateRegExpSearchResult(cx, *maybeMatches);
    1104           0 :         return true;
    1105             :     }
    1106             :     return RegExpSearcherImpl(cx, regexp, input, lastIndex,
    1107           0 :                               UpdateRegExpStatics, result);
    1108             : }
    1109             : 
    1110             : bool
    1111           0 : js::regexp_exec_no_statics(JSContext* cx, unsigned argc, Value* vp)
    1112             : {
    1113           0 :     CallArgs args = CallArgsFromVp(argc, vp);
    1114           0 :     MOZ_ASSERT(args.length() == 2);
    1115           0 :     MOZ_ASSERT(IsRegExpObject(args[0]));
    1116           0 :     MOZ_ASSERT(args[1].isString());
    1117             : 
    1118           0 :     RootedObject regexp(cx, &args[0].toObject());
    1119           0 :     RootedString string(cx, args[1].toString());
    1120             : 
    1121           0 :     return RegExpMatcherImpl(cx, regexp, string, 0,
    1122           0 :                              DontUpdateRegExpStatics, args.rval());
    1123             : }
    1124             : 
    1125             : /*
    1126             :  * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
    1127             :  * steps 3, 9-14, except 12.a.i, 12.c.i.1.
    1128             :  */
    1129             : bool
    1130         134 : js::RegExpTester(JSContext* cx, unsigned argc, Value* vp)
    1131             : {
    1132         134 :     CallArgs args = CallArgsFromVp(argc, vp);
    1133         134 :     MOZ_ASSERT(args.length() == 3);
    1134         134 :     MOZ_ASSERT(IsRegExpObject(args[0]));
    1135         134 :     MOZ_ASSERT(args[1].isString());
    1136         134 :     MOZ_ASSERT(args[2].isNumber());
    1137             : 
    1138         268 :     RootedObject regexp(cx, &args[0].toObject());
    1139         268 :     RootedString string(cx, args[1].toString());
    1140             : 
    1141             :     int32_t lastIndex;
    1142         134 :     MOZ_ALWAYS_TRUE(ToInt32(cx, args[2], &lastIndex));
    1143             : 
    1144             :     /* Steps 3, 9-14, except 12.a.i, 12.c.i.1. */
    1145         134 :     size_t endIndex = 0;
    1146         268 :     RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex,
    1147         134 :                                            nullptr, &endIndex, UpdateRegExpStatics);
    1148             : 
    1149         134 :     if (status == RegExpRunStatus_Error)
    1150           0 :         return false;
    1151             : 
    1152         134 :     if (status == RegExpRunStatus_Success) {
    1153          62 :         MOZ_ASSERT(endIndex <= INT32_MAX);
    1154          62 :         args.rval().setInt32(int32_t(endIndex));
    1155             :     } else {
    1156          72 :         args.rval().setInt32(-1);
    1157             :     }
    1158         134 :     return true;
    1159             : }
    1160             : 
    1161             : /*
    1162             :  * Separate interface for use by IonMonkey.
    1163             :  * This code cannot re-enter Ion code.
    1164             :  */
    1165             : bool
    1166           0 : js::RegExpTesterRaw(JSContext* cx, HandleObject regexp, HandleString input,
    1167             :                     int32_t lastIndex, int32_t* endIndex)
    1168             : {
    1169           0 :     MOZ_ASSERT(lastIndex >= 0);
    1170             : 
    1171           0 :     size_t endIndexTmp = 0;
    1172             :     RegExpRunStatus status = ExecuteRegExp(cx, regexp, input, lastIndex,
    1173           0 :                                            nullptr, &endIndexTmp, UpdateRegExpStatics);
    1174             : 
    1175           0 :     if (status == RegExpRunStatus_Success) {
    1176           0 :         MOZ_ASSERT(endIndexTmp <= INT32_MAX);
    1177           0 :         *endIndex = int32_t(endIndexTmp);
    1178           0 :         return true;
    1179             :     }
    1180           0 :     if (status == RegExpRunStatus_Success_NotFound) {
    1181           0 :         *endIndex = -1;
    1182           0 :         return true;
    1183             :     }
    1184             : 
    1185           0 :     return false;
    1186             : }
    1187             : 
    1188             : bool
    1189           0 : js::regexp_test_no_statics(JSContext* cx, unsigned argc, Value* vp)
    1190             : {
    1191           0 :     CallArgs args = CallArgsFromVp(argc, vp);
    1192           0 :     MOZ_ASSERT(args.length() == 2);
    1193           0 :     MOZ_ASSERT(IsRegExpObject(args[0]));
    1194           0 :     MOZ_ASSERT(args[1].isString());
    1195             : 
    1196           0 :     RootedObject regexp(cx, &args[0].toObject());
    1197           0 :     RootedString string(cx, args[1].toString());
    1198             : 
    1199           0 :     size_t ignored = 0;
    1200           0 :     RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, 0,
    1201           0 :                                            nullptr, &ignored, DontUpdateRegExpStatics);
    1202           0 :     args.rval().setBoolean(status == RegExpRunStatus_Success);
    1203           0 :     return status != RegExpRunStatus_Error;
    1204             : }
    1205             : 
    1206             : static void
    1207           0 : GetParen(JSLinearString* matched, const JS::Value& capture, JSSubString* out)
    1208             : {
    1209           0 :     if (capture.isUndefined()) {
    1210           0 :         out->initEmpty(matched);
    1211           0 :         return;
    1212             :     }
    1213           0 :     JSLinearString& captureLinear = capture.toString()->asLinear();
    1214           0 :     out->init(&captureLinear, 0, captureLinear.length());
    1215             : }
    1216             : 
    1217             : template <typename CharT>
    1218             : static bool
    1219           0 : InterpretDollar(JSLinearString* matched, JSLinearString* string, size_t position, size_t tailPos,
    1220             :                 MutableHandle<GCVector<Value>> captures, JSLinearString* replacement,
    1221             :                 const CharT* replacementBegin, const CharT* currentDollar,
    1222             :                 const CharT* replacementEnd,
    1223             :                 JSSubString* out, size_t* skip)
    1224             : {
    1225           0 :     MOZ_ASSERT(*currentDollar == '$');
    1226             : 
    1227             :     /* If there is only a dollar, bail now. */
    1228           0 :     if (currentDollar + 1 >= replacementEnd)
    1229           0 :         return false;
    1230             : 
    1231             :     /* ES 2016 draft Mar 25, 2016 Table 46. */
    1232           0 :     char16_t c = currentDollar[1];
    1233           0 :     if (JS7_ISDEC(c)) {
    1234             :         /* $n, $nn */
    1235           0 :         unsigned num = JS7_UNDEC(c);
    1236           0 :         if (num > captures.length()) {
    1237             :             // The result is implementation-defined, do not substitute.
    1238           0 :             return false;
    1239             :         }
    1240             : 
    1241           0 :         const CharT* currentChar = currentDollar + 2;
    1242           0 :         if (currentChar < replacementEnd) {
    1243           0 :             c = *currentChar;
    1244           0 :             if (JS7_ISDEC(c)) {
    1245           0 :                 unsigned tmpNum = 10 * num + JS7_UNDEC(c);
    1246             :                 // If num > captures.length(), the result is implementation-defined.
    1247             :                 // Consume next character only if num <= captures.length().
    1248           0 :                 if (tmpNum <= captures.length()) {
    1249           0 :                     currentChar++;
    1250           0 :                     num = tmpNum;
    1251             :                 }
    1252             :             }
    1253             :         }
    1254             : 
    1255           0 :         if (num == 0) {
    1256             :             // The result is implementation-defined.
    1257             :             // Do not substitute.
    1258           0 :             return false;
    1259             :         }
    1260             : 
    1261           0 :         *skip = currentChar - currentDollar;
    1262             : 
    1263           0 :         MOZ_ASSERT(num <= captures.length());
    1264             : 
    1265           0 :         GetParen(matched, captures[num -1], out);
    1266           0 :         return true;
    1267             :     }
    1268             : 
    1269           0 :     *skip = 2;
    1270           0 :     switch (c) {
    1271             :       default:
    1272           0 :         return false;
    1273             :       case '$':
    1274           0 :         out->init(replacement, currentDollar - replacementBegin, 1);
    1275           0 :         break;
    1276             :       case '&':
    1277           0 :         out->init(matched, 0, matched->length());
    1278           0 :         break;
    1279             :       case '+':
    1280             :         // SpiderMonkey extension
    1281           0 :         if (captures.length() == 0)
    1282           0 :             out->initEmpty(matched);
    1283             :         else
    1284           0 :             GetParen(matched, captures[captures.length() - 1], out);
    1285           0 :         break;
    1286             :       case '`':
    1287           0 :         out->init(string, 0, position);
    1288           0 :         break;
    1289             :       case '\'':
    1290           0 :         out->init(string, tailPos, string->length() - tailPos);
    1291           0 :         break;
    1292             :     }
    1293           0 :     return true;
    1294             : }
    1295             : 
    1296             : template <typename CharT>
    1297             : static bool
    1298           0 : FindReplaceLengthString(JSContext* cx, HandleLinearString matched, HandleLinearString string,
    1299             :                         size_t position, size_t tailPos, MutableHandle<GCVector<Value>> captures,
    1300             :                         HandleLinearString replacement, size_t firstDollarIndex, size_t* sizep)
    1301             : {
    1302           0 :     CheckedInt<uint32_t> replen = replacement->length();
    1303             : 
    1304           0 :     JS::AutoCheckCannotGC nogc;
    1305           0 :     MOZ_ASSERT(firstDollarIndex < replacement->length());
    1306           0 :     const CharT* replacementBegin = replacement->chars<CharT>(nogc);
    1307           0 :     const CharT* currentDollar = replacementBegin + firstDollarIndex;
    1308           0 :     const CharT* replacementEnd = replacementBegin + replacement->length();
    1309           0 :     do {
    1310           0 :         JSSubString sub;
    1311             :         size_t skip;
    1312           0 :         if (InterpretDollar(matched, string, position, tailPos, captures, replacement,
    1313             :                             replacementBegin, currentDollar, replacementEnd, &sub, &skip))
    1314             :         {
    1315           0 :             if (sub.length > skip)
    1316           0 :                 replen += sub.length - skip;
    1317             :             else
    1318           0 :                 replen -= skip - sub.length;
    1319           0 :             currentDollar += skip;
    1320             :         } else {
    1321           0 :             currentDollar++;
    1322             :         }
    1323             : 
    1324           0 :         currentDollar = js_strchr_limit(currentDollar, '$', replacementEnd);
    1325             :     } while (currentDollar);
    1326             : 
    1327           0 :     if (!replen.isValid()) {
    1328           0 :         ReportAllocationOverflow(cx);
    1329           0 :         return false;
    1330             :     }
    1331             : 
    1332           0 :     *sizep = replen.value();
    1333           0 :     return true;
    1334             : }
    1335             : 
    1336             : static bool
    1337           0 : FindReplaceLength(JSContext* cx, HandleLinearString matched, HandleLinearString string,
    1338             :                   size_t position, size_t tailPos, MutableHandle<GCVector<Value>> captures,
    1339             :                   HandleLinearString replacement, size_t firstDollarIndex, size_t* sizep)
    1340             : {
    1341           0 :     return replacement->hasLatin1Chars()
    1342           0 :            ? FindReplaceLengthString<Latin1Char>(cx, matched, string, position, tailPos, captures,
    1343             :                                                  replacement, firstDollarIndex, sizep)
    1344             :            : FindReplaceLengthString<char16_t>(cx, matched, string, position, tailPos, captures,
    1345           0 :                                                replacement, firstDollarIndex, sizep);
    1346             : }
    1347             : 
    1348             : /*
    1349             :  * Precondition: |sb| already has necessary growth space reserved (as
    1350             :  * derived from FindReplaceLength), and has been inflated to TwoByte if
    1351             :  * necessary.
    1352             :  */
    1353             : template <typename CharT>
    1354             : static void
    1355           0 : DoReplace(HandleLinearString matched, HandleLinearString string,
    1356             :           size_t position, size_t tailPos, MutableHandle<GCVector<Value>> captures,
    1357             :           HandleLinearString replacement, size_t firstDollarIndex, StringBuffer &sb)
    1358             : {
    1359           0 :     JS::AutoCheckCannotGC nogc;
    1360           0 :     const CharT* replacementBegin = replacement->chars<CharT>(nogc);
    1361           0 :     const CharT* currentChar = replacementBegin;
    1362             : 
    1363           0 :     MOZ_ASSERT(firstDollarIndex < replacement->length());
    1364           0 :     const CharT* currentDollar = replacementBegin + firstDollarIndex;
    1365           0 :     const CharT* replacementEnd = replacementBegin + replacement->length();
    1366           0 :     do {
    1367             :         /* Move one of the constant portions of the replacement value. */
    1368           0 :         size_t len = currentDollar - currentChar;
    1369           0 :         sb.infallibleAppend(currentChar, len);
    1370           0 :         currentChar = currentDollar;
    1371             : 
    1372           0 :         JSSubString sub;
    1373             :         size_t skip;
    1374           0 :         if (InterpretDollar(matched, string, position, tailPos, captures, replacement,
    1375             :                             replacementBegin, currentDollar, replacementEnd, &sub, &skip))
    1376             :         {
    1377           0 :             sb.infallibleAppendSubstring(sub.base, sub.offset, sub.length);
    1378           0 :             currentChar += skip;
    1379           0 :             currentDollar += skip;
    1380             :         } else {
    1381           0 :             currentDollar++;
    1382             :         }
    1383             : 
    1384           0 :         currentDollar = js_strchr_limit(currentDollar, '$', replacementEnd);
    1385             :     } while (currentDollar);
    1386           0 :     sb.infallibleAppend(currentChar, replacement->length() - (currentChar - replacementBegin));
    1387           0 : }
    1388             : 
    1389             : static bool
    1390           0 : NeedTwoBytes(HandleLinearString string, HandleLinearString replacement,
    1391             :              HandleLinearString matched, Handle<GCVector<Value>> captures)
    1392             : {
    1393           0 :     if (string->hasTwoByteChars())
    1394           0 :         return true;
    1395           0 :     if (replacement->hasTwoByteChars())
    1396           0 :         return true;
    1397           0 :     if (matched->hasTwoByteChars())
    1398           0 :         return true;
    1399             : 
    1400           0 :     for (size_t i = 0, len = captures.length(); i < len; i++) {
    1401           0 :         Value capture = captures[i];
    1402           0 :         if (capture.isUndefined())
    1403           0 :             continue;
    1404           0 :         if (capture.toString()->hasTwoByteChars())
    1405           0 :             return true;
    1406             :     }
    1407             : 
    1408           0 :     return false;
    1409             : }
    1410             : 
    1411             : /* ES 2016 draft Mar 25, 2016 21.1.3.14.1. */
    1412             : bool
    1413           0 : js::RegExpGetSubstitution(JSContext* cx, HandleLinearString matched, HandleLinearString string,
    1414             :                           size_t position, HandleObject capturesObj, HandleLinearString replacement,
    1415             :                           size_t firstDollarIndex, MutableHandleValue rval)
    1416             : {
    1417           0 :     MOZ_ASSERT(firstDollarIndex < replacement->length());
    1418             : 
    1419             :     // Step 1 (skipped).
    1420             : 
    1421             :     // Step 2.
    1422           0 :     size_t matchLength = matched->length();
    1423             : 
    1424             :     // Steps 3-5 (skipped).
    1425             : 
    1426             :     // Step 6.
    1427           0 :     MOZ_ASSERT(position <= string->length());
    1428             : 
    1429             :     // Step 10 (reordered).
    1430             :     uint32_t nCaptures;
    1431           0 :     if (!GetLengthProperty(cx, capturesObj, &nCaptures))
    1432           0 :         return false;
    1433             : 
    1434           0 :     Rooted<GCVector<Value>> captures(cx, GCVector<Value>(cx));
    1435           0 :     if (!captures.reserve(nCaptures))
    1436           0 :         return false;
    1437             : 
    1438             :     // Step 7.
    1439           0 :     RootedValue capture(cx);
    1440           0 :     for (uint32_t i = 0; i < nCaptures; i++) {
    1441           0 :         if (!GetElement(cx, capturesObj, capturesObj, i, &capture))
    1442           0 :             return false;
    1443             : 
    1444           0 :         if (capture.isUndefined()) {
    1445           0 :             captures.infallibleAppend(capture);
    1446           0 :             continue;
    1447             :         }
    1448             : 
    1449           0 :         MOZ_ASSERT(capture.isString());
    1450           0 :         RootedLinearString captureLinear(cx, capture.toString()->ensureLinear(cx));
    1451           0 :         if (!captureLinear)
    1452           0 :             return false;
    1453           0 :         captures.infallibleAppend(StringValue(captureLinear));
    1454             :     }
    1455             : 
    1456             :     // Step 8 (skipped).
    1457             : 
    1458             :     // Step 9.
    1459           0 :     CheckedInt<uint32_t> checkedTailPos(0);
    1460           0 :     checkedTailPos += position;
    1461           0 :     checkedTailPos += matchLength;
    1462           0 :     if (!checkedTailPos.isValid()) {
    1463           0 :         ReportAllocationOverflow(cx);
    1464           0 :         return false;
    1465             :     }
    1466           0 :     uint32_t tailPos = checkedTailPos.value();
    1467             : 
    1468             :     // Step 11.
    1469             :     size_t reserveLength;
    1470           0 :     if (!FindReplaceLength(cx, matched, string, position, tailPos, &captures, replacement,
    1471             :                            firstDollarIndex, &reserveLength))
    1472             :     {
    1473           0 :         return false;
    1474             :     }
    1475             : 
    1476           0 :     StringBuffer result(cx);
    1477           0 :     if (NeedTwoBytes(string, replacement, matched, captures)) {
    1478           0 :         if (!result.ensureTwoByteChars())
    1479           0 :             return false;
    1480             :     }
    1481             : 
    1482           0 :     if (!result.reserve(reserveLength))
    1483           0 :         return false;
    1484             : 
    1485           0 :     if (replacement->hasLatin1Chars()) {
    1486           0 :         DoReplace<Latin1Char>(matched, string, position, tailPos, &captures,
    1487           0 :                               replacement, firstDollarIndex, result);
    1488             :     } else {
    1489           0 :         DoReplace<char16_t>(matched, string, position, tailPos, &captures,
    1490           0 :                             replacement, firstDollarIndex, result);
    1491             :     }
    1492             : 
    1493             :     // Step 12.
    1494           0 :     JSString* resultString = result.finishString();
    1495           0 :     if (!resultString)
    1496           0 :         return false;
    1497             : 
    1498           0 :     rval.setString(resultString);
    1499           0 :     return true;
    1500             : }
    1501             : 
    1502             : bool
    1503          30 : js::GetFirstDollarIndex(JSContext* cx, unsigned argc, Value* vp)
    1504             : {
    1505          30 :     CallArgs args = CallArgsFromVp(argc, vp);
    1506          30 :     MOZ_ASSERT(args.length() == 1);
    1507          60 :     RootedString str(cx, args[0].toString());
    1508             : 
    1509             :     // Should be handled in different path.
    1510          30 :     MOZ_ASSERT(str->length() != 0);
    1511             : 
    1512          30 :     int32_t index = -1;
    1513          30 :     if (!GetFirstDollarIndexRaw(cx, str, &index))
    1514           0 :         return false;
    1515             : 
    1516          30 :     args.rval().setInt32(index);
    1517          30 :     return true;
    1518             : }
    1519             : 
    1520             : template <typename TextChar>
    1521             : static MOZ_ALWAYS_INLINE int
    1522          30 : GetFirstDollarIndexImpl(const TextChar* text, uint32_t textLen)
    1523             : {
    1524          30 :     const TextChar* end = text + textLen;
    1525         453 :     for (const TextChar* c = text; c != end; ++c) {
    1526         435 :         if (*c == '$')
    1527          12 :             return c - text;
    1528             :     }
    1529          18 :     return -1;
    1530             : }
    1531             : 
    1532             : int32_t
    1533          30 : js::GetFirstDollarIndexRawFlat(JSLinearString* text)
    1534             : {
    1535          30 :     uint32_t len = text->length();
    1536             : 
    1537          60 :     JS::AutoCheckCannotGC nogc;
    1538          30 :     if (text->hasLatin1Chars())
    1539          27 :         return GetFirstDollarIndexImpl(text->latin1Chars(nogc), len);
    1540             : 
    1541           3 :     return  GetFirstDollarIndexImpl(text->twoByteChars(nogc), len);
    1542             : }
    1543             : 
    1544             : bool
    1545          30 : js::GetFirstDollarIndexRaw(JSContext* cx, HandleString str, int32_t* index)
    1546             : {
    1547          30 :     JSLinearString* text = str->ensureLinear(cx);
    1548          30 :     if (!text)
    1549           0 :         return false;
    1550             : 
    1551          30 :     *index = GetFirstDollarIndexRawFlat(text);
    1552          30 :     return true;
    1553             : }
    1554             : 
    1555             : bool
    1556         109 : js::RegExpPrototypeOptimizable(JSContext* cx, unsigned argc, Value* vp)
    1557             : {
    1558             :     // This can only be called from self-hosted code.
    1559         109 :     CallArgs args = CallArgsFromVp(argc, vp);
    1560         109 :     MOZ_ASSERT(args.length() == 1);
    1561             : 
    1562         109 :     args.rval().setBoolean(RegExpPrototypeOptimizableRaw(cx, &args[0].toObject()));
    1563         109 :     return true;
    1564             : }
    1565             : 
    1566             : bool
    1567         109 : js::RegExpPrototypeOptimizableRaw(JSContext* cx, JSObject* proto)
    1568             : {
    1569         218 :     JS::AutoCheckCannotGC nogc;
    1570         218 :     AutoAssertNoPendingException aanpe(cx);
    1571         109 :     if (!proto->isNative())
    1572           0 :         return false;
    1573             : 
    1574         109 :     NativeObject* nproto = static_cast<NativeObject*>(proto);
    1575             : 
    1576         109 :     Shape* shape = cx->compartment()->regExps.getOptimizableRegExpPrototypeShape();
    1577         109 :     if (shape == nproto->lastProperty())
    1578         101 :         return true;
    1579             : 
    1580             :     JSFunction* flagsGetter;
    1581           8 :     if (!GetOwnGetterPure(cx, proto, NameToId(cx->names().flags), &flagsGetter))
    1582           0 :         return false;
    1583             : 
    1584           8 :     if (!flagsGetter)
    1585           0 :         return false;
    1586             : 
    1587           8 :     if (!IsSelfHostedFunctionWithName(flagsGetter, cx->names().RegExpFlagsGetter))
    1588           0 :         return false;
    1589             : 
    1590             :     JSNative globalGetter;
    1591           8 :     if (!GetOwnNativeGetterPure(cx, proto, NameToId(cx->names().global), &globalGetter))
    1592           0 :         return false;
    1593             : 
    1594           8 :     if (globalGetter != regexp_global)
    1595           0 :         return false;
    1596             : 
    1597             :     JSNative ignoreCaseGetter;
    1598           8 :     if (!GetOwnNativeGetterPure(cx, proto, NameToId(cx->names().ignoreCase), &ignoreCaseGetter))
    1599           0 :         return false;
    1600             : 
    1601           8 :     if (ignoreCaseGetter != regexp_ignoreCase)
    1602           0 :         return false;
    1603             : 
    1604             :     JSNative multilineGetter;
    1605           8 :     if (!GetOwnNativeGetterPure(cx, proto, NameToId(cx->names().multiline), &multilineGetter))
    1606           0 :         return false;
    1607             : 
    1608           8 :     if (multilineGetter != regexp_multiline)
    1609           0 :         return false;
    1610             : 
    1611             :     JSNative stickyGetter;
    1612           8 :     if (!GetOwnNativeGetterPure(cx, proto, NameToId(cx->names().sticky), &stickyGetter))
    1613           0 :         return false;
    1614             : 
    1615           8 :     if (stickyGetter != regexp_sticky)
    1616           0 :         return false;
    1617             : 
    1618             :     JSNative unicodeGetter;
    1619           8 :     if (!GetOwnNativeGetterPure(cx, proto, NameToId(cx->names().unicode), &unicodeGetter))
    1620           0 :         return false;
    1621             : 
    1622           8 :     if (unicodeGetter != regexp_unicode)
    1623           0 :         return false;
    1624             : 
    1625             :     // Check if @@match, @@search, and exec are own data properties,
    1626             :     // those values should be tested in selfhosted JS.
    1627           8 :     bool has = false;
    1628           8 :     if (!HasOwnDataPropertyPure(cx, proto, SYMBOL_TO_JSID(cx->wellKnownSymbols().match), &has))
    1629           0 :         return false;
    1630           8 :     if (!has)
    1631           0 :         return false;
    1632             : 
    1633           8 :     if (!HasOwnDataPropertyPure(cx, proto, SYMBOL_TO_JSID(cx->wellKnownSymbols().search), &has))
    1634           0 :         return false;
    1635           8 :     if (!has)
    1636           0 :         return false;
    1637             : 
    1638           8 :     if (!HasOwnDataPropertyPure(cx, proto, NameToId(cx->names().exec), &has))
    1639           0 :         return false;
    1640           8 :     if (!has)
    1641           0 :         return false;
    1642             : 
    1643           8 :     cx->compartment()->regExps.setOptimizableRegExpPrototypeShape(nproto->lastProperty());
    1644           8 :     return true;
    1645             : }
    1646             : 
    1647             : bool
    1648          93 : js::RegExpInstanceOptimizable(JSContext* cx, unsigned argc, Value* vp)
    1649             : {
    1650             :     // This can only be called from self-hosted code.
    1651          93 :     CallArgs args = CallArgsFromVp(argc, vp);
    1652          93 :     MOZ_ASSERT(args.length() == 2);
    1653             : 
    1654         186 :     args.rval().setBoolean(RegExpInstanceOptimizableRaw(cx, &args[0].toObject(),
    1655         279 :                                                         &args[1].toObject()));
    1656          93 :     return true;
    1657             : }
    1658             : 
    1659             : bool
    1660          93 : js::RegExpInstanceOptimizableRaw(JSContext* cx, JSObject* obj, JSObject* proto)
    1661             : {
    1662         186 :     JS::AutoCheckCannotGC nogc;
    1663         186 :     AutoAssertNoPendingException aanpe(cx);
    1664             : 
    1665          93 :     RegExpObject* rx = &obj->as<RegExpObject>();
    1666             : 
    1667          93 :     Shape* shape = cx->compartment()->regExps.getOptimizableRegExpInstanceShape();
    1668          93 :     if (shape == rx->lastProperty())
    1669          86 :         return true;
    1670             : 
    1671           7 :     if (!rx->hasStaticPrototype())
    1672           0 :         return false;
    1673             : 
    1674           7 :     if (rx->staticPrototype() != proto)
    1675           0 :         return false;
    1676             : 
    1677           7 :     if (!RegExpObject::isInitialShape(rx))
    1678           0 :         return false;
    1679             : 
    1680           7 :     cx->compartment()->regExps.setOptimizableRegExpInstanceShape(rx->lastProperty());
    1681           7 :     return true;
    1682             : }
    1683             : 
    1684             : /*
    1685             :  * Pattern match the script to check if it is is indexing into a particular
    1686             :  * object, e.g. 'function(a) { return b[a]; }'. Avoid calling the script in
    1687             :  * such cases, which are used by javascript packers (particularly the popular
    1688             :  * Dean Edwards packer) to efficiently encode large scripts. We only handle the
    1689             :  * code patterns generated by such packers here.
    1690             :  */
    1691             : bool
    1692          14 : js::intrinsic_GetElemBaseForLambda(JSContext* cx, unsigned argc, Value* vp)
    1693             : {
    1694             :     // This can only be called from self-hosted code.
    1695          14 :     CallArgs args = CallArgsFromVp(argc, vp);
    1696          14 :     MOZ_ASSERT(args.length() == 1);
    1697             : 
    1698          14 :     JSObject& lambda = args[0].toObject();
    1699          14 :     args.rval().setUndefined();
    1700             : 
    1701          14 :     if (!lambda.is<JSFunction>())
    1702           0 :         return true;
    1703             : 
    1704          28 :     RootedFunction fun(cx, &lambda.as<JSFunction>());
    1705          14 :     if (!fun->isInterpreted() || fun->isClassConstructor())
    1706           4 :         return true;
    1707             : 
    1708          10 :     JSScript* script = JSFunction::getOrCreateScript(cx, fun);
    1709          10 :     if (!script)
    1710           0 :         return false;
    1711             : 
    1712          10 :     jsbytecode* pc = script->code();
    1713             : 
    1714             :     /*
    1715             :      * JSOP_GETALIASEDVAR tells us exactly where to find the base object 'b'.
    1716             :      * Rule out the (unlikely) possibility of a function with environment
    1717             :      * objects since it would make our environment walk off.
    1718             :      */
    1719          10 :     if (JSOp(*pc) != JSOP_GETALIASEDVAR || fun->needsSomeEnvironmentObject())
    1720          10 :         return true;
    1721           0 :     EnvironmentCoordinate ec(pc);
    1722           0 :     EnvironmentObject* env = &fun->environment()->as<EnvironmentObject>();
    1723           0 :     for (unsigned i = 0; i < ec.hops(); ++i)
    1724           0 :         env = &env->enclosingEnvironment().as<EnvironmentObject>();
    1725           0 :     Value b = env->aliasedBinding(ec);
    1726           0 :     pc += JSOP_GETALIASEDVAR_LENGTH;
    1727             : 
    1728             :     /* Look for 'a' to be the lambda's first argument. */
    1729           0 :     if (JSOp(*pc) != JSOP_GETARG || GET_ARGNO(pc) != 0)
    1730           0 :         return true;
    1731           0 :     pc += JSOP_GETARG_LENGTH;
    1732             : 
    1733             :     /* 'b[a]' */
    1734           0 :     if (JSOp(*pc) != JSOP_GETELEM)
    1735           0 :         return true;
    1736           0 :     pc += JSOP_GETELEM_LENGTH;
    1737             : 
    1738             :     /* 'return b[a]' */
    1739           0 :     if (JSOp(*pc) != JSOP_RETURN)
    1740           0 :         return true;
    1741             : 
    1742             :     /* 'b' must behave like a normal object. */
    1743           0 :     if (!b.isObject())
    1744           0 :         return true;
    1745             : 
    1746           0 :     JSObject& bobj = b.toObject();
    1747           0 :     const Class* clasp = bobj.getClass();
    1748           0 :     if (!clasp->isNative() || clasp->getOpsLookupProperty() || clasp->getOpsGetProperty())
    1749           0 :         return true;
    1750             : 
    1751           0 :     args.rval().setObject(bobj);
    1752           0 :     return true;
    1753             : }
    1754             : 
    1755             : /*
    1756             :  * Emulates `b[a]` property access, that is detected in GetElemBaseForLambda.
    1757             :  * It returns the property value only if the property is data property and the
    1758             :  * propety value is a string.  Otherwise it returns undefined.
    1759             :  */
    1760             : bool
    1761           0 : js::intrinsic_GetStringDataProperty(JSContext* cx, unsigned argc, Value* vp)
    1762             : {
    1763           0 :     CallArgs args = CallArgsFromVp(argc, vp);
    1764           0 :     MOZ_ASSERT(args.length() == 2);
    1765             : 
    1766           0 :     RootedObject obj(cx, &args[0].toObject());
    1767           0 :     if (!obj->isNative()) {
    1768             :         // The object is already checked to be native in GetElemBaseForLambda,
    1769             :         // but it can be swapped to the other class that is non-native.
    1770             :         // Return undefined to mark failure to get the property.
    1771           0 :         args.rval().setUndefined();
    1772           0 :         return true;
    1773             :     }
    1774             : 
    1775           0 :     RootedNativeObject nobj(cx, &obj->as<NativeObject>());
    1776           0 :     RootedString name(cx, args[1].toString());
    1777             : 
    1778           0 :     RootedAtom atom(cx, AtomizeString(cx, name));
    1779           0 :     if (!atom)
    1780           0 :         return false;
    1781             : 
    1782           0 :     RootedValue v(cx);
    1783           0 :     if (GetPropertyPure(cx, nobj, AtomToId(atom), v.address()) && v.isString())
    1784           0 :         args.rval().set(v);
    1785             :     else
    1786           0 :         args.rval().setUndefined();
    1787             : 
    1788           0 :     return true;
    1789             : }

Generated by: LCOV version 1.13