DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Implementation

Mercurial (b6d82b1a6b02)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* JavaScript RegExp objects. */

#ifndef vm_RegExpObject_h
#define vm_RegExpObject_h

#include "mozilla/Attributes.h"
#include "mozilla/MemoryReporting.h"

#include "builtin/SelfHostingDefines.h"
#include "gc/Marking.h"
#include "js/GCHashTable.h"
#include "js/RegExpFlags.h"
#include "proxy/Proxy.h"
#include "vm/ArrayObject.h"
#include "vm/JSContext.h"
#include "vm/RegExpShared.h"
#include "vm/Shape.h"

/*
 * JavaScript Regular Expressions
 *
 * There are several engine concepts associated with a single logical regexp:
 *
 *   RegExpObject:
 *     The JS-visible object whose .[[Class]] equals "RegExp".
 *   RegExpShared:
 *     The compiled representation of the regexp (lazily created, cleared
 *     during some forms of GC).
 *   RegExpZone:
 *     Owns all RegExpShared instances in a zone.
 */
namespace js {

struct MatchPair;
class MatchPairs;
class RegExpStatics;

namespace frontend {
class TokenStreamAnyChars;
}

extern RegExpObject* RegExpAlloc(JSContext* cx, NewObjectKind newKind,
                                 HandleObject proto = nullptr);

extern JSObject* CloneRegExpObject(JSContext* cx, Handle<RegExpObject*> regex);

class RegExpObject : public NativeObject {
  static const unsigned LAST_INDEX_SLOT = 0;
  static const unsigned SOURCE_SLOT = 1;
  static const unsigned FLAGS_SLOT = 2;

  static_assert(RegExpObject::FLAGS_SLOT == REGEXP_FLAGS_SLOT,
                "FLAGS_SLOT values should be in sync with self-hosted JS");

 public:
  static const unsigned RESERVED_SLOTS = 3;
  static const unsigned PRIVATE_SLOT = 3;

  static const JSClass class_;
  static const JSClass protoClass_;

  // The maximum number of pairs a MatchResult can have, without having to
  // allocate a bigger MatchResult.
  static const size_t MaxPairCount = 14;

  template <typename CharT>
  static RegExpObject* create(JSContext* cx, const CharT* chars, size_t length,
                              JS::RegExpFlags flags, NewObjectKind newKind);

  template <typename CharT>
  static RegExpObject* create(JSContext* cx, const CharT* chars, size_t length,
                              JS::RegExpFlags flags,
                              frontend::TokenStreamAnyChars& ts,
                              NewObjectKind kind);

  static RegExpObject* create(JSContext* cx, HandleAtom source,
                              JS::RegExpFlags flags, NewObjectKind newKind);

  static RegExpObject* create(JSContext* cx, HandleAtom source,
                              JS::RegExpFlags flags,
                              frontend::TokenStreamAnyChars& ts,
                              NewObjectKind newKind);

  /*
   * Compute the initial shape to associate with fresh RegExp objects,
   * encoding their initial properties. Return the shape after
   * changing |obj|'s last property to it.
   */
  static Shape* assignInitialShape(JSContext* cx, Handle<RegExpObject*> obj);

  /* Accessors. */

  static unsigned lastIndexSlot() { return LAST_INDEX_SLOT; }

  static bool isInitialShape(RegExpObject* rx) {
    Shape* shape = rx->lastProperty();
    if (shape->isEmptyShape() || !shape->isDataProperty()) {
      return false;
    }
    if (shape->maybeSlot() != LAST_INDEX_SLOT) {
      return false;
    }
    return true;
  }

  const Value& getLastIndex() const { return getSlot(LAST_INDEX_SLOT); }

  void setLastIndex(double d) { setSlot(LAST_INDEX_SLOT, NumberValue(d)); }

  void zeroLastIndex(JSContext* cx) {
    MOZ_ASSERT(lookupPure(cx->names().lastIndex)->writable(),
               "can't infallibly zero a non-writable lastIndex on a "
               "RegExp that's been exposed to script");
    setSlot(LAST_INDEX_SLOT, Int32Value(0));
  }

