tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

RegExpStatics.h (9794B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      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 "js/RegExpFlags.h"
     11 #include "vm/JSContext.h"
     12 #include "vm/MatchPairs.h"
     13 #include "vm/Runtime.h"
     14 
     15 namespace js {
     16 
     17 class RegExpStatics {
     18  /* The latest RegExp output, set after execution. */
     19  VectorMatchPairs matches;
     20  HeapPtr<JSLinearString*> matchesInput;
     21 
     22  /*
     23   * The previous RegExp input, used to resolve lazy state.
     24   * A raw RegExpShared cannot be stored because it may be in
     25   * a different compartment via evalcx().
     26   */
     27  HeapPtr<JSAtom*> lazySource;
     28  JS::RegExpFlags lazyFlags;
     29  size_t lazyIndex;
     30 
     31  /* The latest RegExp input, set before execution. */
     32  HeapPtr<JSString*> pendingInput;
     33 
     34  /*
     35   * If non-zero, |matchesInput| and the |lazy*| fields may be used
     36   * to replay the last executed RegExp, and |matches| is invalid.
     37   */
     38  int32_t pendingLazyEvaluation;
     39 
     40 public:
     41  RegExpStatics() { clear(); }
     42  static UniquePtr<RegExpStatics> create(JSContext* cx);
     43 
     44 private:
     45  bool executeLazy(JSContext* cx);
     46 
     47  inline void checkInvariants();
     48 
     49  // Legacy RegExp static properties support.
     50 private:
     51  bool invalidated_ = false;
     52 
     53 public:
     54  bool isInvalidated() const {
     55    if (!JS::Prefs::experimental_legacy_regexp()) {
     56      return false;
     57    }
     58    return invalidated_;
     59  }
     60 
     61  inline void invalidate() {
     62    if (JS::Prefs::experimental_legacy_regexp()) {
     63      invalidated_ = true;
     64    }
     65  }
     66 
     67  inline void clearInvalidation() {
     68    if (JS::Prefs::experimental_legacy_regexp()) {
     69      invalidated_ = false;
     70    }
     71  }
     72 
     73  /*
     74   * Check whether a match for pair |pairNum| occurred.  If so, allocate and
     75   * store the match string in |*out|; otherwise place |undefined| in |*out|.
     76   */
     77  bool makeMatch(JSContext* cx, size_t pairNum, MutableHandleValue out);
     78  bool createDependent(JSContext* cx, size_t start, size_t end,
     79                       MutableHandleValue out);
     80 
     81 public:
     82  /* Mutators. */
     83  inline bool updateFromMatchPairs(JSContext* cx, JSLinearString* input,
     84                                   VectorMatchPairs& newPairs);
     85 
     86  inline void clear();
     87 
     88  /* Corresponds to JSAPI functionality to set the pending RegExp input. */
     89  void reset(JSString* newInput) {
     90    clear();
     91    pendingInput = newInput;
     92    checkInvariants();
     93  }
     94 
     95  inline void setPendingInput(JSString* newInput);
     96 
     97 public:
     98  void trace(JSTracer* trc) {
     99    /*
    100     * Changes to this function must also be reflected in
    101     * RegExpStatics::AutoRooter::trace().
    102     */
    103    TraceNullableEdge(trc, &matchesInput, "res->matchesInput");
    104    TraceNullableEdge(trc, &lazySource, "res->lazySource");
    105    TraceNullableEdge(trc, &pendingInput, "res->pendingInput");
    106  }
    107 
    108  size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
    109    return mallocSizeOf(this) + matches.sizeOfExcludingThis(mallocSizeOf);
    110  }
    111 
    112  /* Value creators. */
    113 
    114  bool createPendingInput(JSContext* cx, MutableHandleValue out);
    115  bool createLastMatch(JSContext* cx, MutableHandleValue out);
    116  bool createLastParen(JSContext* cx, MutableHandleValue out);
    117  bool createParen(JSContext* cx, size_t pairNum, MutableHandleValue out);
    118  bool createLeftContext(JSContext* cx, MutableHandleValue out);
    119  bool createRightContext(JSContext* cx, MutableHandleValue out);
    120 
    121  static size_t offsetOfPendingInput() {
    122    return offsetof(RegExpStatics, pendingInput);
    123  }
    124 
    125  static size_t offsetOfMatchesInput() {
    126    return offsetof(RegExpStatics, matchesInput);
    127  }
    128 
    129  static size_t offsetOfLazySource() {
    130    return offsetof(RegExpStatics, lazySource);
    131  }
    132 
    133  static size_t offsetOfLazyFlags() {
    134    return offsetof(RegExpStatics, lazyFlags);
    135  }
    136 
    137  static size_t offsetOfLazyIndex() {
    138    return offsetof(RegExpStatics, lazyIndex);
    139  }
    140 
    141  static size_t offsetOfPendingLazyEvaluation() {
    142    return offsetof(RegExpStatics, pendingLazyEvaluation);
    143  }
    144 
    145  static size_t offsetOfInvalidated() {
    146    return offsetof(RegExpStatics, invalidated_);
    147  }
    148 };
    149 
    150 inline bool RegExpStatics::createDependent(JSContext* cx, size_t start,
    151                                           size_t end, MutableHandleValue out) {
    152  /* Private function: caller must perform lazy evaluation. */
    153  MOZ_ASSERT(!pendingLazyEvaluation);
    154 
    155  MOZ_ASSERT(start <= end);
    156  MOZ_ASSERT(end <= matchesInput->length());
    157  JSString* str = NewDependentString(cx, matchesInput, start, end - start);
    158  if (!str) {
    159    return false;
    160  }
    161  out.setString(str);
    162  return true;
    163 }
    164 
    165 inline bool RegExpStatics::createPendingInput(JSContext* cx,
    166                                              MutableHandleValue out) {
    167  /* Lazy evaluation need not be resolved to return the input. */
    168  out.setString(pendingInput ? pendingInput.get()
    169                             : cx->runtime()->emptyString.ref());
    170  return true;
    171 }
    172 
    173 inline bool RegExpStatics::makeMatch(JSContext* cx, size_t pairNum,
    174                                     MutableHandleValue out) {
    175  /* Private function: caller must perform lazy evaluation. */
    176  MOZ_ASSERT(!pendingLazyEvaluation);
    177 
    178  if (matches.empty() || pairNum >= matches.pairCount() ||
    179      matches[pairNum].isUndefined()) {
    180    out.setUndefined();
    181    return true;
    182  }
    183 
    184  const MatchPair& pair = matches[pairNum];
    185  return createDependent(cx, pair.start, pair.limit, out);
    186 }
    187 
    188 inline bool RegExpStatics::createLastMatch(JSContext* cx,
    189                                           MutableHandleValue out) {
    190  if (!executeLazy(cx)) {
    191    return false;
    192  }
    193 
    194  if (isInvalidated()) {
    195    out.setUndefined();
    196    return true;
    197  }
    198 
    199  return makeMatch(cx, 0, out);
    200 }
    201 
    202 inline bool RegExpStatics::createLastParen(JSContext* cx,
    203                                           MutableHandleValue out) {
    204  if (!executeLazy(cx)) {
    205    return false;
    206  }
    207 
    208  if (isInvalidated()) {
    209    out.setUndefined();
    210    return true;
    211  }
    212 
    213  if (matches.empty() || matches.pairCount() == 1) {
    214    out.setString(cx->runtime()->emptyString);
    215    return true;
    216  }
    217  const MatchPair& pair = matches[matches.pairCount() - 1];
    218  if (pair.start == -1) {
    219    out.setString(cx->runtime()->emptyString);
    220    return true;
    221  }
    222  MOZ_ASSERT(pair.start >= 0 && pair.limit >= 0);
    223  MOZ_ASSERT(pair.limit >= pair.start);
    224  return createDependent(cx, pair.start, pair.limit, out);
    225 }
    226 
    227 inline bool RegExpStatics::createParen(JSContext* cx, size_t pairNum,
    228                                       MutableHandleValue out) {
    229  MOZ_ASSERT(pairNum >= 1);
    230  if (!executeLazy(cx)) {
    231    return false;
    232  }
    233 
    234  if (isInvalidated()) {
    235    out.setUndefined();
    236    return true;
    237  }
    238 
    239  if (matches.empty() || pairNum >= matches.pairCount()) {
    240    out.setString(cx->runtime()->emptyString);
    241    return true;
    242  }
    243  return makeMatch(cx, pairNum, out);
    244 }
    245 
    246 inline bool RegExpStatics::createLeftContext(JSContext* cx,
    247                                             MutableHandleValue out) {
    248  if (!executeLazy(cx)) {
    249    return false;
    250  }
    251 
    252  if (isInvalidated()) {
    253    out.setUndefined();
    254    return true;
    255  }
    256 
    257  if (matches.empty()) {
    258    out.setString(cx->runtime()->emptyString);
    259    return true;
    260  }
    261  if (matches[0].start < 0) {
    262    out.setUndefined();
    263    return true;
    264  }
    265  return createDependent(cx, 0, matches[0].start, out);
    266 }
    267 
    268 inline bool RegExpStatics::createRightContext(JSContext* cx,
    269                                              MutableHandleValue out) {
    270  if (!executeLazy(cx)) {
    271    return false;
    272  }
    273 
    274  if (isInvalidated()) {
    275    out.setUndefined();
    276    return true;
    277  }
    278 
    279  if (matches.empty()) {
    280    out.setString(cx->runtime()->emptyString);
    281    return true;
    282  }
    283  if (matches[0].limit < 0) {
    284    out.setUndefined();
    285    return true;
    286  }
    287  return createDependent(cx, matches[0].limit, matchesInput->length(), out);
    288 }
    289 
    290 inline bool RegExpStatics::updateFromMatchPairs(JSContext* cx,
    291                                                JSLinearString* input,
    292                                                VectorMatchPairs& newPairs) {
    293  MOZ_ASSERT(input);
    294 
    295  /* Unset all lazy state. */
    296  pendingLazyEvaluation = false;
    297  this->lazySource = nullptr;
    298  this->lazyIndex = size_t(-1);
    299 
    300  BarrieredSetPair<JSString, JSLinearString>(cx->zone(), pendingInput, input,
    301                                             matchesInput, input);
    302 
    303  if (!matches.initArrayFrom(newPairs)) {
    304    ReportOutOfMemory(cx);
    305    return false;
    306  }
    307  clearInvalidation();
    308  return true;
    309 }
    310 
    311 inline void RegExpStatics::clear() {
    312  matches.forgetArray();
    313  matchesInput = nullptr;
    314  lazySource = nullptr;
    315  lazyFlags = JS::RegExpFlag::NoFlags;
    316  lazyIndex = size_t(-1);
    317  pendingInput = nullptr;
    318  pendingLazyEvaluation = false;
    319 }
    320 
    321 inline void RegExpStatics::setPendingInput(JSString* newInput) {
    322  pendingInput = newInput;
    323 }
    324 
    325 inline void RegExpStatics::checkInvariants() {
    326 #ifdef DEBUG
    327  if (pendingLazyEvaluation) {
    328    MOZ_ASSERT(lazySource);
    329    MOZ_ASSERT(matchesInput);
    330    MOZ_ASSERT(lazyIndex != size_t(-1));
    331    return;
    332  }
    333 
    334  if (matches.empty()) {
    335    MOZ_ASSERT(!matchesInput);
    336    return;
    337  }
    338 
    339  /* Pair count is non-zero, so there must be match pairs input. */
    340  MOZ_ASSERT(matchesInput);
    341  size_t mpiLen = matchesInput->length();
    342 
    343  /* Both members of the first pair must be non-negative. */
    344  MOZ_ASSERT(!matches[0].isUndefined());
    345  MOZ_ASSERT(matches[0].limit >= 0);
    346 
    347  /* Present pairs must be valid. */
    348  for (size_t i = 0; i < matches.pairCount(); i++) {
    349    if (matches[i].isUndefined()) {
    350      continue;
    351    }
    352    const MatchPair& pair = matches[i];
    353    MOZ_ASSERT(mpiLen >= size_t(pair.limit) && pair.limit >= pair.start &&
    354               pair.start >= 0);
    355  }
    356 #endif /* DEBUG */
    357 }
    358 
    359 } /* namespace js */
    360 
    361 #endif /* vm_RegExpStatics_h */