LCOV - code coverage report
Current view: top level - js/src/vm - RegExpObject.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 255 764 33.4 %
Date: 2017-07-14 16:53:18 Functions: 34 63 54.0 %
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 "vm/RegExpObject.h"
       8             : 
       9             : #include "mozilla/MemoryReporting.h"
      10             : #include "mozilla/PodOperations.h"
      11             : 
      12             : #include "jshashutil.h"
      13             : #include "jsstr.h"
      14             : #ifdef DEBUG
      15             : #include "jsutil.h"
      16             : #endif
      17             : 
      18             : #include "builtin/RegExp.h"
      19             : #include "frontend/TokenStream.h"
      20             : #ifdef DEBUG
      21             : #include "irregexp/RegExpBytecode.h"
      22             : #endif
      23             : #include "irregexp/RegExpParser.h"
      24             : #include "vm/MatchPairs.h"
      25             : #include "vm/RegExpStatics.h"
      26             : #include "vm/StringBuffer.h"
      27             : #include "vm/TraceLogging.h"
      28             : #ifdef DEBUG
      29             : #include "vm/Unicode.h"
      30             : #endif
      31             : #include "vm/Xdr.h"
      32             : 
      33             : #include "jsobjinlines.h"
      34             : 
      35             : #include "vm/NativeObject-inl.h"
      36             : #include "vm/Shape-inl.h"
      37             : 
      38             : using namespace js;
      39             : 
      40             : using mozilla::ArrayLength;
      41             : using mozilla::DebugOnly;
      42             : using mozilla::Maybe;
      43             : using mozilla::PodCopy;
      44             : using js::frontend::TokenStream;
      45             : 
      46             : using JS::AutoCheckCannotGC;
      47             : 
      48             : JS_STATIC_ASSERT(IgnoreCaseFlag == JSREG_FOLD);
      49             : JS_STATIC_ASSERT(GlobalFlag == JSREG_GLOB);
      50             : JS_STATIC_ASSERT(MultilineFlag == JSREG_MULTILINE);
      51             : JS_STATIC_ASSERT(StickyFlag == JSREG_STICKY);
      52             : JS_STATIC_ASSERT(UnicodeFlag == JSREG_UNICODE);
      53             : 
      54             : RegExpObject*
      55         677 : js::RegExpAlloc(JSContext* cx, NewObjectKind newKind, HandleObject proto /* = nullptr */)
      56             : {
      57        1353 :     Rooted<RegExpObject*> regexp(cx, NewObjectWithClassProto<RegExpObject>(cx, proto, newKind));
      58         676 :     if (!regexp)
      59           0 :         return nullptr;
      60             : 
      61         676 :     regexp->initPrivate(nullptr);
      62             : 
      63         676 :     if (!EmptyShape::ensureInitialCustomShape<RegExpObject>(cx, regexp))
      64           0 :         return nullptr;
      65             : 
      66         676 :     MOZ_ASSERT(regexp->lookupPure(cx->names().lastIndex)->slot() ==
      67             :                RegExpObject::lastIndexSlot());
      68             : 
      69         676 :     return regexp;
      70             : }
      71             : 
      72             : /* MatchPairs */
      73             : 
      74             : bool
      75          60 : MatchPairs::initArrayFrom(MatchPairs& copyFrom)
      76             : {
      77          60 :     MOZ_ASSERT(copyFrom.pairCount() > 0);
      78             : 
      79          60 :     if (!allocOrExpandArray(copyFrom.pairCount()))
      80           0 :         return false;
      81             : 
      82          60 :     PodCopy(pairs_, copyFrom.pairs_, pairCount_);
      83             : 
      84          60 :     return true;
      85             : }
      86             : 
      87             : bool
      88         124 : ScopedMatchPairs::allocOrExpandArray(size_t pairCount)
      89             : {
      90             :     /* Array expansion is forbidden, but array reuse is acceptable. */
      91         124 :     if (pairCount_) {
      92           0 :         MOZ_ASSERT(pairs_);
      93           0 :         MOZ_ASSERT(pairCount_ == pairCount);
      94           0 :         return true;
      95             :     }
      96             : 
      97         124 :     MOZ_ASSERT(!pairs_);
      98         124 :     pairs_ = (MatchPair*)lifoScope_.alloc().alloc(sizeof(MatchPair) * pairCount);
      99         124 :     if (!pairs_)
     100           0 :         return false;
     101             : 
     102         124 :     pairCount_ = pairCount;
     103         124 :     return true;
     104             : }
     105             : 
     106             : bool
     107          66 : VectorMatchPairs::allocOrExpandArray(size_t pairCount)
     108             : {
     109          66 :     if (!vec_.resizeUninitialized(sizeof(MatchPair) * pairCount))
     110           0 :         return false;
     111             : 
     112          66 :     pairs_ = &vec_[0];
     113          66 :     pairCount_ = pairCount;
     114          66 :     return true;
     115             : }
     116             : 
     117             : /* RegExpObject */
     118             : 
     119             : /* static */ RegExpShared*
     120         481 : RegExpObject::getShared(JSContext* cx, Handle<RegExpObject*> regexp)
     121             : {
     122         481 :     if (regexp->hasShared())
     123         408 :         return regexp->sharedRef();
     124             : 
     125          73 :     return createShared(cx, regexp);
     126             : }
     127             : 
     128             : /* static */ bool
     129           0 : RegExpObject::isOriginalFlagGetter(JSNative native, RegExpFlag* mask)
     130             : {
     131           0 :   if (native == regexp_global) {
     132           0 :       *mask = GlobalFlag;
     133           0 :       return true;
     134             :   }
     135           0 :   if (native == regexp_ignoreCase) {
     136           0 :       *mask = IgnoreCaseFlag;
     137           0 :       return true;
     138             :   }
     139           0 :   if (native == regexp_multiline) {
     140           0 :       *mask = MultilineFlag;
     141           0 :       return true;
     142             :   }
     143           0 :   if (native == regexp_sticky) {
     144           0 :       *mask = StickyFlag;
     145           0 :       return true;
     146             :   }
     147           0 :   if (native == regexp_unicode) {
     148           0 :       *mask = UnicodeFlag;
     149           0 :       return true;
     150             :   }
     151             : 
     152           0 :   return false;
     153             : }
     154             : 
     155             : /* static */ void
     156          33 : RegExpObject::trace(JSTracer* trc, JSObject* obj)
     157             : {
     158          33 :     obj->as<RegExpObject>().trace(trc);
     159          33 : }
     160             : 
     161             : static inline bool
     162           0 : IsMarkingTrace(JSTracer* trc)
     163             : {
     164             :     // Determine whether tracing is happening during normal marking.  We need to
     165             :     // test all the following conditions, since:
     166             :     //
     167             :     //   1. During TraceRuntime, CurrentThreadIsHeapBusy() is true, but the
     168             :     //      tracer might not be a marking tracer.
     169             :     //   2. When a write barrier executes, IsMarkingTracer is true, but
     170             :     //      CurrentThreadIsHeapBusy() will be false.
     171             : 
     172           0 :     return JS::CurrentThreadIsHeapCollecting() && trc->isMarkingTracer();
     173             : }
     174             : 
     175             : void
     176          33 : RegExpObject::trace(JSTracer* trc)
     177             : {
     178          33 :     TraceNullableEdge(trc, &sharedRef(), "RegExpObject shared");
     179          33 : }
     180             : 
     181             : static JSObject*
     182          83 : CreateRegExpPrototype(JSContext* cx, JSProtoKey key)
     183             : {
     184          83 :     return GlobalObject::createBlankPrototype(cx, cx->global(), &RegExpObject::protoClass_);
     185             : }
     186             : 
     187             : static const ClassOps RegExpObjectClassOps = {
     188             :     nullptr, /* addProperty */
     189             :     nullptr, /* delProperty */
     190             :     nullptr, /* getProperty */
     191             :     nullptr, /* setProperty */
     192             :     nullptr, /* enumerate */
     193             :     nullptr, /* newEnumerate */
     194             :     nullptr, /* resolve */
     195             :     nullptr, /* mayResolve */
     196             :     nullptr, /* finalize */
     197             :     nullptr, /* call */
     198             :     nullptr, /* hasInstance */
     199             :     nullptr, /* construct */
     200             :     RegExpObject::trace,
     201             : };
     202             : 
     203             : static const ClassSpec RegExpObjectClassSpec = {
     204             :     GenericCreateConstructor<js::regexp_construct, 2, gc::AllocKind::FUNCTION>,
     205             :     CreateRegExpPrototype,
     206             :     nullptr,
     207             :     js::regexp_static_props,
     208             :     js::regexp_methods,
     209             :     js::regexp_properties
     210             : };
     211             : 
     212             : const Class RegExpObject::class_ = {
     213             :     js_RegExp_str,
     214             :     JSCLASS_HAS_PRIVATE |
     215             :     JSCLASS_HAS_RESERVED_SLOTS(RegExpObject::RESERVED_SLOTS) |
     216             :     JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
     217             :     &RegExpObjectClassOps,
     218             :     &RegExpObjectClassSpec
     219             : };
     220             : 
     221             : const Class RegExpObject::protoClass_ = {
     222             :     js_Object_str,
     223             :     JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
     224             :     JS_NULL_CLASS_OPS,
     225             :     &RegExpObjectClassSpec
     226             : };
     227             : 
     228             : RegExpObject*
     229         169 : RegExpObject::create(JSContext* cx, const char16_t* chars, size_t length, RegExpFlag flags,
     230             :                      const ReadOnlyCompileOptions* options, TokenStream* tokenStream,
     231             :                      LifoAlloc& alloc, NewObjectKind newKind)
     232             : {
     233         338 :     RootedAtom source(cx, AtomizeChars(cx, chars, length));
     234         169 :     if (!source)
     235           0 :         return nullptr;
     236             : 
     237         169 :     return create(cx, source, flags, options, tokenStream, alloc, newKind);
     238             : }
     239             : 
     240             : RegExpObject*
     241         675 : RegExpObject::create(JSContext* cx, HandleAtom source, RegExpFlag flags,
     242             :                      const ReadOnlyCompileOptions* options, TokenStream* tokenStream,
     243             :                      LifoAlloc& alloc, NewObjectKind newKind)
     244             : {
     245        1349 :     Maybe<CompileOptions> dummyOptions;
     246         675 :     if (!tokenStream && !options) {
     247         350 :         dummyOptions.emplace(cx);
     248         350 :         options = dummyOptions.ptr();
     249             :     }
     250        1349 :     Maybe<TokenStream> dummyTokenStream;
     251         675 :     if (!tokenStream) {
     252        1012 :         dummyTokenStream.emplace(cx, *options,
     253             :                                    (const char16_t*) nullptr, 0,
     254         506 :                                    (frontend::StrictModeGetter*) nullptr);
     255         506 :         tokenStream = dummyTokenStream.ptr();
     256             :     }
     257             : 
     258         675 :     if (!irregexp::ParsePatternSyntax(*tokenStream, alloc, source, flags & UnicodeFlag))
     259           0 :         return nullptr;
     260             : 
     261        1349 :     Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx, newKind));
     262         674 :     if (!regexp)
     263           0 :         return nullptr;
     264             : 
     265         674 :     regexp->initAndZeroLastIndex(source, flags, cx);
     266             : 
     267         674 :     return regexp;
     268             : }
     269             : 
     270             : /* static */ RegExpShared*
     271          73 : RegExpObject::createShared(JSContext* cx, Handle<RegExpObject*> regexp)
     272             : {
     273          73 :     MOZ_ASSERT(!regexp->hasShared());
     274         146 :     RootedAtom source(cx, regexp->getSource());
     275          73 :     RegExpShared* shared = cx->zone()->regExps.get(cx, source, regexp->getFlags());
     276          73 :     if (!shared)
     277           0 :         return nullptr;
     278             : 
     279          73 :     regexp->setShared(*shared);
     280          73 :     return shared;
     281             : }
     282             : 
     283             : Shape*
     284          13 : RegExpObject::assignInitialShape(JSContext* cx, Handle<RegExpObject*> self)
     285             : {
     286          13 :     MOZ_ASSERT(self->empty());
     287             : 
     288             :     JS_STATIC_ASSERT(LAST_INDEX_SLOT == 0);
     289             : 
     290             :     /* The lastIndex property alone is writable but non-configurable. */
     291          26 :     return NativeObject::addDataProperty(cx, self, cx->names().lastIndex, LAST_INDEX_SLOT,
     292          26 :                                          JSPROP_PERMANENT);
     293             : }
     294             : 
     295             : void
     296         899 : RegExpObject::initIgnoringLastIndex(JSAtom* source, RegExpFlag flags)
     297             : {
     298             :     // If this is a re-initialization with an existing RegExpShared, 'flags'
     299             :     // may not match getShared()->flags, so forget the RegExpShared.
     300         899 :     sharedRef() = nullptr;
     301             : 
     302         899 :     setSource(source);
     303         899 :     setFlags(flags);
     304         899 : }
     305             : 
     306             : void
     307         897 : RegExpObject::initAndZeroLastIndex(JSAtom* source, RegExpFlag flags, JSContext* cx)
     308             : {
     309         897 :     initIgnoringLastIndex(source, flags);
     310         897 :     zeroLastIndex(cx);
     311         897 : }
     312             : 
     313             : static MOZ_ALWAYS_INLINE bool
     314           0 : IsLineTerminator(const JS::Latin1Char c)
     315             : {
     316           0 :     return c == '\n' || c == '\r';
     317             : }
     318             : 
     319             : static MOZ_ALWAYS_INLINE bool
     320           0 : IsLineTerminator(const char16_t c)
     321             : {
     322           0 :     return c == '\n' || c == '\r' || c == 0x2028 || c == 0x2029;
     323             : }
     324             : 
     325             : static MOZ_ALWAYS_INLINE bool
     326           0 : AppendEscapedLineTerminator(StringBuffer& sb, const JS::Latin1Char c)
     327             : {
     328           0 :     switch (c) {
     329             :       case '\n':
     330           0 :         if (!sb.append('n'))
     331           0 :             return false;
     332           0 :         break;
     333             :       case '\r':
     334           0 :         if (!sb.append('r'))
     335           0 :             return false;
     336           0 :         break;
     337             :       default:
     338           0 :         MOZ_CRASH("Bad LineTerminator");
     339             :     }
     340           0 :     return true;
     341             : }
     342             : 
     343             : static MOZ_ALWAYS_INLINE bool
     344           0 : AppendEscapedLineTerminator(StringBuffer& sb, const char16_t c)
     345             : {
     346           0 :     switch (c) {
     347             :       case '\n':
     348           0 :         if (!sb.append('n'))
     349           0 :             return false;
     350           0 :         break;
     351             :       case '\r':
     352           0 :         if (!sb.append('r'))
     353           0 :             return false;
     354           0 :         break;
     355             :       case 0x2028:
     356           0 :         if (!sb.append("u2028"))
     357           0 :             return false;
     358           0 :         break;
     359             :       case 0x2029:
     360           0 :         if (!sb.append("u2029"))
     361           0 :             return false;
     362           0 :         break;
     363             :       default:
     364           0 :         MOZ_CRASH("Bad LineTerminator");
     365             :     }
     366           0 :     return true;
     367             : }
     368             : 
     369             : template <typename CharT>
     370             : static MOZ_ALWAYS_INLINE bool
     371           0 : SetupBuffer(StringBuffer& sb, const CharT* oldChars, size_t oldLen, const CharT* it)
     372             : {
     373           0 :     if (mozilla::IsSame<CharT, char16_t>::value && !sb.ensureTwoByteChars())
     374           0 :         return false;
     375             : 
     376           0 :     if (!sb.reserve(oldLen + 1))
     377           0 :         return false;
     378             : 
     379           0 :     sb.infallibleAppend(oldChars, size_t(it - oldChars));
     380           0 :     return true;
     381             : }
     382             : 
     383             : // Note: leaves the string buffer empty if no escaping need be performed.
     384             : template <typename CharT>
     385             : static bool
     386           0 : EscapeRegExpPattern(StringBuffer& sb, const CharT* oldChars, size_t oldLen)
     387             : {
     388           0 :     bool inBrackets = false;
     389           0 :     bool previousCharacterWasBackslash = false;
     390             : 
     391           0 :     for (const CharT* it = oldChars; it < oldChars + oldLen; ++it) {
     392           0 :         CharT ch = *it;
     393           0 :         if (!previousCharacterWasBackslash) {
     394           0 :             if (inBrackets) {
     395           0 :                 if (ch == ']')
     396           0 :                     inBrackets = false;
     397           0 :             } else if (ch == '/') {
     398             :                 // There's a forward slash that needs escaping.
     399           0 :                 if (sb.empty()) {
     400             :                     // This is the first char we've seen that needs escaping,
     401             :                     // copy everything up to this point.
     402           0 :                     if (!SetupBuffer(sb, oldChars, oldLen, it))
     403           0 :                         return false;
     404             :                 }
     405           0 :                 if (!sb.append('\\'))
     406           0 :                     return false;
     407           0 :             } else if (ch == '[') {
     408           0 :                 inBrackets = true;
     409             :             }
     410             :         }
     411             : 
     412           0 :         if (IsLineTerminator(ch)) {
     413             :             // There's LineTerminator that needs escaping.
     414           0 :             if (sb.empty()) {
     415             :                 // This is the first char we've seen that needs escaping,
     416             :                 // copy everything up to this point.
     417           0 :                 if (!SetupBuffer(sb, oldChars, oldLen, it))
     418           0 :                     return false;
     419             :             }
     420           0 :             if (!previousCharacterWasBackslash) {
     421           0 :                 if (!sb.append('\\'))
     422           0 :                     return false;
     423             :             }
     424           0 :             if (!AppendEscapedLineTerminator(sb, ch))
     425           0 :                 return false;
     426           0 :         } else if (!sb.empty()) {
     427           0 :             if (!sb.append(ch))
     428           0 :                 return false;
     429             :         }
     430             : 
     431           0 :         if (previousCharacterWasBackslash)
     432           0 :             previousCharacterWasBackslash = false;
     433           0 :         else if (ch == '\\')
     434           0 :             previousCharacterWasBackslash = true;
     435             :     }
     436             : 
     437           0 :     return true;
     438             : }
     439             : 
     440             : // ES6 draft rev32 21.2.3.2.4.
     441             : JSAtom*
     442           0 : js::EscapeRegExpPattern(JSContext* cx, HandleAtom src)
     443             : {
     444             :     // Step 2.
     445           0 :     if (src->length() == 0)
     446           0 :         return cx->names().emptyRegExp;
     447             : 
     448             :     // We may never need to use |sb|. Start using it lazily.
     449           0 :     StringBuffer sb(cx);
     450             : 
     451           0 :     if (src->hasLatin1Chars()) {
     452           0 :         JS::AutoCheckCannotGC nogc;
     453           0 :         if (!::EscapeRegExpPattern(sb, src->latin1Chars(nogc), src->length()))
     454           0 :             return nullptr;
     455             :     } else {
     456           0 :         JS::AutoCheckCannotGC nogc;
     457           0 :         if (!::EscapeRegExpPattern(sb, src->twoByteChars(nogc), src->length()))
     458           0 :             return nullptr;
     459             :     }
     460             : 
     461             :     // Step 3.
     462           0 :     return sb.empty() ? src : sb.finishAtom();
     463             : }
     464             : 
     465             : // ES6 draft rev32 21.2.5.14. Optimized for RegExpObject.
     466             : JSFlatString*
     467           0 : RegExpObject::toString(JSContext* cx) const
     468             : {
     469             :     // Steps 3-4.
     470           0 :     RootedAtom src(cx, getSource());
     471           0 :     if (!src)
     472           0 :         return nullptr;
     473           0 :     RootedAtom escapedSrc(cx, EscapeRegExpPattern(cx, src));
     474             : 
     475             :     // Step 7.
     476           0 :     StringBuffer sb(cx);
     477           0 :     size_t len = escapedSrc->length();
     478           0 :     if (!sb.reserve(len + 2))
     479           0 :         return nullptr;
     480           0 :     sb.infallibleAppend('/');
     481           0 :     if (!sb.append(escapedSrc))
     482           0 :         return nullptr;
     483           0 :     sb.infallibleAppend('/');
     484             : 
     485             :     // Steps 5-7.
     486           0 :     if (global() && !sb.append('g'))
     487           0 :         return nullptr;
     488           0 :     if (ignoreCase() && !sb.append('i'))
     489           0 :         return nullptr;
     490           0 :     if (multiline() && !sb.append('m'))
     491           0 :         return nullptr;
     492           0 :     if (unicode() && !sb.append('u'))
     493           0 :         return nullptr;
     494           0 :     if (sticky() && !sb.append('y'))
     495           0 :         return nullptr;
     496             : 
     497           0 :     return sb.finishString();
     498             : }
     499             : 
     500             : #ifdef DEBUG
     501             : /* static */ bool
     502           0 : RegExpShared::dumpBytecode(JSContext* cx, MutableHandleRegExpShared re, bool match_only,
     503             :                            HandleLinearString input)
     504             : {
     505           0 :     CompilationMode mode = match_only ? MatchOnly : Normal;
     506           0 :     if (!RegExpShared::compileIfNecessary(cx, re, input, mode, ForceByteCode))
     507           0 :         return false;
     508             : 
     509           0 :     const uint8_t* byteCode = re->compilation(mode, input->hasLatin1Chars()).byteCode;
     510           0 :     const uint8_t* pc = byteCode;
     511             : 
     512           0 :     auto Load32Aligned = [](const uint8_t* pc) -> int32_t {
     513           0 :         MOZ_ASSERT((reinterpret_cast<uintptr_t>(pc) & 3) == 0);
     514           0 :         return *reinterpret_cast<const int32_t*>(pc);
     515             :     };
     516             : 
     517           0 :     auto Load16Aligned = [](const uint8_t* pc) -> int32_t {
     518           0 :         MOZ_ASSERT((reinterpret_cast<uintptr_t>(pc) & 1) == 0);
     519           0 :         return *reinterpret_cast<const uint16_t*>(pc);
     520             :     };
     521             : 
     522           0 :     int32_t numRegisters = Load32Aligned(pc);
     523           0 :     fprintf(stderr, "numRegisters: %d\n", numRegisters);
     524           0 :     pc += 4;
     525             : 
     526           0 :     fprintf(stderr, "loc     op\n");
     527           0 :     fprintf(stderr, "-----   --\n");
     528             : 
     529           0 :     auto DumpLower = [](const char* text) {
     530           0 :         while (*text) {
     531           0 :             fprintf(stderr, "%c", unicode::ToLowerCase(*text));
     532           0 :             text++;
     533             :         }
     534           0 :     };
     535             : 
     536             : #define BYTECODE(NAME) \
     537             :     case irregexp::BC_##NAME: \
     538             :       DumpLower(#NAME);
     539             : #define ADVANCE(NAME) \
     540             :     fprintf(stderr, "\n"); \
     541             :     pc += irregexp::BC_##NAME##_LENGTH; \
     542             :     maxPc = js::Max(maxPc, pc); \
     543             :     break;
     544             : #define STOP(NAME) \
     545             :     fprintf(stderr, "\n"); \
     546             :     pc += irregexp::BC_##NAME##_LENGTH; \
     547             :     break;
     548             : #define JUMP(NAME, OFFSET) \
     549             :     fprintf(stderr, "\n"); \
     550             :     maxPc = js::Max(maxPc, byteCode + OFFSET); \
     551             :     pc += irregexp::BC_##NAME##_LENGTH; \
     552             :     break;
     553             : #define BRANCH(NAME, OFFSET) \
     554             :     fprintf(stderr, "\n"); \
     555             :     pc += irregexp::BC_##NAME##_LENGTH; \
     556             :     maxPc = js::Max(maxPc, js::Max(pc, byteCode + OFFSET)); \
     557             :     break;
     558             : 
     559             :     // Bytecode has no end marker, we need to calculate the bytecode length by
     560             :     // tracing jumps and branches.
     561           0 :     const uint8_t* maxPc = pc;
     562           0 :     while (pc <= maxPc) {
     563           0 :         fprintf(stderr, "%05d:  ", int32_t(pc - byteCode));
     564           0 :         int32_t insn = Load32Aligned(pc);
     565           0 :         switch (insn & irregexp::BYTECODE_MASK) {
     566           0 :           BYTECODE(BREAK) {
     567           0 :             STOP(BREAK);
     568             :           }
     569           0 :           BYTECODE(PUSH_CP) {
     570           0 :             ADVANCE(PUSH_CP);
     571             :           }
     572           0 :           BYTECODE(PUSH_BT) {
     573           0 :             int32_t offset = Load32Aligned(pc + 4);
     574             :             fprintf(stderr, " %d",
     575           0 :                     offset);
     576             :             // Pushed value is used by POP_BT for jumping.
     577             :             // Resolve maxPc here.
     578           0 :             BRANCH(PUSH_BT, offset);
     579             :           }
     580           0 :           BYTECODE(PUSH_REGISTER) {
     581           0 :             fprintf(stderr, " reg[%d]",
     582           0 :                     insn >> irregexp::BYTECODE_SHIFT);
     583           0 :             ADVANCE(PUSH_REGISTER);
     584             :           }
     585           0 :           BYTECODE(SET_REGISTER) {
     586           0 :             fprintf(stderr, " reg[%d], %d",
     587             :                     insn >> irregexp::BYTECODE_SHIFT,
     588           0 :                     Load32Aligned(pc + 4));
     589           0 :             ADVANCE(SET_REGISTER);
     590             :           }
     591           0 :           BYTECODE(ADVANCE_REGISTER) {
     592           0 :             fprintf(stderr, " reg[%d], %d",
     593             :                     insn >> irregexp::BYTECODE_SHIFT,
     594           0 :                     Load32Aligned(pc + 4));
     595           0 :             ADVANCE(ADVANCE_REGISTER);
     596             :           }
     597           0 :           BYTECODE(SET_REGISTER_TO_CP) {
     598           0 :             fprintf(stderr, " reg[%d], %d",
     599             :                     insn >> irregexp::BYTECODE_SHIFT,
     600           0 :                     Load32Aligned(pc + 4));
     601           0 :             ADVANCE(SET_REGISTER_TO_CP);
     602             :           }
     603           0 :           BYTECODE(SET_CP_TO_REGISTER) {
     604           0 :             fprintf(stderr, " reg[%d]",
     605           0 :                     insn >> irregexp::BYTECODE_SHIFT);
     606           0 :             ADVANCE(SET_CP_TO_REGISTER);
     607             :           }
     608           0 :           BYTECODE(SET_REGISTER_TO_SP) {
     609           0 :             fprintf(stderr, " reg[%d]",
     610           0 :                     insn >> irregexp::BYTECODE_SHIFT);
     611           0 :             ADVANCE(SET_REGISTER_TO_SP);
     612             :           }
     613           0 :           BYTECODE(SET_SP_TO_REGISTER) {
     614           0 :             fprintf(stderr, " reg[%d]",
     615           0 :                     insn >> irregexp::BYTECODE_SHIFT);
     616           0 :             ADVANCE(SET_SP_TO_REGISTER);
     617             :           }
     618           0 :           BYTECODE(POP_CP) {
     619           0 :             ADVANCE(POP_CP);
     620             :           }
     621           0 :           BYTECODE(POP_BT) {
     622             :             // Jump is already resolved in PUSH_BT.
     623           0 :             STOP(POP_BT);
     624             :           }
     625           0 :           BYTECODE(POP_REGISTER) {
     626           0 :             fprintf(stderr, " reg[%d]",
     627           0 :                     insn >> irregexp::BYTECODE_SHIFT);
     628           0 :             ADVANCE(POP_REGISTER);
     629             :           }
     630           0 :           BYTECODE(FAIL) {
     631           0 :             ADVANCE(FAIL);
     632             :           }
     633           0 :           BYTECODE(SUCCEED) {
     634           0 :             ADVANCE(SUCCEED);
     635             :           }
     636           0 :           BYTECODE(ADVANCE_CP) {
     637           0 :             fprintf(stderr, " %d",
     638           0 :                     insn >> irregexp::BYTECODE_SHIFT);
     639           0 :             ADVANCE(ADVANCE_CP);
     640             :           }
     641           0 :           BYTECODE(GOTO) {
     642           0 :             int32_t offset = Load32Aligned(pc + 4);
     643             :             fprintf(stderr, " %d",
     644           0 :                     offset);
     645           0 :             JUMP(GOTO, offset);
     646             :           }
     647           0 :           BYTECODE(ADVANCE_CP_AND_GOTO) {
     648           0 :             int32_t offset = Load32Aligned(pc + 4);
     649           0 :             fprintf(stderr, " %d, %d",
     650             :                     insn >> irregexp::BYTECODE_SHIFT,
     651           0 :                     offset);
     652           0 :             JUMP(ADVANCE_CP_AND_GOTO, offset);
     653             :           }
     654           0 :           BYTECODE(CHECK_GREEDY) {
     655           0 :             int32_t offset = Load32Aligned(pc + 4);
     656             :             fprintf(stderr, " %d",
     657           0 :                     offset);
     658           0 :             BRANCH(CHECK_GREEDY, offset);
     659             :           }
     660           0 :           BYTECODE(LOAD_CURRENT_CHAR) {
     661           0 :             int32_t offset = Load32Aligned(pc + 4);
     662           0 :             fprintf(stderr, " %d, %d",
     663             :                     insn >> irregexp::BYTECODE_SHIFT,
     664           0 :                     offset);
     665           0 :             BRANCH(LOAD_CURRENT_CHAR, offset);
     666             :           }
     667           0 :           BYTECODE(LOAD_CURRENT_CHAR_UNCHECKED) {
     668           0 :             fprintf(stderr, " %d",
     669           0 :                     insn >> irregexp::BYTECODE_SHIFT);
     670           0 :             ADVANCE(LOAD_CURRENT_CHAR_UNCHECKED);
     671             :           }
     672           0 :           BYTECODE(LOAD_2_CURRENT_CHARS) {
     673           0 :             int32_t offset = Load32Aligned(pc + 4);
     674           0 :             fprintf(stderr, " %d, %d",
     675             :                     insn >> irregexp::BYTECODE_SHIFT,
     676           0 :                     offset);
     677           0 :             BRANCH(LOAD_2_CURRENT_CHARS, offset);
     678             :           }
     679           0 :           BYTECODE(LOAD_2_CURRENT_CHARS_UNCHECKED) {
     680           0 :             fprintf(stderr, " %d",
     681           0 :                     insn >> irregexp::BYTECODE_SHIFT);
     682           0 :             ADVANCE(LOAD_2_CURRENT_CHARS_UNCHECKED);
     683             :           }
     684           0 :           BYTECODE(LOAD_4_CURRENT_CHARS) {
     685           0 :             ADVANCE(LOAD_4_CURRENT_CHARS);
     686             :           }
     687           0 :           BYTECODE(LOAD_4_CURRENT_CHARS_UNCHECKED) {
     688           0 :             ADVANCE(LOAD_4_CURRENT_CHARS_UNCHECKED);
     689             :           }
     690           0 :           BYTECODE(CHECK_4_CHARS) {
     691           0 :             int32_t offset = Load32Aligned(pc + 8);
     692           0 :             fprintf(stderr, " %d, %d",
     693             :                     Load32Aligned(pc + 4),
     694           0 :                     offset);
     695           0 :             BRANCH(CHECK_4_CHARS, offset);
     696             :           }
     697           0 :           BYTECODE(CHECK_CHAR) {
     698           0 :             int32_t offset = Load32Aligned(pc + 4);
     699           0 :             fprintf(stderr, " %d, %d",
     700             :                     insn >> irregexp::BYTECODE_SHIFT,
     701           0 :                     offset);
     702           0 :             BRANCH(CHECK_CHAR, offset);
     703             :           }
     704           0 :           BYTECODE(CHECK_NOT_4_CHARS) {
     705           0 :             int32_t offset = Load32Aligned(pc + 8);
     706           0 :             fprintf(stderr, " %d, %d",
     707             :                     Load32Aligned(pc + 4),
     708           0 :                     offset);
     709           0 :             BRANCH(CHECK_NOT_4_CHARS, offset);
     710             :           }
     711           0 :           BYTECODE(CHECK_NOT_CHAR) {
     712           0 :             int32_t offset = Load32Aligned(pc + 4);
     713           0 :             fprintf(stderr, " %d, %d",
     714             :                     insn >> irregexp::BYTECODE_SHIFT,
     715           0 :                     offset);
     716           0 :             BRANCH(CHECK_NOT_CHAR, offset);
     717             :           }
     718           0 :           BYTECODE(AND_CHECK_4_CHARS) {
     719           0 :             int32_t offset = Load32Aligned(pc + 12);
     720           0 :             fprintf(stderr, " %d, %d, %d",
     721             :                     Load32Aligned(pc + 4),
     722             :                     Load32Aligned(pc + 8),
     723           0 :                     offset);
     724           0 :             BRANCH(AND_CHECK_4_CHARS, offset);
     725             :           }
     726           0 :           BYTECODE(AND_CHECK_CHAR) {
     727           0 :             int32_t offset = Load32Aligned(pc + 8);
     728           0 :             fprintf(stderr, " %d, %d, %d",
     729             :                     insn >> irregexp::BYTECODE_SHIFT,
     730             :                     Load32Aligned(pc + 4),
     731           0 :                     offset);
     732           0 :             BRANCH(AND_CHECK_CHAR, offset);
     733             :           }
     734           0 :           BYTECODE(AND_CHECK_NOT_4_CHARS) {
     735           0 :             int32_t offset = Load32Aligned(pc + 12);
     736           0 :             fprintf(stderr, " %d, %d, %d",
     737             :                     Load32Aligned(pc + 4),
     738             :                     Load32Aligned(pc + 8),
     739           0 :                     offset);
     740           0 :             BRANCH(AND_CHECK_NOT_4_CHARS, offset);
     741             :           }
     742           0 :           BYTECODE(AND_CHECK_NOT_CHAR) {
     743           0 :             int32_t offset = Load32Aligned(pc + 8);
     744           0 :             fprintf(stderr, " %d, %d, %d",
     745             :                     insn >> irregexp::BYTECODE_SHIFT,
     746             :                     Load32Aligned(pc + 4),
     747           0 :                     offset);
     748           0 :             BRANCH(AND_CHECK_NOT_CHAR, offset);
     749             :           }
     750           0 :           BYTECODE(MINUS_AND_CHECK_NOT_CHAR) {
     751           0 :             int32_t offset = Load32Aligned(pc + 8);
     752           0 :             fprintf(stderr, " %d, %d, %d, %d",
     753             :                     insn >> irregexp::BYTECODE_SHIFT,
     754             :                     Load16Aligned(pc + 4),
     755             :                     Load16Aligned(pc + 6),
     756           0 :                     offset);
     757           0 :             BRANCH(MINUS_AND_CHECK_NOT_CHAR, offset);
     758             :           }
     759           0 :           BYTECODE(CHECK_CHAR_IN_RANGE) {
     760           0 :             int32_t offset = Load32Aligned(pc + 8);
     761           0 :             fprintf(stderr, " %d, %d, %d",
     762             :                     Load16Aligned(pc + 4),
     763             :                     Load16Aligned(pc + 6),
     764           0 :                     offset);
     765           0 :             BRANCH(CHECK_CHAR_IN_RANGE, offset);
     766             :           }
     767           0 :           BYTECODE(CHECK_CHAR_NOT_IN_RANGE) {
     768           0 :             int32_t offset = Load32Aligned(pc + 8);
     769           0 :             fprintf(stderr, " %d, %d, %d",
     770             :                     Load16Aligned(pc + 4),
     771             :                     Load16Aligned(pc + 6),
     772           0 :                     offset);
     773           0 :             BRANCH(CHECK_CHAR_NOT_IN_RANGE, offset);
     774             :           }
     775           0 :           BYTECODE(CHECK_BIT_IN_TABLE) {
     776           0 :             int32_t offset = Load32Aligned(pc + 4);
     777           0 :             fprintf(stderr, " %d, "
     778             :                     "%02x %02x %02x %02x %02x %02x %02x %02x "
     779             :                     "%02x %02x %02x %02x %02x %02x %02x %02x",
     780             :                     offset,
     781           0 :                     pc[8], pc[9], pc[10], pc[11],
     782           0 :                     pc[12], pc[13], pc[14], pc[15],
     783           0 :                     pc[16], pc[17], pc[18], pc[19],
     784           0 :                     pc[20], pc[21], pc[22], pc[23]);
     785           0 :             BRANCH(CHECK_BIT_IN_TABLE, offset);
     786             :           }
     787           0 :           BYTECODE(CHECK_LT) {
     788           0 :             int32_t offset = Load32Aligned(pc + 4);
     789           0 :             fprintf(stderr, " %d, %d",
     790             :                     insn >> irregexp::BYTECODE_SHIFT,
     791           0 :                     offset);
     792           0 :             BRANCH(CHECK_LT, offset);
     793             :           }
     794           0 :           BYTECODE(CHECK_GT) {
     795           0 :             int32_t offset = Load32Aligned(pc + 4);
     796           0 :             fprintf(stderr, " %d, %d",
     797             :                     insn >> irregexp::BYTECODE_SHIFT,
     798           0 :                     offset);
     799           0 :             BRANCH(CHECK_GT, offset);
     800             :           }
     801           0 :           BYTECODE(CHECK_REGISTER_LT) {
     802           0 :             int32_t offset = Load32Aligned(pc + 8);
     803           0 :             fprintf(stderr, " reg[%d], %d, %d",
     804             :                     insn >> irregexp::BYTECODE_SHIFT,
     805             :                     Load32Aligned(pc + 4),
     806           0 :                     offset);
     807           0 :             BRANCH(CHECK_REGISTER_LT, offset);
     808             :           }
     809           0 :           BYTECODE(CHECK_REGISTER_GE) {
     810           0 :             int32_t offset = Load32Aligned(pc + 8);
     811           0 :             fprintf(stderr, " reg[%d], %d, %d",
     812             :                     insn >> irregexp::BYTECODE_SHIFT,
     813             :                     Load32Aligned(pc + 4),
     814           0 :                     offset);
     815           0 :             BRANCH(CHECK_REGISTER_GE, offset);
     816             :           }
     817           0 :           BYTECODE(CHECK_REGISTER_EQ_POS) {
     818           0 :             int32_t offset = Load32Aligned(pc + 4);
     819           0 :             fprintf(stderr, " reg[%d], %d",
     820             :                     insn >> irregexp::BYTECODE_SHIFT,
     821           0 :                     offset);
     822           0 :             BRANCH(CHECK_REGISTER_EQ_POS, offset);
     823             :           }
     824           0 :           BYTECODE(CHECK_NOT_REGS_EQUAL) {
     825           0 :             int32_t offset = Load32Aligned(pc + 8);
     826           0 :             fprintf(stderr, " reg[%d], %d, %d",
     827             :                     insn >> irregexp::BYTECODE_SHIFT,
     828             :                     Load32Aligned(pc + 4),
     829           0 :                     offset);
     830           0 :             BRANCH(CHECK_NOT_REGS_EQUAL, offset);
     831             :           }
     832           0 :           BYTECODE(CHECK_NOT_BACK_REF) {
     833           0 :             int32_t offset = Load32Aligned(pc + 4);
     834           0 :             fprintf(stderr, " reg[%d], %d",
     835             :                     insn >> irregexp::BYTECODE_SHIFT,
     836           0 :                     offset);
     837           0 :             BRANCH(CHECK_NOT_BACK_REF, offset);
     838             :           }
     839           0 :           BYTECODE(CHECK_NOT_BACK_REF_NO_CASE) {
     840           0 :             int32_t offset = Load32Aligned(pc + 4);
     841           0 :             fprintf(stderr, " reg[%d], %d",
     842             :                     insn >> irregexp::BYTECODE_SHIFT,
     843           0 :                     offset);
     844           0 :             BRANCH(CHECK_NOT_BACK_REF_NO_CASE, offset);
     845             :           }
     846           0 :           BYTECODE(CHECK_NOT_BACK_REF_NO_CASE_UNICODE) {
     847           0 :             int32_t offset = Load32Aligned(pc + 4);
     848           0 :             fprintf(stderr, " reg[%d], %d",
     849             :                     insn >> irregexp::BYTECODE_SHIFT,
     850           0 :                     offset);
     851           0 :             BRANCH(CHECK_NOT_BACK_REF_NO_CASE_UNICODE, offset);
     852             :           }
     853           0 :           BYTECODE(CHECK_AT_START) {
     854           0 :             int32_t offset = Load32Aligned(pc + 4);
     855             :             fprintf(stderr, " %d",
     856           0 :                     offset);
     857           0 :             BRANCH(CHECK_AT_START, offset);
     858             :           }
     859           0 :           BYTECODE(CHECK_NOT_AT_START) {
     860           0 :             int32_t offset = Load32Aligned(pc + 4);
     861             :             fprintf(stderr, " %d",
     862           0 :                     offset);
     863           0 :             BRANCH(CHECK_NOT_AT_START, offset);
     864             :           }
     865           0 :           BYTECODE(SET_CURRENT_POSITION_FROM_END) {
     866           0 :             fprintf(stderr, " %u",
     867           0 :                     static_cast<uint32_t>(insn) >> irregexp::BYTECODE_SHIFT);
     868           0 :             ADVANCE(SET_CURRENT_POSITION_FROM_END);
     869             :           }
     870             :           default:
     871           0 :             MOZ_CRASH("Bad bytecode");
     872             :         }
     873             :     }
     874             : 
     875             : #undef BYTECODE
     876             : #undef ADVANCE
     877             : #undef STOP
     878             : #undef JUMP
     879             : #undef BRANCH
     880             : 
     881           0 :     return true;
     882             : }
     883             : 
     884             : /* static */ bool
     885           0 : RegExpObject::dumpBytecode(JSContext* cx, Handle<RegExpObject*> regexp,
     886             :                            bool match_only, HandleLinearString input)
     887             : {
     888           0 :     RootedRegExpShared shared(cx, getShared(cx, regexp));
     889           0 :     if (!shared)
     890           0 :         return false;
     891             : 
     892           0 :     return RegExpShared::dumpBytecode(cx, &shared, match_only, input);
     893             : }
     894             : #endif
     895             : 
     896             : template <typename CharT>
     897             : static MOZ_ALWAYS_INLINE bool
     898         128 : IsRegExpMetaChar(CharT ch)
     899             : {
     900         128 :     switch (ch) {
     901             :       /* ES 2016 draft Mar 25, 2016 21.2.1 SyntaxCharacter. */
     902             :       case '^': case '$': case '\\': case '.': case '*': case '+':
     903             :       case '?': case '(': case ')': case '[': case ']': case '{':
     904             :       case '}': case '|':
     905          48 :         return true;
     906             :       default:
     907          80 :         return false;
     908             :     }
     909             : }
     910             : 
     911             : template <typename CharT>
     912             : bool
     913          60 : js::HasRegExpMetaChars(const CharT* chars, size_t length)
     914             : {
     915         140 :     for (size_t i = 0; i < length; ++i) {
     916         128 :         if (IsRegExpMetaChar<CharT>(chars[i]))
     917          48 :             return true;
     918             :     }
     919          12 :     return false;
     920             : }
     921             : 
     922             : template bool
     923             : js::HasRegExpMetaChars<Latin1Char>(const Latin1Char* chars, size_t length);
     924             : 
     925             : template bool
     926             : js::HasRegExpMetaChars<char16_t>(const char16_t* chars, size_t length);
     927             : 
     928             : bool
     929          42 : js::StringHasRegExpMetaChars(JSLinearString* str)
     930             : {
     931          84 :     AutoCheckCannotGC nogc;
     932          42 :     if (str->hasLatin1Chars())
     933          42 :         return HasRegExpMetaChars(str->latin1Chars(nogc), str->length());
     934             : 
     935           0 :     return HasRegExpMetaChars(str->twoByteChars(nogc), str->length());
     936             : }
     937             : 
     938             : /* RegExpShared */
     939             : 
     940          69 : RegExpShared::RegExpShared(JSAtom* source, RegExpFlag flags)
     941          69 :   : source(source), flags(flags), canStringMatch(false), parenCount(0)
     942          69 : {}
     943             : 
     944             : void
     945           0 : RegExpShared::traceChildren(JSTracer* trc)
     946             : {
     947             :     // Discard code to avoid holding onto ExecutablePools.
     948           0 :     if (IsMarkingTrace(trc) && trc->runtime()->gc.isShrinkingGC())
     949           0 :         discardJitCode();
     950             : 
     951           0 :     TraceNullableEdge(trc, &source, "RegExpShared source");
     952           0 :     for (auto& comp : compilationArray)
     953           0 :         TraceNullableEdge(trc, &comp.jitCode, "RegExpShared code");
     954           0 : }
     955             : 
     956             : void
     957           0 : RegExpShared::discardJitCode()
     958             : {
     959           0 :     for (auto& comp : compilationArray)
     960           0 :         comp.jitCode = nullptr;
     961             : 
     962             :     // We can also purge the tables used by JIT code.
     963           0 :     tables.clearAndFree();
     964           0 : }
     965             : 
     966             : void
     967           0 : RegExpShared::finalize(FreeOp* fop)
     968             : {
     969           0 :     for (auto& comp : compilationArray)
     970           0 :         js_free(comp.byteCode);
     971           0 :     tables.~JitCodeTables();
     972           0 : }
     973             : 
     974             : /* static */ bool
     975          40 : RegExpShared::compile(JSContext* cx, MutableHandleRegExpShared re, HandleLinearString input,
     976             :                       CompilationMode mode, ForceByteCodeEnum force)
     977             : {
     978          40 :     TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
     979          80 :     AutoTraceLog logCompile(logger, TraceLogger_IrregexpCompile);
     980             : 
     981          80 :     RootedAtom pattern(cx, re->source);
     982          80 :     return compile(cx, re, pattern, input, mode, force);
     983             : }
     984             : 
     985             : /* static */ bool
     986          40 : RegExpShared::compile(JSContext* cx, MutableHandleRegExpShared re, HandleAtom pattern,
     987             :                       HandleLinearString input, CompilationMode mode, ForceByteCodeEnum force)
     988             : {
     989          40 :     if (!re->ignoreCase() && !StringHasRegExpMetaChars(pattern))
     990           3 :         re->canStringMatch = true;
     991             : 
     992          80 :     CompileOptions options(cx);
     993          80 :     frontend::TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
     994             : 
     995          80 :     LifoAllocScope scope(&cx->tempLifoAlloc());
     996             : 
     997             :     /* Parse the pattern. */
     998          40 :     irregexp::RegExpCompileData data;
     999         240 :     if (!irregexp::ParsePattern(dummyTokenStream, cx->tempLifoAlloc(), pattern,
    1000          80 :                                 re->multiline(), mode == MatchOnly, re->unicode(),
    1001         160 :                                 re->ignoreCase(), re->global(), re->sticky(), &data))
    1002             :     {
    1003           0 :         return false;
    1004             :     }
    1005             : 
    1006          40 :     re->parenCount = data.capture_count;
    1007             : 
    1008          80 :     JitCodeTables tables;
    1009             :     irregexp::RegExpCode code = irregexp::CompilePattern(cx, re, &data, input,
    1010             :                                                          false /* global() */,
    1011          40 :                                                          re->ignoreCase(),
    1012          40 :                                                          input->hasLatin1Chars(),
    1013             :                                                          mode == MatchOnly,
    1014             :                                                          force == ForceByteCode,
    1015          40 :                                                          re->sticky(),
    1016          40 :                                                          re->unicode(),
    1017         160 :                                                          tables);
    1018          40 :     if (code.empty())
    1019           0 :         return false;
    1020             : 
    1021          40 :     MOZ_ASSERT(!code.jitCode || !code.byteCode);
    1022          40 :     MOZ_ASSERT_IF(force == ForceByteCode, code.byteCode);
    1023             : 
    1024          40 :     RegExpCompilation& compilation = re->compilation(mode, input->hasLatin1Chars());
    1025          40 :     if (code.jitCode) {
    1026             :         // First copy the tables. GC can purge the tables if the RegExpShared
    1027             :         // has no JIT code, so it's important to do this right before setting
    1028             :         // compilation.jitCode (to ensure no purging happens between adding the
    1029             :         // tables and setting the JIT code).
    1030          56 :         for (size_t i = 0; i < tables.length(); i++) {
    1031          16 :             if (!re->addTable(Move(tables[i])))
    1032           0 :                 return false;
    1033             :         }
    1034          40 :         compilation.jitCode = code.jitCode;
    1035           0 :     } else if (code.byteCode) {
    1036           0 :         MOZ_ASSERT(tables.empty(), "RegExpInterpreter does not use data tables");
    1037           0 :         compilation.byteCode = code.byteCode;
    1038             :     }
    1039             : 
    1040          40 :     return true;
    1041             : }
    1042             : 
    1043             : /* static */ bool
    1044         264 : RegExpShared::compileIfNecessary(JSContext* cx, MutableHandleRegExpShared re,
    1045             :                                  HandleLinearString input, CompilationMode mode,
    1046             :                                  ForceByteCodeEnum force)
    1047             : {
    1048         264 :     if (re->isCompiled(mode, input->hasLatin1Chars(), force))
    1049         224 :         return true;
    1050          40 :     return compile(cx, re, input, mode, force);
    1051             : }
    1052             : 
    1053             : /* static */ RegExpRunStatus
    1054         264 : RegExpShared::execute(JSContext* cx, MutableHandleRegExpShared re, HandleLinearString input,
    1055             :                       size_t start, MatchPairs* matches, size_t* endIndex)
    1056             : {
    1057         264 :     MOZ_ASSERT_IF(matches, !endIndex);
    1058         264 :     MOZ_ASSERT_IF(!matches, endIndex);
    1059         264 :     TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
    1060             : 
    1061         264 :     CompilationMode mode = matches ? Normal : MatchOnly;
    1062             : 
    1063             :     /* Compile the code at point-of-use. */
    1064         264 :     if (!compileIfNecessary(cx, re, input, mode, DontForceByteCode))
    1065           0 :         return RegExpRunStatus_Error;
    1066             : 
    1067             :     /*
    1068             :      * Ensure sufficient memory for output vector.
    1069             :      * No need to initialize it. The RegExp engine fills them in on a match.
    1070             :      */
    1071         264 :     if (matches && !matches->allocOrExpandArray(re->pairCount())) {
    1072           0 :         ReportOutOfMemory(cx);
    1073           0 :         return RegExpRunStatus_Error;
    1074             :     }
    1075             : 
    1076         264 :     size_t length = input->length();
    1077             : 
    1078             :     // Reset the Irregexp backtrack stack if it grows during execution.
    1079         528 :     irregexp::RegExpStackScope stackScope(cx);
    1080             : 
    1081         264 :     if (re->canStringMatch) {
    1082          19 :         MOZ_ASSERT(re->pairCount() == 1);
    1083          19 :         size_t sourceLength = re->source->length();
    1084          19 :         if (re->sticky()) {
    1085             :             // First part checks size_t overflow.
    1086           0 :             if (sourceLength + start < sourceLength || sourceLength + start > length)
    1087           0 :                 return RegExpRunStatus_Success_NotFound;
    1088           0 :             if (!HasSubstringAt(input, re->source, start))
    1089           0 :                 return RegExpRunStatus_Success_NotFound;
    1090             : 
    1091           0 :             if (matches) {
    1092           0 :                 (*matches)[0].start = start;
    1093           0 :                 (*matches)[0].limit = start + sourceLength;
    1094             : 
    1095           0 :                 matches->checkAgainst(length);
    1096           0 :             } else if (endIndex) {
    1097           0 :                 *endIndex = start + sourceLength;
    1098             :             }
    1099           0 :             return RegExpRunStatus_Success;
    1100             :         }
    1101             : 
    1102          19 :         int res = StringFindPattern(input, re->source, start);
    1103          19 :         if (res == -1)
    1104          16 :             return RegExpRunStatus_Success_NotFound;
    1105             : 
    1106           3 :         if (matches) {
    1107           3 :             (*matches)[0].start = res;
    1108           3 :             (*matches)[0].limit = res + sourceLength;
    1109             : 
    1110           3 :             matches->checkAgainst(length);
    1111           0 :         } else if (endIndex) {
    1112           0 :             *endIndex = res + sourceLength;
    1113             :         }
    1114           3 :         return RegExpRunStatus_Success;
    1115             :     }
    1116             : 
    1117             :     do {
    1118         245 :         jit::JitCode* code = re->compilation(mode, input->hasLatin1Chars()).jitCode;
    1119         245 :         if (!code)
    1120           0 :             break;
    1121             : 
    1122             :         RegExpRunStatus result;
    1123             :         {
    1124         490 :             AutoTraceLog logJIT(logger, TraceLogger_IrregexpExecute);
    1125         490 :             AutoCheckCannotGC nogc;
    1126         245 :             if (input->hasLatin1Chars()) {
    1127         233 :                 const Latin1Char* chars = input->latin1Chars(nogc);
    1128         233 :                 result = irregexp::ExecuteCode(cx, code, chars, start, length, matches, endIndex);
    1129             :             } else {
    1130          12 :                 const char16_t* chars = input->twoByteChars(nogc);
    1131          12 :                 result = irregexp::ExecuteCode(cx, code, chars, start, length, matches, endIndex);
    1132             :             }
    1133             :         }
    1134             : 
    1135         245 :         if (result == RegExpRunStatus_Error) {
    1136             :             // An 'Error' result is returned if a stack overflow guard or
    1137             :             // interrupt guard failed. If CheckOverRecursed doesn't throw, break
    1138             :             // out and retry the regexp in the bytecode interpreter, which can
    1139             :             // execute while tolerating future interrupts. Otherwise, if we keep
    1140             :             // getting interrupted we will never finish executing the regexp.
    1141           0 :             if (!jit::CheckOverRecursed(cx))
    1142           0 :                 return RegExpRunStatus_Error;
    1143           0 :             break;
    1144             :         }
    1145             : 
    1146         245 :         if (result == RegExpRunStatus_Success_NotFound)
    1147         120 :             return RegExpRunStatus_Success_NotFound;
    1148             : 
    1149         125 :         MOZ_ASSERT(result == RegExpRunStatus_Success);
    1150             : 
    1151         125 :         if (matches)
    1152          63 :             matches->checkAgainst(length);
    1153         125 :         return RegExpRunStatus_Success;
    1154             :     } while (false);
    1155             : 
    1156             :     // Compile bytecode for the RegExp if necessary.
    1157           0 :     if (!compileIfNecessary(cx, re, input, mode, ForceByteCode))
    1158           0 :         return RegExpRunStatus_Error;
    1159             : 
    1160           0 :     uint8_t* byteCode = re->compilation(mode, input->hasLatin1Chars()).byteCode;
    1161           0 :     AutoTraceLog logInterpreter(logger, TraceLogger_IrregexpExecute);
    1162             : 
    1163           0 :     AutoStableStringChars inputChars(cx);
    1164           0 :     if (!inputChars.init(cx, input))
    1165           0 :         return RegExpRunStatus_Error;
    1166             : 
    1167             :     RegExpRunStatus result;
    1168           0 :     if (inputChars.isLatin1()) {
    1169           0 :         const Latin1Char* chars = inputChars.latin1Range().begin().get();
    1170           0 :         result = irregexp::InterpretCode(cx, byteCode, chars, start, length, matches, endIndex);
    1171             :     } else {
    1172           0 :         const char16_t* chars = inputChars.twoByteRange().begin().get();
    1173           0 :         result = irregexp::InterpretCode(cx, byteCode, chars, start, length, matches, endIndex);
    1174             :     }
    1175             : 
    1176           0 :     if (result == RegExpRunStatus_Success && matches)
    1177           0 :         matches->checkAgainst(length);
    1178           0 :     return result;
    1179             : }
    1180             : 
    1181             : size_t
    1182           0 : RegExpShared::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
    1183             : {
    1184           0 :     size_t n = 0;
    1185             : 
    1186           0 :     for (size_t i = 0; i < ArrayLength(compilationArray); i++) {
    1187           0 :         const RegExpCompilation& compilation = compilationArray[i];
    1188           0 :         if (compilation.byteCode)
    1189           0 :             n += mallocSizeOf(compilation.byteCode);
    1190             :     }
    1191             : 
    1192           0 :     n += tables.sizeOfExcludingThis(mallocSizeOf);
    1193           0 :     for (size_t i = 0; i < tables.length(); i++)
    1194           0 :         n += mallocSizeOf(tables[i].get());
    1195             : 
    1196           0 :     return n;
    1197             : }
    1198             : 
    1199             : /* RegExpCompartment */
    1200             : 
    1201         315 : RegExpCompartment::RegExpCompartment(Zone* zone)
    1202             :   : matchResultTemplateObject_(nullptr),
    1203             :     optimizableRegExpPrototypeShape_(nullptr),
    1204         315 :     optimizableRegExpInstanceShape_(nullptr)
    1205         315 : {}
    1206             : 
    1207             : ArrayObject*
    1208           6 : RegExpCompartment::createMatchResultTemplateObject(JSContext* cx)
    1209             : {
    1210           6 :     MOZ_ASSERT(!matchResultTemplateObject_);
    1211             : 
    1212             :     /* Create template array object */
    1213          12 :     RootedArrayObject templateObject(cx, NewDenseUnallocatedArray(cx, RegExpObject::MaxPairCount,
    1214          12 :                                                                   nullptr, TenuredObject));
    1215           6 :     if (!templateObject)
    1216           0 :         return matchResultTemplateObject_; // = nullptr
    1217             : 
    1218             :     // Create a new group for the template.
    1219          12 :     Rooted<TaggedProto> proto(cx, templateObject->taggedProto());
    1220           6 :     ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, templateObject->getClass(), proto);
    1221           6 :     if (!group)
    1222           0 :         return matchResultTemplateObject_; // = nullptr
    1223           6 :     templateObject->setGroup(group);
    1224             : 
    1225             :     /* Set dummy index property */
    1226          12 :     RootedValue index(cx, Int32Value(0));
    1227           6 :     if (!NativeDefineProperty(cx, templateObject, cx->names().index, index, nullptr, nullptr,
    1228             :                               JSPROP_ENUMERATE))
    1229             :     {
    1230           0 :         return matchResultTemplateObject_; // = nullptr
    1231             :     }
    1232             : 
    1233             :     /* Set dummy input property */
    1234          12 :     RootedValue inputVal(cx, StringValue(cx->runtime()->emptyString));
    1235           6 :     if (!NativeDefineProperty(cx, templateObject, cx->names().input, inputVal, nullptr, nullptr,
    1236             :                               JSPROP_ENUMERATE))
    1237             :     {
    1238           0 :         return matchResultTemplateObject_; // = nullptr
    1239             :     }
    1240             : 
    1241             :     // Make sure that the properties are in the right slots.
    1242          12 :     DebugOnly<Shape*> shape = templateObject->lastProperty();
    1243           6 :     MOZ_ASSERT(shape->previous()->slot() == 0 &&
    1244             :                shape->previous()->propidRef() == NameToId(cx->names().index));
    1245           6 :     MOZ_ASSERT(shape->slot() == 1 &&
    1246             :                shape->propidRef() == NameToId(cx->names().input));
    1247             : 
    1248             :     // Make sure type information reflects the indexed properties which might
    1249             :     // be added.
    1250           6 :     AddTypePropertyId(cx, templateObject, JSID_VOID, TypeSet::StringType());
    1251           6 :     AddTypePropertyId(cx, templateObject, JSID_VOID, TypeSet::UndefinedType());
    1252             : 
    1253           6 :     matchResultTemplateObject_.set(templateObject);
    1254             : 
    1255           6 :     return matchResultTemplateObject_;
    1256             : }
    1257             : 
    1258             : bool
    1259          31 : RegExpZone::init()
    1260             : {
    1261          31 :     if (!set_.init(0))
    1262           0 :         return false;
    1263             : 
    1264          31 :     return true;
    1265             : }
    1266             : 
    1267             : void
    1268           0 : RegExpCompartment::sweep(JSRuntime* rt)
    1269             : {
    1270           0 :     if (matchResultTemplateObject_ &&
    1271           0 :         IsAboutToBeFinalized(&matchResultTemplateObject_))
    1272             :     {
    1273           0 :         matchResultTemplateObject_.set(nullptr);
    1274             :     }
    1275             : 
    1276           0 :     if (optimizableRegExpPrototypeShape_ &&
    1277           0 :         IsAboutToBeFinalized(&optimizableRegExpPrototypeShape_))
    1278             :     {
    1279           0 :         optimizableRegExpPrototypeShape_.set(nullptr);
    1280             :     }
    1281             : 
    1282           0 :     if (optimizableRegExpInstanceShape_ &&
    1283           0 :         IsAboutToBeFinalized(&optimizableRegExpInstanceShape_))
    1284             :     {
    1285           0 :         optimizableRegExpInstanceShape_.set(nullptr);
    1286             :     }
    1287           0 : }
    1288             : 
    1289             : RegExpShared*
    1290          79 : RegExpZone::get(JSContext* cx, HandleAtom source, RegExpFlag flags)
    1291             : {
    1292          79 :     DependentAddPtr<Set> p(cx, set_, Key(source, flags));
    1293          79 :     if (p)
    1294          10 :         return *p;
    1295             : 
    1296          69 :     auto shared = Allocate<RegExpShared>(cx);
    1297          69 :     if (!shared)
    1298           0 :         return nullptr;
    1299             : 
    1300          69 :     new (shared) RegExpShared(source, flags);
    1301             : 
    1302          69 :     if (!p.add(cx, set_, Key(source, flags), shared)) {
    1303           0 :         ReportOutOfMemory(cx);
    1304           0 :         return nullptr;
    1305             :     }
    1306             : 
    1307          69 :     return shared;
    1308             : }
    1309             : 
    1310             : RegExpShared*
    1311           0 : RegExpZone::get(JSContext* cx, HandleAtom atom, JSString* opt)
    1312             : {
    1313           0 :     RegExpFlag flags = RegExpFlag(0);
    1314           0 :     if (opt && !ParseRegExpFlags(cx, opt, &flags))
    1315           0 :         return nullptr;
    1316             : 
    1317           0 :     return get(cx, atom, flags);
    1318             : }
    1319             : 
    1320             : size_t
    1321           0 : RegExpZone::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
    1322             : {
    1323           0 :     return set_.sizeOfExcludingThis(mallocSizeOf);
    1324             : }
    1325             : 
    1326          31 : RegExpZone::RegExpZone(Zone* zone)
    1327          31 :   : set_(zone, zone->runtimeFromActiveCooperatingThread())
    1328          31 : {}
    1329             : 
    1330             : /* Functions */
    1331             : 
    1332             : JSObject*
    1333         223 : js::CloneRegExpObject(JSContext* cx, Handle<RegExpObject*> regex)
    1334             : {
    1335             :     // Unlike RegExpAlloc, all clones must use |regex|'s group.
    1336         446 :     RootedObjectGroup group(cx, regex->group());
    1337         446 :     Rooted<RegExpObject*> clone(cx, NewObjectWithGroup<RegExpObject>(cx, group, GenericObject));
    1338         223 :     if (!clone)
    1339           0 :         return nullptr;
    1340         223 :     clone->initPrivate(nullptr);
    1341             : 
    1342         223 :     if (!EmptyShape::ensureInitialCustomShape<RegExpObject>(cx, clone))
    1343           0 :         return nullptr;
    1344             : 
    1345         223 :     RegExpShared* shared = RegExpObject::getShared(cx, regex);
    1346         223 :     if (!shared)
    1347           0 :         return nullptr;
    1348             : 
    1349         223 :     clone->initAndZeroLastIndex(shared->getSource(), shared->getFlags(), cx);
    1350         223 :     clone->setShared(*shared);
    1351             : 
    1352         223 :     return clone;
    1353             : }
    1354             : 
    1355             : static bool
    1356           1 : HandleRegExpFlag(RegExpFlag flag, RegExpFlag* flags)
    1357             : {
    1358           1 :     if (*flags & flag)
    1359           0 :         return false;
    1360           1 :     *flags = RegExpFlag(*flags | flag);
    1361           1 :     return true;
    1362             : }
    1363             : 
    1364             : template <typename CharT>
    1365             : static size_t
    1366           1 : ParseRegExpFlags(const CharT* chars, size_t length, RegExpFlag* flagsOut, char16_t* lastParsedOut)
    1367             : {
    1368           1 :     *flagsOut = RegExpFlag(0);
    1369             : 
    1370           2 :     for (size_t i = 0; i < length; i++) {
    1371           1 :         *lastParsedOut = chars[i];
    1372           1 :         switch (chars[i]) {
    1373             :           case 'i':
    1374           1 :             if (!HandleRegExpFlag(IgnoreCaseFlag, flagsOut))
    1375           0 :                 return false;
    1376           1 :             break;
    1377             :           case 'g':
    1378           0 :             if (!HandleRegExpFlag(GlobalFlag, flagsOut))
    1379           0 :                 return false;
    1380           0 :             break;
    1381             :           case 'm':
    1382           0 :             if (!HandleRegExpFlag(MultilineFlag, flagsOut))
    1383           0 :                 return false;
    1384           0 :             break;
    1385             :           case 'y':
    1386           0 :             if (!HandleRegExpFlag(StickyFlag, flagsOut))
    1387           0 :                 return false;
    1388           0 :             break;
    1389             :           case 'u':
    1390           0 :             if (!HandleRegExpFlag(UnicodeFlag, flagsOut))
    1391           0 :                 return false;
    1392           0 :             break;
    1393             :           default:
    1394           0 :             return false;
    1395             :         }
    1396             :     }
    1397             : 
    1398           1 :     return true;
    1399             : }
    1400             : 
    1401             : bool
    1402           1 : js::ParseRegExpFlags(JSContext* cx, JSString* flagStr, RegExpFlag* flagsOut)
    1403             : {
    1404           1 :     JSLinearString* linear = flagStr->ensureLinear(cx);
    1405           1 :     if (!linear)
    1406           0 :         return false;
    1407             : 
    1408           1 :     size_t len = linear->length();
    1409             : 
    1410             :     bool ok;
    1411             :     char16_t lastParsed;
    1412           1 :     if (linear->hasLatin1Chars()) {
    1413           2 :         AutoCheckCannotGC nogc;
    1414           1 :         ok = ::ParseRegExpFlags(linear->latin1Chars(nogc), len, flagsOut, &lastParsed);
    1415             :     } else {
    1416           0 :         AutoCheckCannotGC nogc;
    1417           0 :         ok = ::ParseRegExpFlags(linear->twoByteChars(nogc), len, flagsOut, &lastParsed);
    1418             :     }
    1419             : 
    1420           1 :     if (!ok) {
    1421           0 :         TwoByteChars range(&lastParsed, 1);
    1422           0 :         UniqueChars utf8(JS::CharsToNewUTF8CharsZ(nullptr, range).c_str());
    1423           0 :         if (!utf8)
    1424           0 :             return false;
    1425           0 :         JS_ReportErrorFlagsAndNumberUTF8(cx, JSREPORT_ERROR, GetErrorMessage, nullptr,
    1426           0 :                                          JSMSG_BAD_REGEXP_FLAG, utf8.get());
    1427           0 :         return false;
    1428             :     }
    1429             : 
    1430           1 :     return true;
    1431             : }
    1432             : 
    1433             : template<XDRMode mode>
    1434             : bool
    1435         339 : js::XDRScriptRegExpObject(XDRState<mode>* xdr, MutableHandle<RegExpObject*> objp)
    1436             : {
    1437             :     /* NB: Keep this in sync with CloneScriptRegExpObject. */
    1438             : 
    1439         677 :     RootedAtom source(xdr->cx());
    1440         339 :     uint32_t flagsword = 0;
    1441             : 
    1442             :     if (mode == XDR_ENCODE) {
    1443          88 :         MOZ_ASSERT(objp);
    1444          88 :         RegExpObject& reobj = *objp;
    1445          88 :         source = reobj.getSource();
    1446          88 :         flagsword = reobj.getFlags();
    1447             :     }
    1448         339 :     if (!XDRAtom(xdr, &source) || !xdr->codeUint32(&flagsword))
    1449           0 :         return false;
    1450             :     if (mode == XDR_DECODE) {
    1451         251 :         RegExpFlag flags = RegExpFlag(flagsword);
    1452         251 :         const ReadOnlyCompileOptions* options = nullptr;
    1453         251 :         if (xdr->hasOptions())
    1454         156 :             options = &xdr->options();
    1455         501 :         RegExpObject* reobj = RegExpObject::create(xdr->cx(), source, flags,
    1456             :                                                    options, nullptr, xdr->lifoAlloc(),
    1457         250 :                                                    TenuredObject);
    1458         250 :         if (!reobj)
    1459           0 :             return false;
    1460             : 
    1461         250 :         objp.set(reobj);
    1462             :     }
    1463         338 :     return true;
    1464             : }
    1465             : 
    1466             : template bool
    1467             : js::XDRScriptRegExpObject(XDRState<XDR_ENCODE>* xdr, MutableHandle<RegExpObject*> objp);
    1468             : 
    1469             : template bool
    1470             : js::XDRScriptRegExpObject(XDRState<XDR_DECODE>* xdr, MutableHandle<RegExpObject*> objp);
    1471             : 
    1472             : JSObject*
    1473         255 : js::CloneScriptRegExpObject(JSContext* cx, RegExpObject& reobj)
    1474             : {
    1475             :     /* NB: Keep this in sync with XDRScriptRegExpObject. */
    1476             : 
    1477         510 :     RootedAtom source(cx, reobj.getSource());
    1478         255 :     cx->markAtom(source);
    1479             : 
    1480         510 :     return RegExpObject::create(cx, source, reobj.getFlags(),
    1481             :                                 nullptr, nullptr, cx->tempLifoAlloc(),
    1482         510 :                                 TenuredObject);
    1483             : }
    1484             : 
    1485             : JS_FRIEND_API(RegExpShared*)
    1486           0 : js::RegExpToSharedNonInline(JSContext* cx, HandleObject obj)
    1487             : {
    1488           0 :     return RegExpToShared(cx, obj);
    1489             : }
    1490             : 
    1491             : JS::ubi::Node::Size
    1492           0 : JS::ubi::Concrete<RegExpShared>::size(mozilla::MallocSizeOf mallocSizeOf) const
    1493             : {
    1494           0 :     return js::gc::Arena::thingSize(gc::AllocKind::REGEXP_SHARED) +
    1495           0 :         get().sizeOfExcludingThis(mallocSizeOf);
    1496             : }

Generated by: LCOV version 1.13