Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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/. */
7 #ifndef vm_RegExpStatics_h
8 #define vm_RegExpStatics_h
10 #include "gc/Marking.h"
11 #include "vm/MatchPairs.h"
12 #include "vm/RegExpObject.h"
13 #include "vm/Runtime.h"
15 namespace js {
17 class GlobalObject;
19 class RegExpStatics
20 {
21 /* The latest RegExp output, set after execution. */
22 VectorMatchPairs matches;
23 HeapPtr<JSLinearString> matchesInput;
25 /*
26 * The previous RegExp input, used to resolve lazy state.
27 * A raw RegExpShared cannot be stored because it may be in
28 * a different compartment via evalcx().
29 */
30 HeapPtr<JSAtom> lazySource;
31 RegExpFlag lazyFlags;
32 size_t lazyIndex;
34 /* The latest RegExp input, set before execution. */
35 HeapPtr<JSString> pendingInput;
36 RegExpFlag flags;
38 /*
39 * If true, |matchesInput| and the |lazy*| fields may be used
40 * to replay the last executed RegExp, and |matches| is invalid.
41 */
42 bool pendingLazyEvaluation;
44 /* Linkage for preserving RegExpStatics during nested RegExp execution. */
45 RegExpStatics *bufferLink;
46 bool copied;
48 public:
49 RegExpStatics() : bufferLink(nullptr), copied(false) { clear(); }
50 static JSObject *create(JSContext *cx, GlobalObject *parent);
52 private:
53 bool executeLazy(JSContext *cx);
55 inline void aboutToWrite();
56 inline void copyTo(RegExpStatics &dst);
58 inline void restore();
59 bool save(JSContext *cx, RegExpStatics *buffer) {
60 JS_ASSERT(!buffer->copied && !buffer->bufferLink);
61 buffer->bufferLink = bufferLink;
62 bufferLink = buffer;
63 if (!buffer->matches.allocOrExpandArray(matches.length())) {
64 js_ReportOutOfMemory(cx);
65 return false;
66 }
67 return true;
68 }
70 inline void checkInvariants();
72 /*
73 * Check whether the index at |checkValidIndex| is valid (>= 0).
74 * If so, construct a string for it and place it in |*out|.
75 * If not, place undefined in |*out|.
76 */
77 bool makeMatch(JSContext *cx, size_t checkValidIndex, size_t pairNum, MutableHandleValue out);
78 bool createDependent(JSContext *cx, size_t start, size_t end, MutableHandleValue out);
80 void markFlagsSet(JSContext *cx);
82 struct InitBuffer {};
83 explicit RegExpStatics(InitBuffer) : bufferLink(nullptr), copied(false) {}
85 friend class PreserveRegExpStatics;
86 friend class AutoRegExpStaticsBuffer;
88 public:
89 /* Mutators. */
90 inline void updateLazily(JSContext *cx, JSLinearString *input,
91 RegExpShared *shared, size_t lastIndex);
92 inline bool updateFromMatchPairs(JSContext *cx, JSLinearString *input, MatchPairs &newPairs);
94 void setMultiline(JSContext *cx, bool enabled) {
95 aboutToWrite();
96 if (enabled) {
97 flags = RegExpFlag(flags | MultilineFlag);
98 markFlagsSet(cx);
99 } else {
100 flags = RegExpFlag(flags & ~MultilineFlag);
101 }
102 }
104 inline void clear();
106 /* Corresponds to JSAPI functionality to set the pending RegExp input. */
107 void reset(JSContext *cx, JSString *newInput, bool newMultiline) {
108 aboutToWrite();
109 clear();
110 pendingInput = newInput;
111 setMultiline(cx, newMultiline);
112 checkInvariants();
113 }
115 inline void setPendingInput(JSString *newInput);
117 public:
118 /* Default match accessor. */
119 const MatchPairs &getMatches() const {
120 /* Safe: only used by String methods, which do not set lazy mode. */
121 JS_ASSERT(!pendingLazyEvaluation);
122 return matches;
123 }
125 JSString *getPendingInput() const { return pendingInput; }
127 RegExpFlag getFlags() const { return flags; }
128 bool multiline() const { return flags & MultilineFlag; }
130 /* Returns whether results for a non-empty match are present. */
131 bool matched() const {
132 /* Safe: only used by String methods, which do not set lazy mode. */
133 JS_ASSERT(!pendingLazyEvaluation);
134 JS_ASSERT(matches.pairCount() > 0);
135 return matches[0].limit - matches[0].start > 0;
136 }
138 void mark(JSTracer *trc) {
139 /*
140 * Changes to this function must also be reflected in
141 * RegExpStatics::AutoRooter::trace().
142 */
143 if (matchesInput)
144 MarkString(trc, &matchesInput, "res->matchesInput");
145 if (lazySource)
146 MarkString(trc, &lazySource, "res->lazySource");
147 if (pendingInput)
148 MarkString(trc, &pendingInput, "res->pendingInput");
149 }
151 /* Value creators. */
153 bool createPendingInput(JSContext *cx, MutableHandleValue out);
154 bool createLastMatch(JSContext *cx, MutableHandleValue out);
155 bool createLastParen(JSContext *cx, MutableHandleValue out);
156 bool createParen(JSContext *cx, size_t pairNum, MutableHandleValue out);
157 bool createLeftContext(JSContext *cx, MutableHandleValue out);
158 bool createRightContext(JSContext *cx, MutableHandleValue out);
160 /* Infallible substring creators. */
162 void getParen(size_t pairNum, JSSubString *out) const;
163 void getLastMatch(JSSubString *out) const;
164 void getLastParen(JSSubString *out) const;
165 void getLeftContext(JSSubString *out) const;
166 void getRightContext(JSSubString *out) const;
167 };
169 class AutoRegExpStaticsBuffer : private JS::CustomAutoRooter
170 {
171 public:
172 explicit AutoRegExpStaticsBuffer(JSContext *cx
173 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
174 : CustomAutoRooter(cx), statics(RegExpStatics::InitBuffer())
175 {
176 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
177 }
179 RegExpStatics& getStatics() { return statics; }
181 private:
182 virtual void trace(JSTracer *trc) {
183 if (statics.matchesInput) {
184 MarkStringRoot(trc, reinterpret_cast<JSString**>(&statics.matchesInput),
185 "AutoRegExpStaticsBuffer matchesInput");
186 }
187 if (statics.lazySource) {
188 MarkStringRoot(trc, reinterpret_cast<JSString**>(&statics.lazySource),
189 "AutoRegExpStaticsBuffer lazySource");
190 }
191 if (statics.pendingInput) {
192 MarkStringRoot(trc, reinterpret_cast<JSString**>(&statics.pendingInput),
193 "AutoRegExpStaticsBuffer pendingInput");
194 }
195 }
197 RegExpStatics statics;
198 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
199 };
201 class PreserveRegExpStatics
202 {
203 RegExpStatics * const original;
204 AutoRegExpStaticsBuffer buffer;
206 public:
207 explicit PreserveRegExpStatics(JSContext *cx, RegExpStatics *original)
208 : original(original),
209 buffer(cx)
210 {}
212 bool init(JSContext *cx) {
213 return original->save(cx, &buffer.getStatics());
214 }
216 ~PreserveRegExpStatics() { original->restore(); }
217 };
219 inline bool
220 RegExpStatics::createDependent(JSContext *cx, size_t start, size_t end, MutableHandleValue out)
221 {
222 /* Private function: caller must perform lazy evaluation. */
223 JS_ASSERT(!pendingLazyEvaluation);
225 JS_ASSERT(start <= end);
226 JS_ASSERT(end <= matchesInput->length());
227 JSString *str = js_NewDependentString(cx, matchesInput, start, end - start);
228 if (!str)
229 return false;
230 out.setString(str);
231 return true;
232 }
234 inline bool
235 RegExpStatics::createPendingInput(JSContext *cx, MutableHandleValue out)
236 {
237 /* Lazy evaluation need not be resolved to return the input. */
238 out.setString(pendingInput ? pendingInput.get() : cx->runtime()->emptyString);
239 return true;
240 }
242 inline bool
243 RegExpStatics::makeMatch(JSContext *cx, size_t checkValidIndex, size_t pairNum,
244 MutableHandleValue out)
245 {
246 /* Private function: caller must perform lazy evaluation. */
247 JS_ASSERT(!pendingLazyEvaluation);
249 bool checkWhich = checkValidIndex % 2;
250 size_t checkPair = checkValidIndex / 2;
252 if (matches.empty() || checkPair >= matches.pairCount() ||
253 (checkWhich ? matches[checkPair].limit : matches[checkPair].start) < 0)
254 {
255 out.setString(cx->runtime()->emptyString);
256 return true;
257 }
258 const MatchPair &pair = matches[pairNum];
259 return createDependent(cx, pair.start, pair.limit, out);
260 }
262 inline bool
263 RegExpStatics::createLastMatch(JSContext *cx, MutableHandleValue out)
264 {
265 if (!executeLazy(cx))
266 return false;
267 return makeMatch(cx, 0, 0, out);
268 }
270 inline bool
271 RegExpStatics::createLastParen(JSContext *cx, MutableHandleValue out)
272 {
273 if (!executeLazy(cx))
274 return false;
276 if (matches.empty() || matches.pairCount() == 1) {
277 out.setString(cx->runtime()->emptyString);
278 return true;
279 }
280 const MatchPair &pair = matches[matches.pairCount() - 1];
281 if (pair.start == -1) {
282 out.setString(cx->runtime()->emptyString);
283 return true;
284 }
285 JS_ASSERT(pair.start >= 0 && pair.limit >= 0);
286 JS_ASSERT(pair.limit >= pair.start);
287 return createDependent(cx, pair.start, pair.limit, out);
288 }
290 inline bool
291 RegExpStatics::createParen(JSContext *cx, size_t pairNum, MutableHandleValue out)
292 {
293 JS_ASSERT(pairNum >= 1);
294 if (!executeLazy(cx))
295 return false;
297 if (matches.empty() || pairNum >= matches.pairCount()) {
298 out.setString(cx->runtime()->emptyString);
299 return true;
300 }
301 return makeMatch(cx, pairNum * 2, pairNum, out);
302 }
304 inline bool
305 RegExpStatics::createLeftContext(JSContext *cx, MutableHandleValue out)
306 {
307 if (!executeLazy(cx))
308 return false;
310 if (matches.empty()) {
311 out.setString(cx->runtime()->emptyString);
312 return true;
313 }
314 if (matches[0].start < 0) {
315 out.setUndefined();
316 return true;
317 }
318 return createDependent(cx, 0, matches[0].start, out);
319 }
321 inline bool
322 RegExpStatics::createRightContext(JSContext *cx, MutableHandleValue out)
323 {
324 if (!executeLazy(cx))
325 return false;
327 if (matches.empty()) {
328 out.setString(cx->runtime()->emptyString);
329 return true;
330 }
331 if (matches[0].limit < 0) {
332 out.setUndefined();
333 return true;
334 }
335 return createDependent(cx, matches[0].limit, matchesInput->length(), out);
336 }
338 inline void
339 RegExpStatics::getParen(size_t pairNum, JSSubString *out) const
340 {
341 JS_ASSERT(!pendingLazyEvaluation);
343 JS_ASSERT(pairNum >= 1 && pairNum < matches.pairCount());
344 const MatchPair &pair = matches[pairNum];
345 if (pair.isUndefined()) {
346 *out = js_EmptySubString;
347 return;
348 }
349 out->chars = matchesInput->chars() + pair.start;
350 out->length = pair.length();
351 }
353 inline void
354 RegExpStatics::getLastMatch(JSSubString *out) const
355 {
356 JS_ASSERT(!pendingLazyEvaluation);
358 if (matches.empty()) {
359 *out = js_EmptySubString;
360 return;
361 }
362 JS_ASSERT(matchesInput);
363 out->chars = matchesInput->chars() + matches[0].start;
364 JS_ASSERT(matches[0].limit >= matches[0].start);
365 out->length = matches[0].length();
366 }
368 inline void
369 RegExpStatics::getLastParen(JSSubString *out) const
370 {
371 JS_ASSERT(!pendingLazyEvaluation);
373 /* Note: the first pair is the whole match. */
374 if (matches.empty() || matches.pairCount() == 1) {
375 *out = js_EmptySubString;
376 return;
377 }
378 getParen(matches.parenCount(), out);
379 }
381 inline void
382 RegExpStatics::getLeftContext(JSSubString *out) const
383 {
384 JS_ASSERT(!pendingLazyEvaluation);
386 if (matches.empty()) {
387 *out = js_EmptySubString;
388 return;
389 }
390 out->chars = matchesInput->chars();
391 out->length = matches[0].start;
392 }
394 inline void
395 RegExpStatics::getRightContext(JSSubString *out) const
396 {
397 JS_ASSERT(!pendingLazyEvaluation);
399 if (matches.empty()) {
400 *out = js_EmptySubString;
401 return;
402 }
403 out->chars = matchesInput->chars() + matches[0].limit;
404 JS_ASSERT(matches[0].limit <= int(matchesInput->length()));
405 out->length = matchesInput->length() - matches[0].limit;
406 }
408 inline void
409 RegExpStatics::copyTo(RegExpStatics &dst)
410 {
411 /* Destination buffer has already been reserved by save(). */
412 if (!pendingLazyEvaluation)
413 dst.matches.initArrayFrom(matches);
415 dst.matchesInput = matchesInput;
416 dst.lazySource = lazySource;
417 dst.lazyFlags = lazyFlags;
418 dst.lazyIndex = lazyIndex;
419 dst.pendingInput = pendingInput;
420 dst.flags = flags;
421 dst.pendingLazyEvaluation = pendingLazyEvaluation;
423 JS_ASSERT_IF(pendingLazyEvaluation, lazySource);
424 JS_ASSERT_IF(pendingLazyEvaluation, matchesInput);
425 }
427 inline void
428 RegExpStatics::aboutToWrite()
429 {
430 if (bufferLink && !bufferLink->copied) {
431 copyTo(*bufferLink);
432 bufferLink->copied = true;
433 }
434 }
436 inline void
437 RegExpStatics::restore()
438 {
439 if (bufferLink->copied)
440 bufferLink->copyTo(*this);
441 bufferLink = bufferLink->bufferLink;
442 }
444 inline void
445 RegExpStatics::updateLazily(JSContext *cx, JSLinearString *input,
446 RegExpShared *shared, size_t lastIndex)
447 {
448 JS_ASSERT(input && shared);
449 aboutToWrite();
451 BarrieredSetPair<JSString, JSLinearString>(cx->zone(),
452 pendingInput, input,
453 matchesInput, input);
455 lazySource = shared->source;
456 lazyFlags = shared->flags;
457 lazyIndex = lastIndex;
458 pendingLazyEvaluation = true;
459 }
461 inline bool
462 RegExpStatics::updateFromMatchPairs(JSContext *cx, JSLinearString *input, MatchPairs &newPairs)
463 {
464 JS_ASSERT(input);
465 aboutToWrite();
467 /* Unset all lazy state. */
468 pendingLazyEvaluation = false;
469 this->lazySource = nullptr;
470 this->lazyIndex = size_t(-1);
472 BarrieredSetPair<JSString, JSLinearString>(cx->zone(),
473 pendingInput, input,
474 matchesInput, input);
476 if (!matches.initArrayFrom(newPairs)) {
477 js_ReportOutOfMemory(cx);
478 return false;
479 }
481 return true;
482 }
484 inline void
485 RegExpStatics::clear()
486 {
487 aboutToWrite();
489 matches.forgetArray();
490 matchesInput = nullptr;
491 lazySource = nullptr;
492 lazyFlags = RegExpFlag(0);
493 lazyIndex = size_t(-1);
494 pendingInput = nullptr;
495 flags = RegExpFlag(0);
496 pendingLazyEvaluation = false;
497 }
499 inline void
500 RegExpStatics::setPendingInput(JSString *newInput)
501 {
502 aboutToWrite();
503 pendingInput = newInput;
504 }
506 inline void
507 RegExpStatics::checkInvariants()
508 {
509 #ifdef DEBUG
510 if (pendingLazyEvaluation) {
511 JS_ASSERT(lazySource);
512 JS_ASSERT(matchesInput);
513 JS_ASSERT(lazyIndex != size_t(-1));
514 return;
515 }
517 if (matches.empty()) {
518 JS_ASSERT(!matchesInput);
519 return;
520 }
522 /* Pair count is non-zero, so there must be match pairs input. */
523 JS_ASSERT(matchesInput);
524 size_t mpiLen = matchesInput->length();
526 /* Both members of the first pair must be non-negative. */
527 JS_ASSERT(!matches[0].isUndefined());
528 JS_ASSERT(matches[0].limit >= 0);
530 /* Present pairs must be valid. */
531 for (size_t i = 0; i < matches.pairCount(); i++) {
532 if (matches[i].isUndefined())
533 continue;
534 const MatchPair &pair = matches[i];
535 JS_ASSERT(mpiLen >= size_t(pair.limit) && pair.limit >= pair.start && pair.start >= 0);
536 }
537 #endif /* DEBUG */
538 }
540 } /* namespace js */
542 #endif /* vm_RegExpStatics_h */