Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / src / jsregexp.h
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * ***** BEGIN LICENSE BLOCK *****
4  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5  *
6  * The contents of this file are subject to the Mozilla Public License Version
7  * 1.1 (the "License"); you may not use this file except in compliance with
8  * the License. You may obtain a copy of the License at
9  * http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  *
16  * The Original Code is Mozilla Communicator client code, released
17  * March 31, 1998.
18  *
19  * The Initial Developer of the Original Code is
20  * Netscape Communications Corporation.
21  * Portions created by the Initial Developer are Copyright (C) 1998
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Alternatively, the contents of this file may be used under the terms of
27  * either of the GNU General Public License Version 2 or later (the "GPL"),
28  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29  * in which case the provisions of the GPL or the LGPL are applicable instead
30  * of those above. If you wish to allow use of your version of this file only
31  * under the terms of either the GPL or the LGPL, and not to allow others to
32  * use your version of this file under the terms of the MPL, indicate your
33  * decision by deleting the provisions above and replace them with the notice
34  * and other provisions required by the GPL or the LGPL. If you do not delete
35  * the provisions above, a recipient may use your version of this file under
36  * the terms of any one of the MPL, the GPL or the LGPL.
37  *
38  * ***** END LICENSE BLOCK ***** */
39
40 #ifndef jsregexp_h___
41 #define jsregexp_h___
42 /*
43  * JS regular expression interface.
44  */
45 #include <stddef.h>
46 #include "jsprvtd.h"
47 #include "jsstr.h"
48 #include "jscntxt.h"
49 #include "jsvector.h"
50
51 #ifdef JS_THREADSAFE
52 #include "jsdhash.h"
53 #endif
54
55 extern js::Class js_RegExpClass;
56
57 namespace js {
58
59 class RegExpStatics
60 {
61     typedef Vector<int, 20, SystemAllocPolicy> MatchPairs;
62     MatchPairs      matchPairs;
63     /* The input that was used to produce matchPairs. */
64     JSLinearString  *matchPairsInput;
65     /* The input last set on the statics. */
66     JSString        *pendingInput;
67     uintN           flags;
68     RegExpStatics   *bufferLink;
69     bool            copied;
70
71     bool createDependent(JSContext *cx, size_t start, size_t end, Value *out) const;
72
73     void copyTo(RegExpStatics &dst) {
74         dst.matchPairs.clear();
75         /* 'save' has already reserved space in matchPairs */
76         JS_ALWAYS_TRUE(dst.matchPairs.append(matchPairs));
77         dst.matchPairsInput = matchPairsInput;
78         dst.pendingInput = pendingInput;
79         dst.flags = flags;
80     }
81
82     void aboutToWrite() {
83         if (bufferLink && !bufferLink->copied) {
84             copyTo(*bufferLink);
85             bufferLink->copied = true;
86         }
87     }
88
89     bool save(JSContext *cx, RegExpStatics *buffer) {
90         JS_ASSERT(!buffer->copied && !buffer->bufferLink);
91         buffer->bufferLink = bufferLink;
92         bufferLink = buffer;
93         if (!buffer->matchPairs.reserve(matchPairs.length())) {
94             js_ReportOutOfMemory(cx);
95             return false;
96         }
97         return true;
98     }
99
100     void restore() {
101         if (bufferLink->copied)
102             bufferLink->copyTo(*this);
103         bufferLink = bufferLink->bufferLink;
104     }
105
106     void checkInvariants() {
107 #if DEBUG
108         if (pairCount() == 0) {
109             JS_ASSERT(!matchPairsInput);
110             return;
111         }
112
113         /* Pair count is non-zero, so there must be match pairs input. */
114         JS_ASSERT(matchPairsInput);
115         size_t mpiLen = matchPairsInput->length();
116
117         /* Both members of the first pair must be non-negative. */
118         JS_ASSERT(pairIsPresent(0));
119         JS_ASSERT(get(0, 1) >= 0);
120
121         /* Present pairs must be valid. */
122         for (size_t i = 0; i < pairCount(); ++i) {
123             if (!pairIsPresent(i))
124                 continue;
125             int start = get(i, 0);
126             int limit = get(i, 1);
127             JS_ASSERT(mpiLen >= size_t(limit) && limit >= start && start >= 0);
128         }
129 #endif
130     }
131
132     /* 
133      * Since the first pair indicates the whole match, the paren pair numbers have to be in the
134      * range [1, pairCount).
135      */
136     void checkParenNum(size_t pairNum) const {
137         JS_ASSERT(1 <= pairNum);
138         JS_ASSERT(pairNum < pairCount());
139     }
140
141     bool pairIsPresent(size_t pairNum) const {
142         return get(pairNum, 0) >= 0;
143     }
144
145     /* Precondition: paren is present. */
146     size_t getParenLength(size_t pairNum) const {
147         checkParenNum(pairNum);
148         JS_ASSERT(pairIsPresent(pairNum));
149         return get(pairNum, 1) - get(pairNum, 0);
150     }
151
152     int get(size_t pairNum, bool which) const {
153         JS_ASSERT(pairNum < pairCount());
154         return matchPairs[2 * pairNum + which];
155     }
156
157     /*
158      * Check whether the index at |checkValidIndex| is valid (>= 0).
159      * If so, construct a string for it and place it in |*out|.
160      * If not, place undefined in |*out|.
161      */
162     bool makeMatch(JSContext *cx, size_t checkValidIndex, size_t pairNum, Value *out) const;
163
164     static const uintN allFlags = JSREG_FOLD | JSREG_GLOB | JSREG_STICKY | JSREG_MULTILINE;
165
166     struct InitBuffer {};
167     explicit RegExpStatics(InitBuffer) : bufferLink(NULL), copied(false) {}
168
169     friend class PreserveRegExpStatics;
170
171   public:
172     RegExpStatics() : bufferLink(NULL), copied(false) { clear(); }
173
174     static RegExpStatics *extractFrom(JSObject *global);
175
176     /* Mutators. */
177
178     bool updateFromMatch(JSContext *cx, JSLinearString *input, int *buf, size_t matchItemCount) {
179         aboutToWrite();
180         pendingInput = input;
181
182         if (!matchPairs.resizeUninitialized(matchItemCount)) {
183             js_ReportOutOfMemory(cx);
184             return false;
185         }
186
187         for (size_t i = 0; i < matchItemCount; ++i)
188             matchPairs[i] = buf[i];
189
190         matchPairsInput = input;
191         return true;
192     }
193
194     void setMultiline(bool enabled) {
195         aboutToWrite();
196         if (enabled)
197             flags = flags | JSREG_MULTILINE;
198         else
199             flags = flags & ~JSREG_MULTILINE;
200     }
201
202     void clear() {
203         aboutToWrite();
204         flags = 0;
205         pendingInput = NULL;
206         matchPairsInput = NULL;
207         matchPairs.clear();
208     }
209
210     /* Corresponds to JSAPI functionality to set the pending RegExp input. */
211     void reset(JSString *newInput, bool newMultiline) {
212         aboutToWrite();
213         clear();
214         pendingInput = newInput;
215         setMultiline(newMultiline);
216         checkInvariants();
217     }
218
219     void setPendingInput(JSString *newInput) {
220         aboutToWrite();
221         pendingInput = newInput;
222     }
223
224     /* Accessors. */
225
226     /*
227      * When there is a match present, the pairCount is at least 1 for the whole
228      * match. There is one additional pair per parenthesis.
229      *
230      * Getting a parenCount requires there to be a match result as a precondition.
231      */
232
233   private:
234     size_t pairCount() const {
235         JS_ASSERT(matchPairs.length() % 2 == 0);
236         return matchPairs.length() / 2;
237     }
238
239   public:
240     size_t parenCount() const {
241         size_t pc = pairCount();
242         JS_ASSERT(pc);
243         return pc - 1;
244     }
245
246     JSString *getPendingInput() const { return pendingInput; }
247     uintN getFlags() const { return flags; }
248     bool multiline() const { return flags & JSREG_MULTILINE; }
249
250     size_t matchStart() const {
251         int start = get(0, 0);
252         JS_ASSERT(start >= 0);
253         return size_t(start);
254     }
255
256     size_t matchLimit() const {
257         int limit = get(0, 1);
258         JS_ASSERT(size_t(limit) >= matchStart() && limit >= 0);
259         return size_t(limit);
260     }
261
262     /* Returns whether results for a non-empty match are present. */
263     bool matched() const {
264         JS_ASSERT(pairCount() > 0);
265         JS_ASSERT_IF(get(0, 1) == -1, get(1, 1) == -1);
266         return get(0, 1) - get(0, 0) > 0;
267     }
268
269     void mark(JSTracer *trc) const {
270         if (pendingInput)
271             JS_CALL_STRING_TRACER(trc, pendingInput, "res->pendingInput");
272         if (matchPairsInput)
273             JS_CALL_STRING_TRACER(trc, matchPairsInput, "res->matchPairsInput");
274     }
275
276     /* Value creators. */
277
278     bool createPendingInput(JSContext *cx, Value *out) const;
279     bool createLastMatch(JSContext *cx, Value *out) const { return makeMatch(cx, 0, 0, out); }
280     bool createLastParen(JSContext *cx, Value *out) const;
281     bool createLeftContext(JSContext *cx, Value *out) const;
282     bool createRightContext(JSContext *cx, Value *out) const;
283
284     /* @param pairNum   Any number >= 1. */
285     bool createParen(JSContext *cx, size_t pairNum, Value *out) const {
286         JS_ASSERT(pairNum >= 1);
287         if (pairNum >= pairCount()) {
288             out->setString(cx->runtime->emptyString);
289             return true;
290         }
291         return makeMatch(cx, pairNum * 2, pairNum, out);
292     }
293
294     /* Substring creators. */
295
296     void getParen(size_t pairNum, JSSubString *out) const;
297     void getLastMatch(JSSubString *out) const;
298     void getLastParen(JSSubString *out) const;
299     void getLeftContext(JSSubString *out) const;
300     void getRightContext(JSSubString *out) const;
301 };
302
303 class PreserveRegExpStatics
304 {
305     RegExpStatics *const original;
306     RegExpStatics buffer;
307
308   public:
309     explicit PreserveRegExpStatics(RegExpStatics *original)
310      : original(original),
311        buffer(RegExpStatics::InitBuffer())
312     {}
313
314     bool init(JSContext *cx) {
315         return original->save(cx, &buffer);
316     }
317
318     ~PreserveRegExpStatics() {
319         original->restore();
320     }
321 };
322
323 }
324
325 static inline bool
326 VALUE_IS_REGEXP(JSContext *cx, js::Value v)
327 {
328     return !v.isPrimitive() && v.toObject().isRegExp();
329 }
330
331 inline const js::Value &
332 JSObject::getRegExpLastIndex() const
333 {
334     JS_ASSERT(isRegExp());
335     return getSlot(JSSLOT_REGEXP_LAST_INDEX);
336 }
337
338 inline void
339 JSObject::setRegExpLastIndex(const js::Value &v)
340 {
341     JS_ASSERT(isRegExp());
342     setSlot(JSSLOT_REGEXP_LAST_INDEX, v);
343 }
344
345 inline void
346 JSObject::setRegExpLastIndex(jsdouble d)
347 {
348     JS_ASSERT(isRegExp());
349     setSlot(JSSLOT_REGEXP_LAST_INDEX, js::NumberValue(d));
350 }
351
352 inline void
353 JSObject::zeroRegExpLastIndex()
354 {
355     JS_ASSERT(isRegExp());
356     getSlotRef(JSSLOT_REGEXP_LAST_INDEX).setInt32(0);
357 }
358
359 namespace js { class AutoStringRooter; }
360
361 inline bool
362 JSObject::isRegExp() const
363 {
364     return getClass() == &js_RegExpClass;
365 }
366
367 extern JS_FRIEND_API(JSBool)
368 js_ObjectIsRegExp(JSObject *obj);
369
370 extern JSObject *
371 js_InitRegExpClass(JSContext *cx, JSObject *obj);
372
373 /*
374  * Export js_regexp_toString to the decompiler.
375  */
376 extern JSBool
377 js_regexp_toString(JSContext *cx, JSObject *obj, js::Value *vp);
378
379 extern JS_FRIEND_API(JSObject *) JS_FASTCALL
380 js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto);
381
382 /*
383  * Move data from |cx|'s regexp statics to |statics| and root the input string in |tvr| if it's
384  * available.
385  */
386 extern JS_FRIEND_API(void)
387 js_SaveAndClearRegExpStatics(JSContext *cx, js::RegExpStatics *res, js::AutoStringRooter *tvr);
388
389 /* Move the data from |statics| into |cx|. */
390 extern JS_FRIEND_API(void)
391 js_RestoreRegExpStatics(JSContext *cx, js::RegExpStatics *res);
392
393 extern JSBool
394 js_XDRRegExpObject(JSXDRState *xdr, JSObject **objp);
395
396 extern JSBool
397 js_regexp_exec(JSContext *cx, uintN argc, js::Value *vp);
398 extern JSBool
399 js_regexp_test(JSContext *cx, uintN argc, js::Value *vp);
400
401 #endif /* jsregexp_h___ */