summaryrefslogtreecommitdiffstats
path: root/js/src/vm/RegExpStatics.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/vm/RegExpStatics.h')
-rw-r--r--js/src/vm/RegExpStatics.h415
1 files changed, 415 insertions, 0 deletions
diff --git a/js/src/vm/RegExpStatics.h b/js/src/vm/RegExpStatics.h
new file mode 100644
index 000000000..1274e0894
--- /dev/null
+++ b/js/src/vm/RegExpStatics.h
@@ -0,0 +1,415 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef vm_RegExpStatics_h
+#define vm_RegExpStatics_h
+
+#include "gc/Marking.h"
+#include "vm/MatchPairs.h"
+#include "vm/RegExpObject.h"
+#include "vm/Runtime.h"
+
+namespace js {
+
+class GlobalObject;
+class RegExpStaticsObject;
+
+class RegExpStatics
+{
+ /* The latest RegExp output, set after execution. */
+ VectorMatchPairs matches;
+ HeapPtr<JSLinearString*> matchesInput;
+
+ /*
+ * The previous RegExp input, used to resolve lazy state.
+ * A raw RegExpShared cannot be stored because it may be in
+ * a different compartment via evalcx().
+ */
+ HeapPtr<JSAtom*> lazySource;
+ RegExpFlag lazyFlags;
+ size_t lazyIndex;
+
+ /* The latest RegExp input, set before execution. */
+ HeapPtr<JSString*> pendingInput;
+
+ /*
+ * If non-zero, |matchesInput| and the |lazy*| fields may be used
+ * to replay the last executed RegExp, and |matches| is invalid.
+ */
+ int32_t pendingLazyEvaluation;
+
+ public:
+ RegExpStatics() { clear(); }
+ static RegExpStaticsObject* create(ExclusiveContext* cx, Handle<GlobalObject*> parent);
+
+ private:
+ bool executeLazy(JSContext* cx);
+
+ inline void checkInvariants();
+
+ /*
+ * Check whether a match for pair |pairNum| occurred. If so, allocate and
+ * store the match string in |*out|; otherwise place |undefined| in |*out|.
+ */
+ bool makeMatch(JSContext* cx, size_t pairNum, MutableHandleValue out);
+ bool createDependent(JSContext* cx, size_t start, size_t end, MutableHandleValue out);
+
+ struct InitBuffer {};
+ explicit RegExpStatics(InitBuffer) {}
+
+ public:
+ /* Mutators. */
+ inline void updateLazily(JSContext* cx, JSLinearString* input,
+ RegExpShared* shared, size_t lastIndex);
+ inline bool updateFromMatchPairs(JSContext* cx, JSLinearString* input, MatchPairs& newPairs);
+
+ inline void clear();
+
+ /* Corresponds to JSAPI functionality to set the pending RegExp input. */
+ void reset(JSContext* cx, JSString* newInput) {
+ clear();
+ pendingInput = newInput;
+ checkInvariants();
+ }
+
+ inline void setPendingInput(JSString* newInput);
+
+ public:
+ /* Default match accessor. */
+ const MatchPairs& getMatches() const {
+ /* Safe: only used by String methods, which do not set lazy mode. */
+ MOZ_ASSERT(!pendingLazyEvaluation);
+ return matches;
+ }
+
+ JSString* getPendingInput() const { return pendingInput; }
+
+ void mark(JSTracer* trc) {
+ /*
+ * Changes to this function must also be reflected in
+ * RegExpStatics::AutoRooter::trace().
+ */
+ TraceNullableEdge(trc, &matchesInput, "res->matchesInput");
+ TraceNullableEdge(trc, &lazySource, "res->lazySource");
+ TraceNullableEdge(trc, &pendingInput, "res->pendingInput");
+ }
+
+ /* Value creators. */
+
+ bool createPendingInput(JSContext* cx, MutableHandleValue out);
+ bool createLastMatch(JSContext* cx, MutableHandleValue out);
+ bool createLastParen(JSContext* cx, MutableHandleValue out);
+ bool createParen(JSContext* cx, size_t pairNum, MutableHandleValue out);
+ bool createLeftContext(JSContext* cx, MutableHandleValue out);
+ bool createRightContext(JSContext* cx, MutableHandleValue out);
+
+ /* Infallible substring creators. */
+
+ void getParen(size_t pairNum, JSSubString* out) const;
+ void getLastMatch(JSSubString* out) const;
+ void getLastParen(JSSubString* out) const;
+ void getLeftContext(JSSubString* out) const;
+ void getRightContext(JSSubString* out) const;
+
+ static size_t offsetOfPendingInput() {
+ return offsetof(RegExpStatics, pendingInput);
+ }
+
+ static size_t offsetOfMatchesInput() {
+ return offsetof(RegExpStatics, matchesInput);
+ }
+
+ static size_t offsetOfLazySource() {
+ return offsetof(RegExpStatics, lazySource);
+ }
+
+ static size_t offsetOfLazyFlags() {
+ return offsetof(RegExpStatics, lazyFlags);
+ }
+
+ static size_t offsetOfLazyIndex() {
+ return offsetof(RegExpStatics, lazyIndex);
+ }
+
+ static size_t offsetOfPendingLazyEvaluation() {
+ return offsetof(RegExpStatics, pendingLazyEvaluation);
+ }
+};
+
+inline bool
+RegExpStatics::createDependent(JSContext* cx, size_t start, size_t end, MutableHandleValue out)
+{
+ /* Private function: caller must perform lazy evaluation. */
+ MOZ_ASSERT(!pendingLazyEvaluation);
+
+ MOZ_ASSERT(start <= end);
+ MOZ_ASSERT(end <= matchesInput->length());
+ JSString* str = NewDependentString(cx, matchesInput, start, end - start);
+ if (!str)
+ return false;
+ out.setString(str);
+ return true;
+}
+
+inline bool
+RegExpStatics::createPendingInput(JSContext* cx, MutableHandleValue out)
+{
+ /* Lazy evaluation need not be resolved to return the input. */
+ out.setString(pendingInput ? pendingInput.get() : cx->runtime()->emptyString);
+ return true;
+}
+
+inline bool
+RegExpStatics::makeMatch(JSContext* cx, size_t pairNum, MutableHandleValue out)
+{
+ /* Private function: caller must perform lazy evaluation. */
+ MOZ_ASSERT(!pendingLazyEvaluation);
+
+ if (matches.empty() || pairNum >= matches.pairCount() || matches[pairNum].isUndefined()) {
+ out.setUndefined();
+ return true;
+ }
+
+ const MatchPair& pair = matches[pairNum];
+ return createDependent(cx, pair.start, pair.limit, out);
+}
+
+inline bool
+RegExpStatics::createLastMatch(JSContext* cx, MutableHandleValue out)
+{
+ if (!executeLazy(cx))
+ return false;
+ return makeMatch(cx, 0, out);
+}
+
+inline bool
+RegExpStatics::createLastParen(JSContext* cx, MutableHandleValue out)
+{
+ if (!executeLazy(cx))
+ return false;
+
+ if (matches.empty() || matches.pairCount() == 1) {
+ out.setString(cx->runtime()->emptyString);
+ return true;
+ }
+ const MatchPair& pair = matches[matches.pairCount() - 1];
+ if (pair.start == -1) {
+ out.setString(cx->runtime()->emptyString);
+ return true;
+ }
+ MOZ_ASSERT(pair.start >= 0 && pair.limit >= 0);
+ MOZ_ASSERT(pair.limit >= pair.start);
+ return createDependent(cx, pair.start, pair.limit, out);
+}
+
+inline bool
+RegExpStatics::createParen(JSContext* cx, size_t pairNum, MutableHandleValue out)
+{
+ MOZ_ASSERT(pairNum >= 1);
+ if (!executeLazy(cx))
+ return false;
+
+ if (matches.empty() || pairNum >= matches.pairCount()) {
+ out.setString(cx->runtime()->emptyString);
+ return true;
+ }
+ return makeMatch(cx, pairNum, out);
+}
+
+inline bool
+RegExpStatics::createLeftContext(JSContext* cx, MutableHandleValue out)
+{
+ if (!executeLazy(cx))
+ return false;
+
+ if (matches.empty()) {
+ out.setString(cx->runtime()->emptyString);
+ return true;
+ }
+ if (matches[0].start < 0) {
+ out.setUndefined();
+ return true;
+ }
+ return createDependent(cx, 0, matches[0].start, out);
+}
+
+inline bool
+RegExpStatics::createRightContext(JSContext* cx, MutableHandleValue out)
+{
+ if (!executeLazy(cx))
+ return false;
+
+ if (matches.empty()) {
+ out.setString(cx->runtime()->emptyString);
+ return true;
+ }
+ if (matches[0].limit < 0) {
+ out.setUndefined();
+ return true;
+ }
+ return createDependent(cx, matches[0].limit, matchesInput->length(), out);
+}
+
+inline void
+RegExpStatics::getParen(size_t pairNum, JSSubString* out) const
+{
+ MOZ_ASSERT(!pendingLazyEvaluation);
+
+ MOZ_ASSERT(pairNum >= 1 && pairNum < matches.pairCount());
+ const MatchPair& pair = matches[pairNum];
+ if (pair.isUndefined()) {
+ out->initEmpty(matchesInput);
+ return;
+ }
+ out->init(matchesInput, pair.start, pair.length());
+}
+
+inline void
+RegExpStatics::getLastMatch(JSSubString* out) const
+{
+ MOZ_ASSERT(!pendingLazyEvaluation);
+
+ if (matches.empty()) {
+ out->initEmpty(matchesInput);
+ return;
+ }
+ MOZ_ASSERT(matchesInput);
+ MOZ_ASSERT(matches[0].limit >= matches[0].start);
+ out->init(matchesInput, matches[0].start, matches[0].length());
+}
+
+inline void
+RegExpStatics::getLastParen(JSSubString* out) const
+{
+ MOZ_ASSERT(!pendingLazyEvaluation);
+
+ /* Note: the first pair is the whole match. */
+ if (matches.empty() || matches.pairCount() == 1) {
+ out->initEmpty(matchesInput);
+ return;
+ }
+ getParen(matches.parenCount(), out);
+}
+
+inline void
+RegExpStatics::getLeftContext(JSSubString* out) const
+{
+ MOZ_ASSERT(!pendingLazyEvaluation);
+
+ if (matches.empty()) {
+ out->initEmpty(matchesInput);
+ return;
+ }
+ out->init(matchesInput, 0, matches[0].start);
+}
+
+inline void
+RegExpStatics::getRightContext(JSSubString* out) const
+{
+ MOZ_ASSERT(!pendingLazyEvaluation);
+
+ if (matches.empty()) {
+ out->initEmpty(matchesInput);
+ return;
+ }
+ MOZ_ASSERT(matches[0].limit <= int(matchesInput->length()));
+ size_t length = matchesInput->length() - matches[0].limit;
+ out->init(matchesInput, matches[0].limit, length);
+}
+
+inline void
+RegExpStatics::updateLazily(JSContext* cx, JSLinearString* input,
+ RegExpShared* shared, size_t lastIndex)
+{
+ MOZ_ASSERT(input && shared);
+
+ BarrieredSetPair<JSString, JSLinearString>(cx->zone(),
+ pendingInput, input,
+ matchesInput, input);
+
+ lazySource = shared->source;
+ lazyFlags = shared->flags;
+ lazyIndex = lastIndex;
+ pendingLazyEvaluation = 1;
+}
+
+inline bool
+RegExpStatics::updateFromMatchPairs(JSContext* cx, JSLinearString* input, MatchPairs& newPairs)
+{
+ MOZ_ASSERT(input);
+
+ /* Unset all lazy state. */
+ pendingLazyEvaluation = false;
+ this->lazySource = nullptr;
+ this->lazyIndex = size_t(-1);
+
+ BarrieredSetPair<JSString, JSLinearString>(cx->zone(),
+ pendingInput, input,
+ matchesInput, input);
+
+ if (!matches.initArrayFrom(newPairs)) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+
+ return true;
+}
+
+inline void
+RegExpStatics::clear()
+{
+ matches.forgetArray();
+ matchesInput = nullptr;
+ lazySource = nullptr;
+ lazyFlags = RegExpFlag(0);
+ lazyIndex = size_t(-1);
+ pendingInput = nullptr;
+ pendingLazyEvaluation = false;
+}
+
+inline void
+RegExpStatics::setPendingInput(JSString* newInput)
+{
+ pendingInput = newInput;
+}
+
+inline void
+RegExpStatics::checkInvariants()
+{
+#ifdef DEBUG
+ if (pendingLazyEvaluation) {
+ MOZ_ASSERT(lazySource);
+ MOZ_ASSERT(matchesInput);
+ MOZ_ASSERT(lazyIndex != size_t(-1));
+ return;
+ }
+
+ if (matches.empty()) {
+ MOZ_ASSERT(!matchesInput);
+ return;
+ }
+
+ /* Pair count is non-zero, so there must be match pairs input. */
+ MOZ_ASSERT(matchesInput);
+ size_t mpiLen = matchesInput->length();
+
+ /* Both members of the first pair must be non-negative. */
+ MOZ_ASSERT(!matches[0].isUndefined());
+ MOZ_ASSERT(matches[0].limit >= 0);
+
+ /* Present pairs must be valid. */
+ for (size_t i = 0; i < matches.pairCount(); i++) {
+ if (matches[i].isUndefined())
+ continue;
+ const MatchPair& pair = matches[i];
+ MOZ_ASSERT(mpiLen >= size_t(pair.limit) && pair.limit >= pair.start && pair.start >= 0);
+ }
+#endif /* DEBUG */
+}
+
+} /* namespace js */
+
+#endif /* vm_RegExpStatics_h */