js/src/vm/RegExpStatics.h

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     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/. */
     7 #ifndef vm_RegExpStatics_h
     8 #define vm_RegExpStatics_h
    10 #include "gc/Marking.h"
    11 #include "vm/MatchPairs.h"
    12 #include "vm/RegExpObject.h"
    13 #include "vm/Runtime.h"
    15 namespace js {
    17 class GlobalObject;
    19 class RegExpStatics
    20 {
    21     /* The latest RegExp output, set after execution. */
    22     VectorMatchPairs        matches;
    23     HeapPtr<JSLinearString> matchesInput;
    25     /*
    26      * The previous RegExp input, used to resolve lazy state.
    27      * A raw RegExpShared cannot be stored because it may be in
    28      * a different compartment via evalcx().
    29      */
    30     HeapPtr<JSAtom>         lazySource;
    31     RegExpFlag              lazyFlags;
    32     size_t                  lazyIndex;
    34     /* The latest RegExp input, set before execution. */
    35     HeapPtr<JSString>       pendingInput;
    36     RegExpFlag              flags;
    38     /*
    39      * If true, |matchesInput| and the |lazy*| fields may be used
    40      * to replay the last executed RegExp, and |matches| is invalid.
    41      */
    42     bool                    pendingLazyEvaluation;
    44     /* Linkage for preserving RegExpStatics during nested RegExp execution. */
    45     RegExpStatics           *bufferLink;
    46     bool                    copied;
    48   public:
    49     RegExpStatics() : bufferLink(nullptr), copied(false) { clear(); }
    50     static JSObject *create(JSContext *cx, GlobalObject *parent);
    52   private:
    53     bool executeLazy(JSContext *cx);
    55     inline void aboutToWrite();
    56     inline void copyTo(RegExpStatics &dst);
    58     inline void restore();
    59     bool save(JSContext *cx, RegExpStatics *buffer) {
    60         JS_ASSERT(!buffer->copied && !buffer->bufferLink);
    61         buffer->bufferLink = bufferLink;
    62         bufferLink = buffer;
    63         if (!buffer->matches.allocOrExpandArray(matches.length())) {
    64             js_ReportOutOfMemory(cx);
    65             return false;
    66         }
    67         return true;
    68     }
    70     inline void checkInvariants();
    72     /*
    73      * Check whether the index at |checkValidIndex| is valid (>= 0).
    74      * If so, construct a string for it and place it in |*out|.
    75      * If not, place undefined in |*out|.
    76      */
    77     bool makeMatch(JSContext *cx, size_t checkValidIndex, size_t pairNum, MutableHandleValue out);
    78     bool createDependent(JSContext *cx, size_t start, size_t end, MutableHandleValue out);
    80     void markFlagsSet(JSContext *cx);
    82     struct InitBuffer {};
    83     explicit RegExpStatics(InitBuffer) : bufferLink(nullptr), copied(false) {}
    85     friend class PreserveRegExpStatics;
    86     friend class AutoRegExpStaticsBuffer;
    88   public:
    89     /* Mutators. */
    90     inline void updateLazily(JSContext *cx, JSLinearString *input,
    91                              RegExpShared *shared, size_t lastIndex);
    92     inline bool updateFromMatchPairs(JSContext *cx, JSLinearString *input, MatchPairs &newPairs);
    94     void setMultiline(JSContext *cx, bool enabled) {
    95         aboutToWrite();
    96         if (enabled) {
    97             flags = RegExpFlag(flags | MultilineFlag);
    98             markFlagsSet(cx);
    99         } else {
   100             flags = RegExpFlag(flags & ~MultilineFlag);
   101         }
   102     }
   104     inline void clear();
   106     /* Corresponds to JSAPI functionality to set the pending RegExp input. */
   107     void reset(JSContext *cx, JSString *newInput, bool newMultiline) {
   108         aboutToWrite();
   109         clear();
   110         pendingInput = newInput;
   111         setMultiline(cx, newMultiline);
   112         checkInvariants();
   113     }
   115     inline void setPendingInput(JSString *newInput);
   117   public:
   118     /* Default match accessor. */
   119     const MatchPairs &getMatches() const {
   120         /* Safe: only used by String methods, which do not set lazy mode. */
   121         JS_ASSERT(!pendingLazyEvaluation);
   122         return matches;
   123     }
   125     JSString *getPendingInput() const { return pendingInput; }
   127     RegExpFlag getFlags() const { return flags; }
   128     bool multiline() const { return flags & MultilineFlag; }
   130     /* Returns whether results for a non-empty match are present. */
   131     bool matched() const {
   132         /* Safe: only used by String methods, which do not set lazy mode. */
   133         JS_ASSERT(!pendingLazyEvaluation);
   134         JS_ASSERT(matches.pairCount() > 0);
   135         return matches[0].limit - matches[0].start > 0;
   136     }
   138     void mark(JSTracer *trc) {
   139         /*
   140          * Changes to this function must also be reflected in
   141          * RegExpStatics::AutoRooter::trace().
   142          */
   143         if (matchesInput)
   144             MarkString(trc, &matchesInput, "res->matchesInput");
   145         if (lazySource)
   146             MarkString(trc, &lazySource, "res->lazySource");
   147         if (pendingInput)
   148             MarkString(trc, &pendingInput, "res->pendingInput");
   149     }
   151     /* Value creators. */
   153     bool createPendingInput(JSContext *cx, MutableHandleValue out);
   154     bool createLastMatch(JSContext *cx, MutableHandleValue out);
   155     bool createLastParen(JSContext *cx, MutableHandleValue out);
   156     bool createParen(JSContext *cx, size_t pairNum, MutableHandleValue out);
   157     bool createLeftContext(JSContext *cx, MutableHandleValue out);
   158     bool createRightContext(JSContext *cx, MutableHandleValue out);
   160     /* Infallible substring creators. */
   162     void getParen(size_t pairNum, JSSubString *out) const;
   163     void getLastMatch(JSSubString *out) const;
   164     void getLastParen(JSSubString *out) const;
   165     void getLeftContext(JSSubString *out) const;
   166     void getRightContext(JSSubString *out) const;
   167 };
   169 class AutoRegExpStaticsBuffer : private JS::CustomAutoRooter
   170 {
   171   public:
   172     explicit AutoRegExpStaticsBuffer(JSContext *cx
   173                                      MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
   174       : CustomAutoRooter(cx), statics(RegExpStatics::InitBuffer())
   175     {
   176         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
   177     }
   179     RegExpStatics& getStatics() { return statics; }
   181   private:
   182     virtual void trace(JSTracer *trc) {
   183         if (statics.matchesInput) {
   184             MarkStringRoot(trc, reinterpret_cast<JSString**>(&statics.matchesInput),
   185                                 "AutoRegExpStaticsBuffer matchesInput");
   186         }
   187         if (statics.lazySource) {
   188             MarkStringRoot(trc, reinterpret_cast<JSString**>(&statics.lazySource),
   189                                 "AutoRegExpStaticsBuffer lazySource");
   190         }
   191         if (statics.pendingInput) {
   192             MarkStringRoot(trc, reinterpret_cast<JSString**>(&statics.pendingInput),
   193                                 "AutoRegExpStaticsBuffer pendingInput");
   194         }
   195     }
   197     RegExpStatics statics;
   198     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
   199 };
   201 class PreserveRegExpStatics
   202 {
   203     RegExpStatics * const original;
   204     AutoRegExpStaticsBuffer buffer;
   206   public:
   207     explicit PreserveRegExpStatics(JSContext *cx, RegExpStatics *original)
   208      : original(original),
   209        buffer(cx)
   210     {}
   212     bool init(JSContext *cx) {
   213         return original->save(cx, &buffer.getStatics());
   214     }
   216     ~PreserveRegExpStatics() { original->restore(); }
   217 };
   219 inline bool
   220 RegExpStatics::createDependent(JSContext *cx, size_t start, size_t end, MutableHandleValue out)
   221 {
   222     /* Private function: caller must perform lazy evaluation. */
   223     JS_ASSERT(!pendingLazyEvaluation);
   225     JS_ASSERT(start <= end);
   226     JS_ASSERT(end <= matchesInput->length());
   227     JSString *str = js_NewDependentString(cx, matchesInput, start, end - start);
   228     if (!str)
   229         return false;
   230     out.setString(str);
   231     return true;
   232 }
   234 inline bool
   235 RegExpStatics::createPendingInput(JSContext *cx, MutableHandleValue out)
   236 {
   237     /* Lazy evaluation need not be resolved to return the input. */
   238     out.setString(pendingInput ? pendingInput.get() : cx->runtime()->emptyString);
   239     return true;
   240 }
   242 inline bool
   243 RegExpStatics::makeMatch(JSContext *cx, size_t checkValidIndex, size_t pairNum,
   244                          MutableHandleValue out)
   245 {
   246     /* Private function: caller must perform lazy evaluation. */
   247     JS_ASSERT(!pendingLazyEvaluation);
   249     bool checkWhich  = checkValidIndex % 2;
   250     size_t checkPair = checkValidIndex / 2;
   252     if (matches.empty() || checkPair >= matches.pairCount() ||
   253         (checkWhich ? matches[checkPair].limit : matches[checkPair].start) < 0)
   254     {
   255         out.setString(cx->runtime()->emptyString);
   256         return true;
   257     }
   258     const MatchPair &pair = matches[pairNum];
   259     return createDependent(cx, pair.start, pair.limit, out);
   260 }
   262 inline bool
   263 RegExpStatics::createLastMatch(JSContext *cx, MutableHandleValue out)
   264 {
   265     if (!executeLazy(cx))
   266         return false;
   267     return makeMatch(cx, 0, 0, out);
   268 }
   270 inline bool
   271 RegExpStatics::createLastParen(JSContext *cx, MutableHandleValue out)
   272 {
   273     if (!executeLazy(cx))
   274         return false;
   276     if (matches.empty() || matches.pairCount() == 1) {
   277         out.setString(cx->runtime()->emptyString);
   278         return true;
   279     }
   280     const MatchPair &pair = matches[matches.pairCount() - 1];
   281     if (pair.start == -1) {
   282         out.setString(cx->runtime()->emptyString);
   283         return true;
   284     }
   285     JS_ASSERT(pair.start >= 0 && pair.limit >= 0);
   286     JS_ASSERT(pair.limit >= pair.start);
   287     return createDependent(cx, pair.start, pair.limit, out);
   288 }
   290 inline bool
   291 RegExpStatics::createParen(JSContext *cx, size_t pairNum, MutableHandleValue out)
   292 {
   293     JS_ASSERT(pairNum >= 1);
   294     if (!executeLazy(cx))
   295         return false;
   297     if (matches.empty() || pairNum >= matches.pairCount()) {
   298         out.setString(cx->runtime()->emptyString);
   299         return true;
   300     }
   301     return makeMatch(cx, pairNum * 2, pairNum, out);
   302 }
   304 inline bool
   305 RegExpStatics::createLeftContext(JSContext *cx, MutableHandleValue out)
   306 {
   307     if (!executeLazy(cx))
   308         return false;
   310     if (matches.empty()) {
   311         out.setString(cx->runtime()->emptyString);
   312         return true;
   313     }
   314     if (matches[0].start < 0) {
   315         out.setUndefined();
   316         return true;
   317     }
   318     return createDependent(cx, 0, matches[0].start, out);
   319 }
   321 inline bool
   322 RegExpStatics::createRightContext(JSContext *cx, MutableHandleValue out)
   323 {
   324     if (!executeLazy(cx))
   325         return false;
   327     if (matches.empty()) {
   328         out.setString(cx->runtime()->emptyString);
   329         return true;
   330     }
   331     if (matches[0].limit < 0) {
   332         out.setUndefined();
   333         return true;
   334     }
   335     return createDependent(cx, matches[0].limit, matchesInput->length(), out);
   336 }
   338 inline void
   339 RegExpStatics::getParen(size_t pairNum, JSSubString *out) const
   340 {
   341     JS_ASSERT(!pendingLazyEvaluation);
   343     JS_ASSERT(pairNum >= 1 && pairNum < matches.pairCount());
   344     const MatchPair &pair = matches[pairNum];
   345     if (pair.isUndefined()) {
   346         *out = js_EmptySubString;
   347         return;
   348     }
   349     out->chars  = matchesInput->chars() + pair.start;
   350     out->length = pair.length();
   351 }
   353 inline void
   354 RegExpStatics::getLastMatch(JSSubString *out) const
   355 {
   356     JS_ASSERT(!pendingLazyEvaluation);
   358     if (matches.empty()) {
   359         *out = js_EmptySubString;
   360         return;
   361     }
   362     JS_ASSERT(matchesInput);
   363     out->chars = matchesInput->chars() + matches[0].start;
   364     JS_ASSERT(matches[0].limit >= matches[0].start);
   365     out->length = matches[0].length();
   366 }
   368 inline void
   369 RegExpStatics::getLastParen(JSSubString *out) const
   370 {
   371     JS_ASSERT(!pendingLazyEvaluation);
   373     /* Note: the first pair is the whole match. */
   374     if (matches.empty() || matches.pairCount() == 1) {
   375         *out = js_EmptySubString;
   376         return;
   377     }
   378     getParen(matches.parenCount(), out);
   379 }
   381 inline void
   382 RegExpStatics::getLeftContext(JSSubString *out) const
   383 {
   384     JS_ASSERT(!pendingLazyEvaluation);
   386     if (matches.empty()) {
   387         *out = js_EmptySubString;
   388         return;
   389     }
   390     out->chars = matchesInput->chars();
   391     out->length = matches[0].start;
   392 }
   394 inline void
   395 RegExpStatics::getRightContext(JSSubString *out) const
   396 {
   397     JS_ASSERT(!pendingLazyEvaluation);
   399     if (matches.empty()) {
   400         *out = js_EmptySubString;
   401         return;
   402     }
   403     out->chars = matchesInput->chars() + matches[0].limit;
   404     JS_ASSERT(matches[0].limit <= int(matchesInput->length()));
   405     out->length = matchesInput->length() - matches[0].limit;
   406 }
   408 inline void
   409 RegExpStatics::copyTo(RegExpStatics &dst)
   410 {
   411     /* Destination buffer has already been reserved by save(). */
   412     if (!pendingLazyEvaluation)
   413         dst.matches.initArrayFrom(matches);
   415     dst.matchesInput = matchesInput;
   416     dst.lazySource = lazySource;
   417     dst.lazyFlags = lazyFlags;
   418     dst.lazyIndex = lazyIndex;
   419     dst.pendingInput = pendingInput;
   420     dst.flags = flags;
   421     dst.pendingLazyEvaluation = pendingLazyEvaluation;
   423     JS_ASSERT_IF(pendingLazyEvaluation, lazySource);
   424     JS_ASSERT_IF(pendingLazyEvaluation, matchesInput);
   425 }
   427 inline void
   428 RegExpStatics::aboutToWrite()
   429 {
   430     if (bufferLink && !bufferLink->copied) {
   431         copyTo(*bufferLink);
   432         bufferLink->copied = true;
   433     }
   434 }
   436 inline void
   437 RegExpStatics::restore()
   438 {
   439     if (bufferLink->copied)
   440         bufferLink->copyTo(*this);
   441     bufferLink = bufferLink->bufferLink;
   442 }
   444 inline void
   445 RegExpStatics::updateLazily(JSContext *cx, JSLinearString *input,
   446                             RegExpShared *shared, size_t lastIndex)
   447 {
   448     JS_ASSERT(input && shared);
   449     aboutToWrite();
   451     BarrieredSetPair<JSString, JSLinearString>(cx->zone(),
   452                                                pendingInput, input,
   453                                                matchesInput, input);
   455     lazySource = shared->source;
   456     lazyFlags = shared->flags;
   457     lazyIndex = lastIndex;
   458     pendingLazyEvaluation = true;
   459 }
   461 inline bool
   462 RegExpStatics::updateFromMatchPairs(JSContext *cx, JSLinearString *input, MatchPairs &newPairs)
   463 {
   464     JS_ASSERT(input);
   465     aboutToWrite();
   467     /* Unset all lazy state. */
   468     pendingLazyEvaluation = false;
   469     this->lazySource = nullptr;
   470     this->lazyIndex = size_t(-1);
   472     BarrieredSetPair<JSString, JSLinearString>(cx->zone(),
   473                                                pendingInput, input,
   474                                                matchesInput, input);
   476     if (!matches.initArrayFrom(newPairs)) {
   477         js_ReportOutOfMemory(cx);
   478         return false;
   479     }
   481     return true;
   482 }
   484 inline void
   485 RegExpStatics::clear()
   486 {
   487     aboutToWrite();
   489     matches.forgetArray();
   490     matchesInput = nullptr;
   491     lazySource = nullptr;
   492     lazyFlags = RegExpFlag(0);
   493     lazyIndex = size_t(-1);
   494     pendingInput = nullptr;
   495     flags = RegExpFlag(0);
   496     pendingLazyEvaluation = false;
   497 }
   499 inline void
   500 RegExpStatics::setPendingInput(JSString *newInput)
   501 {
   502     aboutToWrite();
   503     pendingInput = newInput;
   504 }
   506 inline void
   507 RegExpStatics::checkInvariants()
   508 {
   509 #ifdef DEBUG
   510     if (pendingLazyEvaluation) {
   511         JS_ASSERT(lazySource);
   512         JS_ASSERT(matchesInput);
   513         JS_ASSERT(lazyIndex != size_t(-1));
   514         return;
   515     }
   517     if (matches.empty()) {
   518         JS_ASSERT(!matchesInput);
   519         return;
   520     }
   522     /* Pair count is non-zero, so there must be match pairs input. */
   523     JS_ASSERT(matchesInput);
   524     size_t mpiLen = matchesInput->length();
   526     /* Both members of the first pair must be non-negative. */
   527     JS_ASSERT(!matches[0].isUndefined());
   528     JS_ASSERT(matches[0].limit >= 0);
   530     /* Present pairs must be valid. */
   531     for (size_t i = 0; i < matches.pairCount(); i++) {
   532         if (matches[i].isUndefined())
   533             continue;
   534         const MatchPair &pair = matches[i];
   535         JS_ASSERT(mpiLen >= size_t(pair.limit) && pair.limit >= pair.start && pair.start >= 0);
   536     }
   537 #endif /* DEBUG */
   538 }
   540 } /* namespace js */
   542 #endif /* vm_RegExpStatics_h */

mercurial