  JSFlatString* toString(JSContext* cx) const;

  JSAtom* getSource() const {
    return &getSlot(SOURCE_SLOT).toString()->asAtom();
  }

  void setSource(JSAtom* source) { setSlot(SOURCE_SLOT, StringValue(source)); }

  /* Flags. */

  static unsigned flagsSlot() { return FLAGS_SLOT; }

  JS::RegExpFlags getFlags() const {
    return JS::RegExpFlags(getFixedSlot(FLAGS_SLOT).toInt32());
  }
  void setFlags(JS::RegExpFlags flags) {
    setFixedSlot(FLAGS_SLOT, Int32Value(flags.value()));
  }

  bool global() const { return getFlags().global(); }
  bool ignoreCase() const { return getFlags().ignoreCase(); }
  bool multiline() const { return getFlags().multiline(); }
  bool unicode() const { return getFlags().unicode(); }
  bool sticky() const { return getFlags().sticky(); }

  static bool isOriginalFlagGetter(JSNative native, JS::RegExpFlags* mask);

  static RegExpShared* getShared(JSContext* cx, Handle<RegExpObject*> regexp);

  bool hasShared() { return !!sharedRef(); }

  void setShared(RegExpShared& shared) {
    MOZ_ASSERT(!hasShared());
    sharedRef().init(&shared);
  }

  HeapPtrRegExpShared& sharedRef() {
    auto& ref = NativeObject::privateRef(PRIVATE_SLOT);
    return reinterpret_cast<HeapPtrRegExpShared&>(ref);
  }

  static void trace(JSTracer* trc, JSObject* obj);
  void trace(JSTracer* trc);

  void initIgnoringLastIndex(JSAtom* source, JS::RegExpFlags flags);

  // NOTE: This method is *only* safe to call on RegExps that haven't been
  //       exposed to script, because it requires that the "lastIndex"
  //       property be writable.
  void initAndZeroLastIndex(JSAtom* source, JS::RegExpFlags flags,
                            JSContext* cx);

#ifdef DEBUG
  static MOZ_MUST_USE bool dumpBytecode(JSContext* cx,
                                        Handle<RegExpObject*> regexp,
                                        bool match_only,
                                        HandleLinearString input);
#endif

 private:
  /*
   * Precondition: the syntax for |source| has already been validated.
   * Side effect: sets the private field.
   */
  static RegExpShared* createShared(JSContext* cx,
                                    Handle<RegExpObject*> regexp);

  /* Call setShared in preference to setPrivate. */
  void setPrivate(void* priv) = delete;
};

/*
 * Parse regexp flags. Report an error and return false if an invalid
 * sequence of flags is encountered (repeat/invalid flag).
 *
 * N.B. flagStr must be rooted.
 */
bool ParseRegExpFlags(JSContext* cx, JSString* flagStr,
                      JS::RegExpFlags* flagsOut);

// Assuming GetBuiltinClass(obj) is ESClass::RegExp, return a RegExpShared for
// obj.
inline RegExpShared* RegExpToShared(JSContext* cx, HandleObject obj) {
  if (obj->is<RegExpObject>()) {
    return RegExpObject::getShared(cx, obj.as<RegExpObject>());
  }

  return Proxy::regexp_toShared(cx, obj);
}

template <XDRMode mode>
XDRResult XDRScriptRegExpObject(XDRState<mode>* xdr,
                                MutableHandle<RegExpObject*> objp);

extern JSObject* CloneScriptRegExpObject(JSContext* cx, RegExpObject& re);

/* Escape all slashes and newlines in the given string. */
extern JSAtom* EscapeRegExpPattern(JSContext* cx, HandleAtom src);

template <typename CharT>
extern bool HasRegExpMetaChars(const CharT* chars, size_t length);

extern bool StringHasRegExpMetaChars(JSLinearString* str);

} /* namespace js */

#endif /* vm_RegExpObject_h */