#ifndef vm_RegExpShared_h
#define vm_RegExpShared_h
#include "mozilla/Assertions.h"
#include "mozilla/MemoryReporting.h"
#include "gc/Barrier.h"
#include "gc/Heap.h"
#include "gc/Marking.h"
#include "gc/Zone.h"
#include "js/AllocPolicy.h"
#include "js/UbiNode.h"
#include "js/Vector.h"
#include "vm/ArrayObject.h"
#include "vm/JSAtom.h"
#include "vm/RegExpConstants.h"
namespace js {
class ArrayObject;
class RegExpRealm;
class RegExpShared;
class RegExpStatics;
class VectorMatchPairs;
using RootedRegExpShared = JS::Rooted<RegExpShared*>;
using HandleRegExpShared = JS::Handle<RegExpShared*>;
using MutableHandleRegExpShared = JS::MutableHandle<RegExpShared*>;
class RegExpShared : public gc::TenuredCell {
public:
enum CompilationMode { Normal, MatchOnly };
enum ForceByteCodeEnum { DontForceByteCode, ForceByteCode };
using JitCodeTable = UniquePtr<uint8_t[], JS::FreePolicy>;
using JitCodeTables = Vector<JitCodeTable, 0, SystemAllocPolicy>;
private:
friend class RegExpStatics;
friend class RegExpZone;
struct RegExpCompilation {
ReadBarriered<jit::JitCode*> jitCode;
uint8_t* byteCode;
RegExpCompilation() : byteCode(nullptr) {}
bool compiled(ForceByteCodeEnum force = DontForceByteCode) const {
return byteCode || (force == DontForceByteCode && jitCode);
}
};
GCPtr<JSAtom*> source;
RegExpFlag flags;
bool canStringMatch;
size_t parenCount;
RegExpCompilation compilationArray[4];
static int CompilationIndex(CompilationMode mode, bool latin1) {
switch (mode) {
case Normal:
return latin1 ? 0 : 1;
case MatchOnly:
return latin1 ? 2 : 3;
}
MOZ_CRASH();
}
JitCodeTables tables;
RegExpShared(JSAtom* source, RegExpFlag flags);
static bool compile(JSContext* cx, MutableHandleRegExpShared res,
HandleLinearString input, CompilationMode mode,
ForceByteCodeEnum force);
static bool compile(JSContext* cx, MutableHandleRegExpShared res,
HandleAtom pattern, HandleLinearString input,
CompilationMode mode, ForceByteCodeEnum force);
static bool compileIfNecessary(JSContext* cx, MutableHandleRegExpShared res,
HandleLinearString input, CompilationMode mode,
ForceByteCodeEnum force);
const RegExpCompilation& compilation(CompilationMode mode,
bool latin1) const {
return compilationArray[CompilationIndex(mode, latin1)];
}
RegExpCompilation& compilation(CompilationMode mode, bool latin1) {
return compilationArray[CompilationIndex(mode, latin1)];
}
public:
~RegExpShared() = delete;
static RegExpRunStatus execute(JSContext* cx, MutableHandleRegExpShared res,
HandleLinearString input, size_t searchIndex,
VectorMatchPairs* matches, size_t* endIndex);
bool addTable(JitCodeTable table) { return tables.append(std::move(table)); }
size_t getParenCount() const {
MOZ_ASSERT(isCompiled());
return parenCount;
}
size_t pairCount() const { return getParenCount() + 1; }
JSAtom* getSource() const { return source; }
RegExpFlag getFlags() const { return flags; }
bool ignoreCase() const { return flags & IgnoreCaseFlag; }
bool global() const { return flags & GlobalFlag; }
bool multiline() const { return flags & MultilineFlag; }
bool sticky() const { return flags & StickyFlag; }
bool unicode() const { return flags & UnicodeFlag; }
bool isCompiled(CompilationMode mode, bool latin1,
ForceByteCodeEnum force = DontForceByteCode) const {
return compilation(mode, latin1).compiled(force);
}
bool isCompiled() const {
return isCompiled(Normal, true) || isCompiled(Normal, false) ||
isCompiled(MatchOnly, true) || isCompiled(MatchOnly, false);
}
void traceChildren(JSTracer* trc);
void discardJitCode();
void finalize(FreeOp* fop);
static size_t offsetOfSource() { return offsetof(RegExpShared, source); }
static size_t offsetOfFlags() { return offsetof(RegExpShared, flags); }
static size_t offsetOfParenCount() {
return offsetof(RegExpShared, parenCount);
}
static size_t offsetOfLatin1JitCode(CompilationMode mode) {
return offsetof(RegExpShared, compilationArray) +
(CompilationIndex(mode, true) * sizeof(RegExpCompilation)) +
offsetof(RegExpCompilation, jitCode);
}
static size_t offsetOfTwoByteJitCode(CompilationMode mode) {
return offsetof(RegExpShared, compilationArray) +
(CompilationIndex(mode, false) * sizeof(RegExpCompilation)) +
offsetof(RegExpCompilation, jitCode);
}
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
#ifdef DEBUG
static bool dumpBytecode(JSContext* cx, MutableHandleRegExpShared res,
bool match_only, HandleLinearString input);
#endif
};
class RegExpZone {
struct Key {
JSAtom* atom;
uint16_t flag;
Key() : atom(nullptr), flag(0) {}
Key(JSAtom* atom, RegExpFlag flag) : atom(atom), flag(flag) {}
MOZ_IMPLICIT Key(const ReadBarriered<RegExpShared*>& shared)
: atom(shared.unbarrieredGet()->getSource()),
flag(shared.unbarrieredGet()->getFlags()) {}
typedef Key Lookup;
static HashNumber hash(const Lookup& l) {
HashNumber hash = DefaultHasher<JSAtom*>::hash(l.atom);
return mozilla::AddToHash(hash, l.flag);
}
static bool match(Key l, Key r) {
return l.atom == r.atom && l.flag == r.flag;
}
};
using Set = JS::WeakCache<
JS::GCHashSet<ReadBarriered<RegExpShared*>, Key, ZoneAllocPolicy>>;
Set set_;
public:
explicit RegExpZone(Zone* zone);
~RegExpZone() { MOZ_ASSERT(set_.empty()); }
bool empty() const { return set_.empty(); }
RegExpShared* maybeGet(JSAtom* source, RegExpFlag flags) const {
Set::Ptr p = set_.lookup(Key(source, flags));
return p ? *p : nullptr;
}
RegExpShared* get(JSContext* cx, HandleAtom source, RegExpFlag flags);
RegExpShared* get(JSContext* cx, HandleAtom source, JSString* maybeOpt);
#ifdef DEBUG
void clear() { set_.clear(); }
#endif
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
};
class RegExpRealm {
ReadBarriered<ArrayObject*> matchResultTemplateObject_;
ReadBarriered<Shape*> optimizableRegExpPrototypeShape_;
ReadBarriered<Shape*> optimizableRegExpInstanceShape_;
ArrayObject* createMatchResultTemplateObject(JSContext* cx);
public:
explicit RegExpRealm();
void sweep();
static const size_t MatchResultObjectIndexSlot = 0;
static const size_t MatchResultObjectInputSlot = 1;
ArrayObject* getOrCreateMatchResultTemplateObject(JSContext* cx) {
if (matchResultTemplateObject_) {
return matchResultTemplateObject_;
}
return createMatchResultTemplateObject(cx);
}
Shape* getOptimizableRegExpPrototypeShape() {
return optimizableRegExpPrototypeShape_;
}
void setOptimizableRegExpPrototypeShape(Shape* shape) {
optimizableRegExpPrototypeShape_ = shape;
}
Shape* getOptimizableRegExpInstanceShape() {
return optimizableRegExpInstanceShape_;
}
void setOptimizableRegExpInstanceShape(Shape* shape) {
optimizableRegExpInstanceShape_ = shape;
}
static size_t offsetOfOptimizableRegExpPrototypeShape() {
return offsetof(RegExpRealm, optimizableRegExpPrototypeShape_);
}
static size_t offsetOfOptimizableRegExpInstanceShape() {
return offsetof(RegExpRealm, optimizableRegExpInstanceShape_);
}
};
}
namespace JS {
namespace ubi {
template <>
class Concrete<js::RegExpShared> : TracerConcrete<js::RegExpShared> {
protected:
explicit Concrete(js::RegExpShared* ptr)
: TracerConcrete<js::RegExpShared>(ptr) {}
public:
static void construct(void* storage, js::RegExpShared* ptr) {
new (storage) Concrete(ptr);
}
CoarseType coarseType() const final { return CoarseType::Other; }
Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
const char16_t* typeName() const override { return concreteTypeName; }
static const char16_t concreteTypeName[];
};
} }
#endif