Line data Source code
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/. */
6 :
7 : /* JavaScript RegExp objects. */
8 :
9 : #ifndef vm_RegExpObject_h
10 : #define vm_RegExpObject_h
11 :
12 : #include "mozilla/Attributes.h"
13 : #include "mozilla/MemoryReporting.h"
14 :
15 : #include "jscntxt.h"
16 :
17 : #include "builtin/SelfHostingDefines.h"
18 : #include "gc/Marking.h"
19 : #include "js/GCHashTable.h"
20 : #include "proxy/Proxy.h"
21 : #include "vm/ArrayObject.h"
22 : #include "vm/RegExpShared.h"
23 : #include "vm/Shape.h"
24 :
25 : namespace JS { struct Zone; }
26 :
27 : /*
28 : * JavaScript Regular Expressions
29 : *
30 : * There are several engine concepts associated with a single logical regexp:
31 : *
32 : * RegExpObject:
33 : * The JS-visible object whose .[[Class]] equals "RegExp".
34 : * RegExpShared:
35 : * The compiled representation of the regexp (lazily created, cleared
36 : * during some forms of GC).
37 : * RegExpZone:
38 : * Owns all RegExpShared instances in a zone.
39 : */
40 : namespace js {
41 :
42 : struct MatchPair;
43 : class MatchPairs;
44 : class RegExpStatics;
45 :
46 : namespace frontend { class TokenStream; }
47 :
48 : extern RegExpObject*
49 : RegExpAlloc(JSContext* cx, NewObjectKind newKind, HandleObject proto = nullptr);
50 :
51 : extern JSObject*
52 : CloneRegExpObject(JSContext* cx, Handle<RegExpObject*> regex);
53 :
54 : class RegExpObject : public NativeObject
55 : {
56 : static const unsigned LAST_INDEX_SLOT = 0;
57 : static const unsigned SOURCE_SLOT = 1;
58 : static const unsigned FLAGS_SLOT = 2;
59 :
60 : static_assert(RegExpObject::FLAGS_SLOT == REGEXP_FLAGS_SLOT,
61 : "FLAGS_SLOT values should be in sync with self-hosted JS");
62 :
63 : public:
64 : static const unsigned RESERVED_SLOTS = 3;
65 : static const unsigned PRIVATE_SLOT = 3;
66 :
67 : static const Class class_;
68 : static const Class protoClass_;
69 :
70 : // The maximum number of pairs a MatchResult can have, without having to
71 : // allocate a bigger MatchResult.
72 : static const size_t MaxPairCount = 14;
73 :
74 : static RegExpObject*
75 : create(JSContext* cx, const char16_t* chars, size_t length, RegExpFlag flags,
76 : const ReadOnlyCompileOptions* options, frontend::TokenStream* ts, LifoAlloc& alloc,
77 : NewObjectKind newKind);
78 :
79 : static RegExpObject*
80 : create(JSContext* cx, HandleAtom atom, RegExpFlag flags,
81 : const ReadOnlyCompileOptions* options, frontend::TokenStream* ts, LifoAlloc& alloc,
82 : NewObjectKind newKind);
83 :
84 : /*
85 : * Compute the initial shape to associate with fresh RegExp objects,
86 : * encoding their initial properties. Return the shape after
87 : * changing |obj|'s last property to it.
88 : */
89 : static Shape*
90 : assignInitialShape(JSContext* cx, Handle<RegExpObject*> obj);
91 :
92 : /* Accessors. */
93 :
94 686 : static unsigned lastIndexSlot() { return LAST_INDEX_SLOT; }
95 :
96 7 : static bool isInitialShape(RegExpObject* rx) {
97 7 : Shape* shape = rx->lastProperty();
98 7 : if (!shape->hasSlot())
99 0 : return false;
100 7 : if (shape->maybeSlot() != LAST_INDEX_SLOT)
101 0 : return false;
102 7 : return true;
103 : }
104 :
105 : const Value& getLastIndex() const { return getSlot(LAST_INDEX_SLOT); }
106 :
107 : void setLastIndex(double d) {
108 : setSlot(LAST_INDEX_SLOT, NumberValue(d));
109 : }
110 :
111 899 : void zeroLastIndex(JSContext* cx) {
112 899 : MOZ_ASSERT(lookupPure(cx->names().lastIndex)->writable(),
113 : "can't infallibly zero a non-writable lastIndex on a "
114 : "RegExp that's been exposed to script");
115 899 : setSlot(LAST_INDEX_SLOT, Int32Value(0));
116 899 : }
117 :
118 : JSFlatString* toString(JSContext* cx) const;
119 :
120 416 : JSAtom* getSource() const { return &getSlot(SOURCE_SLOT).toString()->asAtom(); }
121 :
122 899 : void setSource(JSAtom* source) {
123 899 : setSlot(SOURCE_SLOT, StringValue(source));
124 899 : }
125 :
126 : /* Flags. */
127 :
128 0 : static unsigned flagsSlot() { return FLAGS_SLOT; }
129 :
130 674 : RegExpFlag getFlags() const {
131 674 : return RegExpFlag(getFixedSlot(FLAGS_SLOT).toInt32());
132 : }
133 899 : void setFlags(RegExpFlag flags) {
134 899 : setSlot(FLAGS_SLOT, Int32Value(flags));
135 899 : }
136 :
137 0 : bool ignoreCase() const { return getFlags() & IgnoreCaseFlag; }
138 0 : bool global() const { return getFlags() & GlobalFlag; }
139 0 : bool multiline() const { return getFlags() & MultilineFlag; }
140 0 : bool sticky() const { return getFlags() & StickyFlag; }
141 258 : bool unicode() const { return getFlags() & UnicodeFlag; }
142 :
143 : static bool isOriginalFlagGetter(JSNative native, RegExpFlag* mask);
144 :
145 : static RegExpShared* getShared(JSContext* cx, Handle<RegExpObject*> regexp);
146 :
147 850 : bool hasShared() {
148 850 : return !!sharedRef();
149 : }
150 :
151 296 : void setShared(RegExpShared& shared) {
152 296 : MOZ_ASSERT(!hasShared());
153 296 : sharedRef().init(&shared);
154 296 : }
155 :
156 2496 : PreBarriered<RegExpShared*>& sharedRef() {
157 2496 : auto& ref = NativeObject::privateRef(PRIVATE_SLOT);
158 2496 : return reinterpret_cast<PreBarriered<RegExpShared*>&>(ref);
159 : }
160 :
161 : static void trace(JSTracer* trc, JSObject* obj);
162 : void trace(JSTracer* trc);
163 :
164 : void initIgnoringLastIndex(JSAtom* source, RegExpFlag flags);
165 :
166 : // NOTE: This method is *only* safe to call on RegExps that haven't been
167 : // exposed to script, because it requires that the "lastIndex"
168 : // property be writable.
169 : void initAndZeroLastIndex(JSAtom* source, RegExpFlag flags, JSContext* cx);
170 :
171 : #ifdef DEBUG
172 : static MOZ_MUST_USE bool dumpBytecode(JSContext* cx, Handle<RegExpObject*> regexp,
173 : bool match_only, HandleLinearString input);
174 : #endif
175 :
176 : private:
177 : /*
178 : * Precondition: the syntax for |source| has already been validated.
179 : * Side effect: sets the private field.
180 : */
181 : static RegExpShared* createShared(JSContext* cx, Handle<RegExpObject*> regexp);
182 :
183 : /* Call setShared in preference to setPrivate. */
184 : void setPrivate(void* priv) = delete;
185 : };
186 :
187 : /*
188 : * Parse regexp flags. Report an error and return false if an invalid
189 : * sequence of flags is encountered (repeat/invalid flag).
190 : *
191 : * N.B. flagStr must be rooted.
192 : */
193 : bool
194 : ParseRegExpFlags(JSContext* cx, JSString* flagStr, RegExpFlag* flagsOut);
195 :
196 : /* Assuming GetBuiltinClass(obj) is ESClass::RegExp, return a RegExpShared for obj. */
197 : inline RegExpShared*
198 0 : RegExpToShared(JSContext* cx, HandleObject obj)
199 : {
200 0 : if (obj->is<RegExpObject>())
201 0 : return RegExpObject::getShared(cx, obj.as<RegExpObject>());
202 :
203 0 : return Proxy::regexp_toShared(cx, obj);
204 : }
205 :
206 : template<XDRMode mode>
207 : bool
208 : XDRScriptRegExpObject(XDRState<mode>* xdr, MutableHandle<RegExpObject*> objp);
209 :
210 : extern JSObject*
211 : CloneScriptRegExpObject(JSContext* cx, RegExpObject& re);
212 :
213 : /* Escape all slashes and newlines in the given string. */
214 : extern JSAtom*
215 : EscapeRegExpPattern(JSContext* cx, HandleAtom src);
216 :
217 : template <typename CharT>
218 : extern bool
219 : HasRegExpMetaChars(const CharT* chars, size_t length);
220 :
221 : extern bool
222 : StringHasRegExpMetaChars(JSLinearString* str);
223 :
224 : } /* namespace js */
225 :
226 : #endif /* vm_RegExpObject_h */
|