LCOV - code coverage report
Current view: top level - js/src/vm - RegExpStatics.h (source / functions) Hit Total Coverage
Test: output.info Lines: 41 138 29.7 %
Date: 2017-07-14 16:53:18 Functions: 6 23 26.1 %
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             : #ifndef vm_RegExpStatics_h
       8             : #define vm_RegExpStatics_h
       9             : 
      10             : #include "jscntxt.h"
      11             : 
      12             : #include "gc/Marking.h"
      13             : #include "vm/MatchPairs.h"
      14             : #include "vm/RegExpShared.h"
      15             : #include "vm/Runtime.h"
      16             : 
      17             : namespace js {
      18             : 
      19             : class GlobalObject;
      20             : class RegExpStaticsObject;
      21             : 
      22           0 : class RegExpStatics
      23             : {
      24             :     /* The latest RegExp output, set after execution. */
      25             :     VectorMatchPairs        matches;
      26             :     HeapPtr<JSLinearString*>  matchesInput;
      27             : 
      28             :     /*
      29             :      * The previous RegExp input, used to resolve lazy state.
      30             :      * A raw RegExpShared cannot be stored because it may be in
      31             :      * a different compartment via evalcx().
      32             :      */
      33             :     HeapPtr<JSAtom*>          lazySource;
      34             :     RegExpFlag              lazyFlags;
      35             :     size_t                  lazyIndex;
      36             : 
      37             :     /* The latest RegExp input, set before execution. */
      38             :     HeapPtr<JSString*>        pendingInput;
      39             : 
      40             :     /*
      41             :      * If non-zero, |matchesInput| and the |lazy*| fields may be used
      42             :      * to replay the last executed RegExp, and |matches| is invalid.
      43             :      */
      44             :     int32_t                 pendingLazyEvaluation;
      45             : 
      46             :   public:
      47          13 :     RegExpStatics() { clear(); }
      48             :     static RegExpStaticsObject* create(JSContext* cx, Handle<GlobalObject*> parent);
      49             : 
      50             :   private:
      51             :     bool executeLazy(JSContext* cx);
      52             : 
      53             :     inline void checkInvariants();
      54             : 
      55             :     /*
      56             :      * Check whether a match for pair |pairNum| occurred.  If so, allocate and
      57             :      * store the match string in |*out|; otherwise place |undefined| in |*out|.
      58             :      */
      59             :     bool makeMatch(JSContext* cx, size_t pairNum, MutableHandleValue out);
      60             :     bool createDependent(JSContext* cx, size_t start, size_t end, MutableHandleValue out);
      61             : 
      62             :     struct InitBuffer {};
      63             :     explicit RegExpStatics(InitBuffer) {}
      64             : 
      65             :   public:
      66             :     /* Mutators. */
      67             :     inline void updateLazily(JSContext* cx, JSLinearString* input,
      68             :                              RegExpShared* shared, size_t lastIndex);
      69             :     inline bool updateFromMatchPairs(JSContext* cx, JSLinearString* input, MatchPairs& newPairs);
      70             : 
      71             :     inline void clear();
      72             : 
      73             :     /* Corresponds to JSAPI functionality to set the pending RegExp input. */
      74           0 :     void reset(JSContext* cx, JSString* newInput) {
      75           0 :         clear();
      76           0 :         pendingInput = newInput;
      77           0 :         checkInvariants();
      78           0 :     }
      79             : 
      80             :     inline void setPendingInput(JSString* newInput);
      81             : 
      82             :   public:
      83             :     /* Default match accessor. */
      84             :     const MatchPairs& getMatches() const {
      85             :         /* Safe: only used by String methods, which do not set lazy mode. */
      86             :         MOZ_ASSERT(!pendingLazyEvaluation);
      87             :         return matches;
      88             :     }
      89             : 
      90             :     JSString* getPendingInput() const { return pendingInput; }
      91             : 
      92           0 :     void trace(JSTracer* trc) {
      93             :         /*
      94             :          * Changes to this function must also be reflected in
      95             :          * RegExpStatics::AutoRooter::trace().
      96             :          */
      97           0 :         TraceNullableEdge(trc, &matchesInput, "res->matchesInput");
      98           0 :         TraceNullableEdge(trc, &lazySource, "res->lazySource");
      99           0 :         TraceNullableEdge(trc, &pendingInput, "res->pendingInput");
     100           0 :     }
     101             : 
     102             :     /* Value creators. */
     103             : 
     104             :     bool createPendingInput(JSContext* cx, MutableHandleValue out);
     105             :     bool createLastMatch(JSContext* cx, MutableHandleValue out);
     106             :     bool createLastParen(JSContext* cx, MutableHandleValue out);
     107             :     bool createParen(JSContext* cx, size_t pairNum, MutableHandleValue out);
     108             :     bool createLeftContext(JSContext* cx, MutableHandleValue out);
     109             :     bool createRightContext(JSContext* cx, MutableHandleValue out);
     110             : 
     111             :     /* Infallible substring creators. */
     112             : 
     113             :     void getParen(size_t pairNum, JSSubString* out) const;
     114             :     void getLastMatch(JSSubString* out) const;
     115             :     void getLastParen(JSSubString* out) const;
     116             :     void getLeftContext(JSSubString* out) const;
     117             :     void getRightContext(JSSubString* out) const;
     118             : 
     119           0 :     static size_t offsetOfPendingInput() {
     120           0 :         return offsetof(RegExpStatics, pendingInput);
     121             :     }
     122             : 
     123           0 :     static size_t offsetOfMatchesInput() {
     124           0 :         return offsetof(RegExpStatics, matchesInput);
     125             :     }
     126             : 
     127           0 :     static size_t offsetOfLazySource() {
     128           0 :         return offsetof(RegExpStatics, lazySource);
     129             :     }
     130             : 
     131           0 :     static size_t offsetOfLazyFlags() {
     132           0 :         return offsetof(RegExpStatics, lazyFlags);
     133             :     }
     134             : 
     135           0 :     static size_t offsetOfLazyIndex() {
     136           0 :         return offsetof(RegExpStatics, lazyIndex);
     137             :     }
     138             : 
     139           0 :     static size_t offsetOfPendingLazyEvaluation() {
     140           0 :         return offsetof(RegExpStatics, pendingLazyEvaluation);
     141             :     }
     142             : };
     143             : 
     144             : inline bool
     145           6 : RegExpStatics::createDependent(JSContext* cx, size_t start, size_t end, MutableHandleValue out)
     146             : {
     147             :     /* Private function: caller must perform lazy evaluation. */
     148           6 :     MOZ_ASSERT(!pendingLazyEvaluation);
     149             : 
     150           6 :     MOZ_ASSERT(start <= end);
     151           6 :     MOZ_ASSERT(end <= matchesInput->length());
     152           6 :     JSString* str = NewDependentString(cx, matchesInput, start, end - start);
     153           6 :     if (!str)
     154           0 :         return false;
     155           6 :     out.setString(str);
     156           6 :     return true;
     157             : }
     158             : 
     159             : inline bool
     160           0 : RegExpStatics::createPendingInput(JSContext* cx, MutableHandleValue out)
     161             : {
     162             :     /* Lazy evaluation need not be resolved to return the input. */
     163           0 :     out.setString(pendingInput ? pendingInput.get() : cx->runtime()->emptyString.ref());
     164           0 :     return true;
     165             : }
     166             : 
     167             : inline bool
     168           0 : RegExpStatics::makeMatch(JSContext* cx, size_t pairNum, MutableHandleValue out)
     169             : {
     170             :     /* Private function: caller must perform lazy evaluation. */
     171           0 :     MOZ_ASSERT(!pendingLazyEvaluation);
     172             : 
     173           0 :     if (matches.empty() || pairNum >= matches.pairCount() || matches[pairNum].isUndefined()) {
     174           0 :         out.setUndefined();
     175           0 :         return true;
     176             :     }
     177             : 
     178           0 :     const MatchPair& pair = matches[pairNum];
     179           0 :     return createDependent(cx, pair.start, pair.limit, out);
     180             : }
     181             : 
     182             : inline bool
     183           0 : RegExpStatics::createLastMatch(JSContext* cx, MutableHandleValue out)
     184             : {
     185           0 :     if (!executeLazy(cx))
     186           0 :         return false;
     187           0 :     return makeMatch(cx, 0, out);
     188             : }
     189             : 
     190             : inline bool
     191           0 : RegExpStatics::createLastParen(JSContext* cx, MutableHandleValue out)
     192             : {
     193           0 :     if (!executeLazy(cx))
     194           0 :         return false;
     195             : 
     196           0 :     if (matches.empty() || matches.pairCount() == 1) {
     197           0 :         out.setString(cx->runtime()->emptyString);
     198           0 :         return true;
     199             :     }
     200           0 :     const MatchPair& pair = matches[matches.pairCount() - 1];
     201           0 :     if (pair.start == -1) {
     202           0 :         out.setString(cx->runtime()->emptyString);
     203           0 :         return true;
     204             :     }
     205           0 :     MOZ_ASSERT(pair.start >= 0 && pair.limit >= 0);
     206           0 :     MOZ_ASSERT(pair.limit >= pair.start);
     207           0 :     return createDependent(cx, pair.start, pair.limit, out);
     208             : }
     209             : 
     210             : inline bool
     211           0 : RegExpStatics::createParen(JSContext* cx, size_t pairNum, MutableHandleValue out)
     212             : {
     213           0 :     MOZ_ASSERT(pairNum >= 1);
     214           0 :     if (!executeLazy(cx))
     215           0 :         return false;
     216             : 
     217           0 :     if (matches.empty() || pairNum >= matches.pairCount()) {
     218           0 :         out.setString(cx->runtime()->emptyString);
     219           0 :         return true;
     220             :     }
     221           0 :     return makeMatch(cx, pairNum, out);
     222             : }
     223             : 
     224             : inline bool
     225           0 : RegExpStatics::createLeftContext(JSContext* cx, MutableHandleValue out)
     226             : {
     227           0 :     if (!executeLazy(cx))
     228           0 :         return false;
     229             : 
     230           0 :     if (matches.empty()) {
     231           0 :         out.setString(cx->runtime()->emptyString);
     232           0 :         return true;
     233             :     }
     234           0 :     if (matches[0].start < 0) {
     235           0 :         out.setUndefined();
     236           0 :         return true;
     237             :     }
     238           0 :     return createDependent(cx, 0, matches[0].start, out);
     239             : }
     240             : 
     241             : inline bool
     242           6 : RegExpStatics::createRightContext(JSContext* cx, MutableHandleValue out)
     243             : {
     244           6 :     if (!executeLazy(cx))
     245           0 :         return false;
     246             : 
     247           6 :     if (matches.empty()) {
     248           0 :         out.setString(cx->runtime()->emptyString);
     249           0 :         return true;
     250             :     }
     251           6 :     if (matches[0].limit < 0) {
     252           0 :         out.setUndefined();
     253           0 :         return true;
     254             :     }
     255           6 :     return createDependent(cx, matches[0].limit, matchesInput->length(), out);
     256             : }
     257             : 
     258             : inline void
     259             : RegExpStatics::getParen(size_t pairNum, JSSubString* out) const
     260             : {
     261             :     MOZ_ASSERT(!pendingLazyEvaluation);
     262             : 
     263             :     MOZ_ASSERT(pairNum >= 1 && pairNum < matches.pairCount());
     264             :     const MatchPair& pair = matches[pairNum];
     265             :     if (pair.isUndefined()) {
     266             :         out->initEmpty(matchesInput);
     267             :         return;
     268             :     }
     269             :     out->init(matchesInput, pair.start, pair.length());
     270             : }
     271             : 
     272             : inline void
     273             : RegExpStatics::getLastMatch(JSSubString* out) const
     274             : {
     275             :     MOZ_ASSERT(!pendingLazyEvaluation);
     276             : 
     277             :     if (matches.empty()) {
     278             :         out->initEmpty(matchesInput);
     279             :         return;
     280             :     }
     281             :     MOZ_ASSERT(matchesInput);
     282             :     MOZ_ASSERT(matches[0].limit >= matches[0].start);
     283             :     out->init(matchesInput, matches[0].start, matches[0].length());
     284             : }
     285             : 
     286             : inline void
     287             : RegExpStatics::getLastParen(JSSubString* out) const
     288             : {
     289             :     MOZ_ASSERT(!pendingLazyEvaluation);
     290             : 
     291             :     /* Note: the first pair is the whole match. */
     292             :     if (matches.empty() || matches.pairCount() == 1) {
     293             :         out->initEmpty(matchesInput);
     294             :         return;
     295             :     }
     296             :     getParen(matches.parenCount(), out);
     297             : }
     298             : 
     299             : inline void
     300             : RegExpStatics::getLeftContext(JSSubString* out) const
     301             : {
     302             :     MOZ_ASSERT(!pendingLazyEvaluation);
     303             : 
     304             :     if (matches.empty()) {
     305             :         out->initEmpty(matchesInput);
     306             :         return;
     307             :     }
     308             :     out->init(matchesInput, 0, matches[0].start);
     309             : }
     310             : 
     311             : inline void
     312             : RegExpStatics::getRightContext(JSSubString* out) const
     313             : {
     314             :     MOZ_ASSERT(!pendingLazyEvaluation);
     315             : 
     316             :     if (matches.empty()) {
     317             :         out->initEmpty(matchesInput);
     318             :         return;
     319             :     }
     320             :     MOZ_ASSERT(matches[0].limit <= int(matchesInput->length()));
     321             :     size_t length = matchesInput->length() - matches[0].limit;
     322             :     out->init(matchesInput, matches[0].limit, length);
     323             : }
     324             : 
     325             : inline void
     326          62 : RegExpStatics::updateLazily(JSContext* cx, JSLinearString* input,
     327             :                             RegExpShared* shared, size_t lastIndex)
     328             : {
     329          62 :     MOZ_ASSERT(input && shared);
     330             : 
     331          62 :     BarrieredSetPair<JSString, JSLinearString>(cx->zone(),
     332             :                                                pendingInput, input,
     333          62 :                                                matchesInput, input);
     334             : 
     335          62 :     lazySource = shared->source;
     336          62 :     lazyFlags = shared->flags;
     337          62 :     lazyIndex = lastIndex;
     338          62 :     pendingLazyEvaluation = 1;
     339          62 : }
     340             : 
     341             : inline bool
     342          60 : RegExpStatics::updateFromMatchPairs(JSContext* cx, JSLinearString* input, MatchPairs& newPairs)
     343             : {
     344          60 :     MOZ_ASSERT(input);
     345             : 
     346             :     /* Unset all lazy state. */
     347          60 :     pendingLazyEvaluation = false;
     348          60 :     this->lazySource = nullptr;
     349          60 :     this->lazyIndex = size_t(-1);
     350             : 
     351          60 :     BarrieredSetPair<JSString, JSLinearString>(cx->zone(),
     352             :                                                pendingInput, input,
     353          60 :                                                matchesInput, input);
     354             : 
     355          60 :     if (!matches.initArrayFrom(newPairs)) {
     356           0 :         ReportOutOfMemory(cx);
     357           0 :         return false;
     358             :     }
     359             : 
     360          60 :     return true;
     361             : }
     362             : 
     363             : inline void
     364          13 : RegExpStatics::clear()
     365             : {
     366          13 :     matches.forgetArray();
     367          13 :     matchesInput = nullptr;
     368          13 :     lazySource = nullptr;
     369          13 :     lazyFlags = RegExpFlag(0);
     370          13 :     lazyIndex = size_t(-1);
     371          13 :     pendingInput = nullptr;
     372          13 :     pendingLazyEvaluation = false;
     373          13 : }
     374             : 
     375             : inline void
     376           0 : RegExpStatics::setPendingInput(JSString* newInput)
     377             : {
     378           0 :     pendingInput = newInput;
     379           0 : }
     380             : 
     381             : inline void
     382           0 : RegExpStatics::checkInvariants()
     383             : {
     384             : #ifdef DEBUG
     385           0 :     if (pendingLazyEvaluation) {
     386           0 :         MOZ_ASSERT(lazySource);
     387           0 :         MOZ_ASSERT(matchesInput);
     388           0 :         MOZ_ASSERT(lazyIndex != size_t(-1));
     389           0 :         return;
     390             :     }
     391             : 
     392           0 :     if (matches.empty()) {
     393           0 :         MOZ_ASSERT(!matchesInput);
     394           0 :         return;
     395             :     }
     396             : 
     397             :     /* Pair count is non-zero, so there must be match pairs input. */
     398           0 :     MOZ_ASSERT(matchesInput);
     399           0 :     size_t mpiLen = matchesInput->length();
     400             : 
     401             :     /* Both members of the first pair must be non-negative. */
     402           0 :     MOZ_ASSERT(!matches[0].isUndefined());
     403           0 :     MOZ_ASSERT(matches[0].limit >= 0);
     404             : 
     405             :     /* Present pairs must be valid. */
     406           0 :     for (size_t i = 0; i < matches.pairCount(); i++) {
     407           0 :         if (matches[i].isUndefined())
     408           0 :             continue;
     409           0 :         const MatchPair& pair = matches[i];
     410           0 :         MOZ_ASSERT(mpiLen >= size_t(pair.limit) && pair.limit >= pair.start && pair.start >= 0);
     411             :     }
     412             : #endif /* DEBUG */
     413             : }
     414             : 
     415             : } /* namespace js */
     416             : 
     417             : #endif /* vm_RegExpStatics_h */

Generated by: LCOV version 1.13