1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sw=4 et tw=99 ft=cpp:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla SpiderMonkey JavaScript code.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * the Mozilla Foundation.
21 : * Portions created by the Initial Developer are Copyright (C) 2011
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * Chris Leary <cdleary@mozilla.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #ifndef RegExpStatics_h__
42 : #define RegExpStatics_h__
43 :
44 : #include "jscntxt.h"
45 : #include "jsgcmark.h"
46 :
47 : #include "gc/Barrier.h"
48 : #include "js/Vector.h"
49 :
50 : #include "vm/MatchPairs.h"
51 :
52 : namespace js {
53 :
54 : class RegExpStatics
55 66981 : {
56 : typedef Vector<int, 20, SystemAllocPolicy> Pairs;
57 : Pairs matchPairs;
58 : /* The input that was used to produce matchPairs. */
59 : HeapPtr<JSLinearString> matchPairsInput;
60 : /* The input last set on the statics. */
61 : HeapPtr<JSString> pendingInput;
62 : RegExpFlag flags;
63 : RegExpStatics *bufferLink;
64 : bool copied;
65 :
66 : bool createDependent(JSContext *cx, size_t start, size_t end, Value *out) const;
67 :
68 : inline void copyTo(RegExpStatics &dst);
69 :
70 : inline void aboutToWrite();
71 :
72 28361 : bool save(JSContext *cx, RegExpStatics *buffer) {
73 28361 : JS_ASSERT(!buffer->copied && !buffer->bufferLink);
74 28361 : buffer->bufferLink = bufferLink;
75 28361 : bufferLink = buffer;
76 28361 : if (!buffer->matchPairs.reserve(matchPairs.length())) {
77 0 : js_ReportOutOfMemory(cx);
78 0 : return false;
79 : }
80 28361 : return true;
81 : }
82 :
83 : inline void restore();
84 :
85 0 : void checkInvariants() {
86 : #if DEBUG
87 0 : if (pairCount() == 0) {
88 0 : JS_ASSERT(!matchPairsInput);
89 0 : return;
90 : }
91 :
92 : /* Pair count is non-zero, so there must be match pairs input. */
93 0 : JS_ASSERT(matchPairsInput);
94 0 : size_t mpiLen = matchPairsInput->length();
95 :
96 : /* Both members of the first pair must be non-negative. */
97 0 : JS_ASSERT(pairIsPresent(0));
98 0 : JS_ASSERT(get(0, 1) >= 0);
99 :
100 : /* Present pairs must be valid. */
101 0 : for (size_t i = 0; i < pairCount(); ++i) {
102 0 : if (!pairIsPresent(i))
103 0 : continue;
104 0 : int start = get(i, 0);
105 0 : int limit = get(i, 1);
106 0 : JS_ASSERT(mpiLen >= size_t(limit) && limit >= start && start >= 0);
107 : }
108 : #endif
109 : }
110 :
111 : /*
112 : * Since the first pair indicates the whole match, the paren pair
113 : * numbers have to be in the range [1, pairCount).
114 : */
115 17914 : void checkParenNum(size_t pairNum) const {
116 17914 : JS_ASSERT(1 <= pairNum);
117 17914 : JS_ASSERT(pairNum < pairCount());
118 17914 : }
119 :
120 : /* Precondition: paren is present. */
121 8948 : size_t getParenLength(size_t pairNum) const {
122 8948 : checkParenNum(pairNum);
123 8948 : JS_ASSERT(pairIsPresent(pairNum));
124 8948 : return get(pairNum, 1) - get(pairNum, 0);
125 : }
126 :
127 13492630 : int get(size_t pairNum, bool which) const {
128 13492630 : JS_ASSERT(pairNum < pairCount());
129 13492630 : return matchPairs[2 * pairNum + which];
130 : }
131 :
132 : /*
133 : * Check whether the index at |checkValidIndex| is valid (>= 0).
134 : * If so, construct a string for it and place it in |*out|.
135 : * If not, place undefined in |*out|.
136 : */
137 : bool makeMatch(JSContext *cx, size_t checkValidIndex, size_t pairNum, Value *out) const;
138 :
139 : void markFlagsSet(JSContext *cx);
140 :
141 : struct InitBuffer {};
142 28361 : explicit RegExpStatics(InitBuffer) : bufferLink(NULL), copied(false) {}
143 :
144 : friend class PreserveRegExpStatics;
145 :
146 : public:
147 : inline RegExpStatics();
148 :
149 : static JSObject *create(JSContext *cx, GlobalObject *parent);
150 :
151 : /* Mutators. */
152 :
153 : inline bool updateFromMatchPairs(JSContext *cx, JSLinearString *input, MatchPairs *newPairs);
154 : inline void setMultiline(JSContext *cx, bool enabled);
155 :
156 : inline void clear();
157 :
158 : /* Corresponds to JSAPI functionality to set the pending RegExp input. */
159 : inline void reset(JSContext *cx, JSString *newInput, bool newMultiline);
160 :
161 : inline void setPendingInput(JSString *newInput);
162 :
163 : /* Accessors. */
164 :
165 : /*
166 : * When there is a match present, the pairCount is at least 1 for the whole
167 : * match. There is one additional pair per parenthesis.
168 : *
169 : * Getting a parenCount requires there to be a match result as a precondition.
170 : */
171 :
172 : private:
173 16513921 : size_t pairCount() const {
174 16513921 : JS_ASSERT(matchPairs.length() % 2 == 0);
175 16513921 : return matchPairs.length() / 2;
176 : }
177 :
178 : public:
179 146304 : size_t parenCount() const {
180 146304 : size_t pc = pairCount();
181 146304 : JS_ASSERT(pc);
182 146304 : return pc - 1;
183 : }
184 :
185 : JSString *getPendingInput() const { return pendingInput; }
186 564398 : RegExpFlag getFlags() const { return flags; }
187 0 : bool multiline() const { return flags & MultilineFlag; }
188 :
189 3768725 : size_t matchStart() const {
190 3768725 : int start = get(0, 0);
191 3768725 : JS_ASSERT(start >= 0);
192 3768725 : return size_t(start);
193 : }
194 :
195 1870144 : size_t matchLimit() const {
196 1870144 : int limit = get(0, 1);
197 1870144 : JS_ASSERT(size_t(limit) >= matchStart() && limit >= 0);
198 1870144 : return size_t(limit);
199 : }
200 :
201 : /* Returns whether results for a non-empty match are present. */
202 1786742 : bool matched() const {
203 1786742 : JS_ASSERT(pairCount() > 0);
204 1786742 : JS_ASSERT_IF(get(0, 1) == -1, get(1, 1) == -1);
205 1786742 : return get(0, 1) - get(0, 0) > 0;
206 : }
207 :
208 196823 : void mark(JSTracer *trc) {
209 196823 : if (pendingInput)
210 8544 : MarkString(trc, &pendingInput, "res->pendingInput");
211 196823 : if (matchPairsInput)
212 8544 : MarkString(trc, &matchPairsInput, "res->matchPairsInput");
213 196823 : }
214 :
215 17914 : bool pairIsPresent(size_t pairNum) const {
216 17914 : return get(pairNum, 0) >= 0;
217 : }
218 :
219 : /* Value creators. */
220 :
221 : bool createPendingInput(JSContext *cx, Value *out) const;
222 763841 : bool createLastMatch(JSContext *cx, Value *out) const { return makeMatch(cx, 0, 0, out); }
223 : bool createLastParen(JSContext *cx, Value *out) const;
224 : bool createLeftContext(JSContext *cx, Value *out) const;
225 : bool createRightContext(JSContext *cx, Value *out) const;
226 :
227 : /* @param pairNum Any number >= 1. */
228 23911 : bool createParen(JSContext *cx, size_t pairNum, Value *out) const {
229 23911 : JS_ASSERT(pairNum >= 1);
230 23911 : if (pairNum >= pairCount()) {
231 0 : out->setString(cx->runtime->emptyString);
232 0 : return true;
233 : }
234 23911 : return makeMatch(cx, pairNum * 2, pairNum, out);
235 : }
236 :
237 : /* Substring creators. */
238 :
239 : void getParen(size_t pairNum, JSSubString *out) const;
240 : void getLastMatch(JSSubString *out) const;
241 : void getLastParen(JSSubString *out) const;
242 : void getLeftContext(JSSubString *out) const;
243 : void getRightContext(JSSubString *out) const;
244 : };
245 :
246 : class PreserveRegExpStatics
247 : {
248 : RegExpStatics * const original;
249 : RegExpStatics buffer;
250 :
251 : public:
252 28361 : explicit PreserveRegExpStatics(RegExpStatics *original)
253 : : original(original),
254 28361 : buffer(RegExpStatics::InitBuffer())
255 28361 : {}
256 :
257 28361 : bool init(JSContext *cx) {
258 28361 : return original->save(cx, &buffer);
259 : }
260 :
261 : inline ~PreserveRegExpStatics();
262 : };
263 :
264 : size_t SizeOfRegExpStaticsData(const JSObject *obj, JSMallocSizeOfFun mallocSizeOf);
265 :
266 : } /* namespace js */
267 :
268 : #endif
|