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"
21 * JavaScript Regular Expressions
23 * There are several engine concepts associated with a single logical regexp:
26 * The JS-visible object whose .[[Class]] equals "RegExp".
28 * The compiled representation of the regexp (lazily created, cleared
29 * during some forms of GC).
31 * Owns all RegExpShared instances in a zone.
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
);
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
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
);
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
&&
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
));
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
) {
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
,
185 [[nodiscard
]] static bool dumpBytecode(JSContext
* cx
,
186 Handle
<RegExpObject
*> regexp
,
187 Handle
<JSLinearString
*> input
);
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
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
);
231 #endif /* vm_RegExpObject_h */