Bug 1865597 - Add error checking when initializing parallel marking and disable on...
[gecko.git] / js / src / vm / RegExpObject.h
blob6a83d424e0c553e4a01fda3463346fc41595959b
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/. */
7 /* JavaScript RegExp objects. */
9 #ifndef vm_RegExpObject_h
10 #define vm_RegExpObject_h
12 #include "builtin/SelfHostingDefines.h"
13 #include "js/RegExpFlags.h"
14 #include "proxy/Proxy.h"
15 #include "vm/JSAtomState.h"
16 #include "vm/JSContext.h"
17 #include "vm/RegExpShared.h"
18 #include "vm/Shape.h"
21 * JavaScript Regular Expressions
23 * There are several engine concepts associated with a single logical regexp:
25 * RegExpObject:
26 * The JS-visible object whose .[[Class]] equals "RegExp".
27 * RegExpShared:
28 * The compiled representation of the regexp (lazily created, cleared
29 * during some forms of GC).
30 * RegExpZone:
31 * Owns all RegExpShared instances in a zone.
33 namespace js {
35 extern RegExpObject* RegExpAlloc(JSContext* cx, NewObjectKind newKind,
36 HandleObject proto = nullptr);
38 extern JSObject* CloneRegExpObject(JSContext* cx, Handle<RegExpObject*> regex);
40 class RegExpObject : public NativeObject {
41 static const unsigned LAST_INDEX_SLOT = 0;
42 static const unsigned SOURCE_SLOT = 1;
43 static const unsigned FLAGS_SLOT = 2;
45 static_assert(RegExpObject::FLAGS_SLOT == REGEXP_FLAGS_SLOT,
46 "FLAGS_SLOT values should be in sync with self-hosted JS");
48 static RegExpObject* create(JSContext* cx, Handle<JSAtom*> source,
49 NewObjectKind newKind);
51 public:
52 static const unsigned SHARED_SLOT = 3;
53 static const unsigned RESERVED_SLOTS = 4;
55 // This must match RESERVED_SLOTS. See assertions in CloneRegExpObject.
56 static constexpr gc::AllocKind AllocKind = gc::AllocKind::OBJECT4_BACKGROUND;
58 static const JSClass class_;
59 static const JSClass protoClass_;
61 // The maximum number of pairs a MatchResult can have, without having to
62 // allocate a bigger MatchResult.
63 static const size_t MaxPairCount = 14;
65 template <typename CharT>
66 static RegExpObject* create(JSContext* cx, const CharT* chars, size_t length,
67 JS::RegExpFlags flags, NewObjectKind newKind);
69 // This variant assumes that the characters have already previously been
70 // syntax checked.
71 static RegExpObject* createSyntaxChecked(JSContext* cx,
72 Handle<JSAtom*> source,
73 JS::RegExpFlags flags,
74 NewObjectKind newKind);
76 static RegExpObject* create(JSContext* cx, Handle<JSAtom*> source,
77 JS::RegExpFlags flags, NewObjectKind newKind);
80 * Compute the initial shape to associate with fresh RegExp objects,
81 * encoding their initial properties. Return the shape after
82 * changing |obj|'s last property to it.
84 static SharedShape* assignInitialShape(JSContext* cx,
85 Handle<RegExpObject*> obj);
87 /* Accessors. */
89 static constexpr size_t lastIndexSlot() { return LAST_INDEX_SLOT; }
91 static constexpr size_t offsetOfLastIndex() {
92 return getFixedSlotOffset(lastIndexSlot());
95 static bool isInitialShape(RegExpObject* rx) {
96 // RegExpObject has a non-configurable lastIndex property, so there must be
97 // at least one property. Even though lastIndex is non-configurable, it can
98 // be made non-writable, so we have to check if it's still writable.
99 MOZ_ASSERT(!rx->empty());
100 PropertyInfoWithKey prop = rx->getLastProperty();
101 return prop.isDataProperty() && prop.slot() == LAST_INDEX_SLOT &&
102 prop.writable();
105 const Value& getLastIndex() const { return getReservedSlot(LAST_INDEX_SLOT); }
107 void setLastIndex(JSContext* cx, int32_t lastIndex) {
108 MOZ_ASSERT(lastIndex >= 0);
109 MOZ_ASSERT(lookupPure(cx->names().lastIndex)->writable(),
110 "can't infallibly set a non-writable lastIndex on a "
111 "RegExp that's been exposed to script");
112 setReservedSlot(LAST_INDEX_SLOT, Int32Value(lastIndex));
114 void zeroLastIndex(JSContext* cx) { setLastIndex(cx, 0); }
116 static JSLinearString* toString(JSContext* cx, Handle<RegExpObject*> obj);
118 JSAtom* getSource() const {
119 return &getReservedSlot(SOURCE_SLOT).toString()->asAtom();
122 void setSource(JSAtom* source) {
123 setReservedSlot(SOURCE_SLOT, StringValue(source));
126 /* Flags. */
128 static constexpr size_t flagsSlot() { return FLAGS_SLOT; }
130 static constexpr size_t offsetOfFlags() {
131 return getFixedSlotOffset(flagsSlot());
134 static constexpr size_t offsetOfShared() {
135 return getFixedSlotOffset(SHARED_SLOT);
138 JS::RegExpFlags getFlags() const {
139 return JS::RegExpFlags(getFixedSlot(FLAGS_SLOT).toInt32());
141 void setFlags(JS::RegExpFlags flags) {
142 setFixedSlot(FLAGS_SLOT, Int32Value(flags.value()));
145 bool hasIndices() const { return getFlags().hasIndices(); }
146 bool global() const { return getFlags().global(); }
147 bool ignoreCase() const { return getFlags().ignoreCase(); }
148 bool multiline() const { return getFlags().multiline(); }
149 bool dotAll() const { return getFlags().dotAll(); }
150 bool unicode() const { return getFlags().unicode(); }
151 bool unicodeSets() const { return getFlags().unicodeSets(); }
152 bool sticky() const { return getFlags().sticky(); }
154 bool isGlobalOrSticky() const {
155 JS::RegExpFlags flags = getFlags();
156 return flags.global() || flags.sticky();
159 static bool isOriginalFlagGetter(JSNative native, JS::RegExpFlags* mask);
161 static RegExpShared* getShared(JSContext* cx, Handle<RegExpObject*> regexp);
163 bool hasShared() const { return !getFixedSlot(SHARED_SLOT).isUndefined(); }
165 RegExpShared* getShared() const {
166 return static_cast<RegExpShared*>(getFixedSlot(SHARED_SLOT).toGCThing());
169 void setShared(RegExpShared* shared) {
170 MOZ_ASSERT(shared);
171 setFixedSlot(SHARED_SLOT, PrivateGCThingValue(shared));
174 void clearShared() { setFixedSlot(SHARED_SLOT, UndefinedValue()); }
176 void initIgnoringLastIndex(JSAtom* source, JS::RegExpFlags flags);
178 // NOTE: This method is *only* safe to call on RegExps that haven't been
179 // exposed to script, because it requires that the "lastIndex"
180 // property be writable.
181 void initAndZeroLastIndex(JSAtom* source, JS::RegExpFlags flags,
182 JSContext* cx);
184 #ifdef DEBUG
185 [[nodiscard]] static bool dumpBytecode(JSContext* cx,
186 Handle<RegExpObject*> regexp,
187 Handle<JSLinearString*> input);
188 #endif
190 private:
192 * Precondition: the syntax for |source| has already been validated.
193 * Side effect: sets the private field.
195 static RegExpShared* createShared(JSContext* cx,
196 Handle<RegExpObject*> regexp);
198 /* Call setShared in preference to setPrivate. */
199 void setPrivate(void* priv) = delete;
203 * Parse regexp flags. Report an error and return false if an invalid
204 * sequence of flags is encountered (repeat/invalid flag).
206 * N.B. flagStr must be rooted.
208 bool ParseRegExpFlags(JSContext* cx, JSString* flagStr,
209 JS::RegExpFlags* flagsOut);
211 // Assuming GetBuiltinClass(obj) is ESClass::RegExp, return a RegExpShared for
212 // obj.
213 inline RegExpShared* RegExpToShared(JSContext* cx, HandleObject obj) {
214 if (obj->is<RegExpObject>()) {
215 return RegExpObject::getShared(cx, obj.as<RegExpObject>());
218 return Proxy::regexp_toShared(cx, obj);
221 /* Escape all slashes and newlines in the given string. */
222 extern JSLinearString* EscapeRegExpPattern(JSContext* cx, Handle<JSAtom*> src);
224 template <typename CharT>
225 extern bool HasRegExpMetaChars(const CharT* chars, size_t length);
227 extern bool StringHasRegExpMetaChars(JSLinearString* str);
229 } /* namespace js */
231 #endif /* vm_RegExpObject_h */