Issue #1677 - Part 4: Implement shim for regexp

This commit is contained in:
Matt A. Tobin 2020-11-09 20:26:59 -05:00 committed by roytam1
commit 12c7f5df33
6 changed files with 2445 additions and 222 deletions

View file

@ -23,9 +23,14 @@ SOURCES += [
'regexp-macro-assembler-tracer.cc',
'regexp-macro-assembler.cc',
'regexp-parser.cc',
'regexp-shim.cc',
'regexp-stack.cc',
'util/unicode.cc'
]
if CONFIG['ENABLE_INTL_API']:
CXXFLAGS += ['-DV8_INTL_SUPPORT']
SOURCES += ['property-sequences.cc']
SOURCES += [
'property-sequences.cc',
'special-case.cc'
]

View file

@ -8,6 +8,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <iostream>
#include "regexp/regexp-shim.h"
namespace v8 {
@ -27,17 +29,24 @@ void PrintF(FILE* out, const char* format, ...) {
va_end(arguments);
}
StdoutStream::operator std::ostream&() const { return std::cerr; }
template <typename T>
std::ostream& StdoutStream::operator<<(T t) { return std::cerr << t; }
template std::ostream& StdoutStream::operator<<(char const* c);
// Origin:
// https://github.com/v8/v8/blob/855591a54d160303349a5f0a32fab15825c708d1/src/utils/ostreams.cc#L120-L169
// (This is a hand-simplified version.)
// Writes the given character to the output escaping everything outside
// of printable ASCII range.
std::ostream& operator<<(std::ostream& os, const AsUC16& c) {
uint16_t v = c.value;
uc16 v = c.value;
bool isPrint = 0x20 < v && v <= 0x7e;
char buf[10];
const char* format = isPrint ? "%c" : (v <= 0xFF) ? "\\x%02x" : "\\u%04x";
snprintf(buf, sizeof(buf), format, v);
SprintfLiteral(buf, format, v);
return os << buf;
}
std::ostream& operator<<(std::ostream& os, const AsUC32& c) {
@ -46,12 +55,136 @@ std::ostream& operator<<(std::ostream& os, const AsUC32& c) {
return os << AsUC16(v);
}
char buf[13];
snprintf(buf, sizeof(buf), "\\u{%06x}", v);
SprintfLiteral(buf, "\\u{%06x}", v);
return os << buf;
}
DisallowJavascriptExecution::DisallowJavascriptExecution(Isolate* isolate)
: nojs_(isolate->cx()) {}
HandleScope::HandleScope(Isolate* isolate)
: isolate_(isolate) {
isolate->openHandleScope(*this);
}
HandleScope::~HandleScope() {
isolate_->closeHandleScope(level_, non_gc_level_);
}
template <typename T>
Handle<T>::Handle(T object, Isolate* isolate)
: location_(isolate->getHandleLocation(JS::Value(object))) {}
template Handle<ByteArray>::Handle(ByteArray b, Isolate* isolate);
template Handle<String>::Handle(String s, Isolate* isolate);
template <typename T>
Handle<T>::Handle(JS::Value value, Isolate* isolate)
: location_(isolate->getHandleLocation(value)) {
T::cast(Object(value)); // Assert that value has the correct type.
}
JS::Value* Isolate::getHandleLocation(JS::Value value) {
js::AutoEnterOOMUnsafeRegion oomUnsafe;
if (!handleArena_.Append(value)) {
oomUnsafe.crash("Irregexp handle allocation");
}
return &handleArena_.GetLast();
}
void* Isolate::allocatePseudoHandle(size_t bytes) {
PseudoHandle<void> ptr;
ptr.reset(js_malloc(bytes));
if (!ptr) {
return nullptr;
}
if (!uniquePtrArena_.Append(std::move(ptr))) {
return nullptr;
}
return uniquePtrArena_.GetLast().get();
}
template <typename T>
PseudoHandle<T> Isolate::takeOwnership(void* ptr) {
for (auto iter = uniquePtrArena_.IterFromLast(); !iter.Done(); iter.Prev()) {
auto& entry = iter.Get();
if (entry.get() == ptr) {
PseudoHandle<T> result;
result.reset(static_cast<T*>(entry.release()));
return result;
}
}
MOZ_CRASH("Tried to take ownership of pseudohandle that is not in the arena");
}
PseudoHandle<ByteArrayData> ByteArray::takeOwnership(Isolate* isolate) {
PseudoHandle<ByteArrayData> result =
isolate->takeOwnership<ByteArrayData>(value_.toPrivate());
value_ = JS::PrivateValue(nullptr);
return result;
}
void Isolate::trace(JSTracer* trc) {
js::gc::AssertRootMarkingPhase(trc);
for (auto iter = handleArena_.Iter(); !iter.Done(); iter.Next()) {
auto& elem = iter.Get();
JS::GCPolicy<JS::Value>::trace(trc, &elem, "Isolate handle arena");
}
}
/*static*/ Handle<String> String::Flatten(Isolate* isolate,
Handle<String> string) {
if (string->IsFlat()) {
return string;
}
js::AutoEnterOOMUnsafeRegion oomUnsafe;
JSLinearString* linear = string->str()->ensureLinear(isolate->cx());
if (!linear) {
oomUnsafe.crash("Irregexp String::Flatten");
}
return Handle<String>(JS::StringValue(linear), isolate);
}
// This is only used for trace messages printing the source of a
// regular expression. To keep things simple, we just return an
// empty string and don't print anything.
std::unique_ptr<char[]> String::ToCString() {
return std::unique_ptr<char[]>();
}
Handle<ByteArray> Isolate::NewByteArray(int length, AllocationType alloc) {
MOZ_RELEASE_ASSERT(length >= 0);
js::AutoEnterOOMUnsafeRegion oomUnsafe;
size_t alloc_size = sizeof(uint32_t) + length;
ByteArrayData* data =
static_cast<ByteArrayData*>(allocatePseudoHandle(alloc_size));
if (!data) {
oomUnsafe.crash("Irregexp NewByteArray");
}
data->length = length;
return Handle<ByteArray>(JS::PrivateValue(data), this);
}
Handle<FixedArray> Isolate::NewFixedArray(int length) {
MOZ_RELEASE_ASSERT(length >= 0);
MOZ_CRASH("TODO");
}
template <typename CharT>
Handle<String> Isolate::InternalizeString(const Vector<const CharT>& str) {
js::AutoEnterOOMUnsafeRegion oomUnsafe;
JSAtom* atom = js::AtomizeChars(cx(), str.begin(), str.length());
if (!atom) {
oomUnsafe.crash("Irregexp InternalizeString");
}
return Handle<String>(JS::StringValue(atom), this);
}
template Handle<String>
Isolate::InternalizeString(const Vector<const uint8_t>& str);
template Handle<String>
Isolate::InternalizeString(const Vector<const char16_t>& str);
// TODO: Map flags to jitoptions
bool FLAG_correctness_fuzzer_suppressions = false;
@ -59,8 +192,8 @@ bool FLAG_enable_regexp_unaligned_accesses = false;
bool FLAG_harmony_regexp_sequence = false;
bool FLAG_regexp_interpret_all = false;
bool FLAG_regexp_mode_modifiers = false;
bool FLAG_regexp_optimization = false;
bool FLAG_regexp_peephole_optimization = false;
bool FLAG_regexp_optimization = true;
bool FLAG_regexp_peephole_optimization = true;
bool FLAG_regexp_possessive_quantifier = false;
bool FLAG_regexp_tier_up = false;
bool FLAG_trace_regexp_assembler = false;

View file

@ -15,9 +15,11 @@
#include "mozilla/Attributes.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/Maybe.h"
#include "mozilla/SegmentedVector.h"
#include "mozilla/Types.h"
#include <algorithm>
#include <cctype>
#include "jit/Label.h"
#include "js/Value.h"
@ -127,6 +129,12 @@ using byte = uint8_t;
using Address = uintptr_t;
static const Address kNullAddress = 0;
// Latin1/UTF-16 constants
// Code-point values in Unicode 4.0 are 21 bits wide.
// Code units in UTF-16 are 16 bits wide.
using uc16 = char16_t;
using uc32 = int32_t;
namespace base {
// Origin:
@ -161,12 +169,36 @@ inline uint8_t saturated_cast<uint8_t, int>(int x) {
return (x >= 0) ? ((x < 255) ? uint8_t(x) : 255) : 0;
}
#define LAZY_INSTANCE_INITIALIZER { mozilla::Nothing() }
template <typename T>
struct LazyInstanceImpl {
mozilla::Maybe<T> value_;
T* Pointer() {
if (value_.isNothing()) {
value_.emplace();
}
return value_.ptr();
}
};
template <typename T>
class LazyInstance {
public:
using type = LazyInstanceImpl<T>;
};
namespace bits {
inline uint64_t CountTrailingZeros(uint64_t value) {
return mozilla::CountTrailingZeroes64(value);
}
inline size_t RoundUpToPowerOfTwo32(size_t value) {
return mozilla::RoundUpPow2(value);
}
} // namespace bits
} // namespace base
@ -178,10 +210,10 @@ using uchar = unsigned int;
// https://github.com/v8/v8/blob/1f1e4cdb04c75eab77adbecd5f5514ddc3eb56cf/src/strings/unicode.h#L133-L150
class Latin1 {
public:
static const uint16_t kMaxChar = 0xff;
static const uc16 kMaxChar = 0xff;
// Convert the character to Latin-1 case equivalent if possible.
static inline uint16_t TryConvertToLatin1(uint16_t c) {
static inline uc16 TryConvertToLatin1(uc16 c) {
// "GREEK CAPITAL LETTER MU" case maps to "MICRO SIGN".
// "GREEK SMALL LETTER MU" case maps to "MICRO SIGN".
if (c == 0x039C || c == 0x03BC) {
@ -206,10 +238,10 @@ class Utf16 {
static inline bool IsTrailSurrogate(int code) {
return js::unicode::IsTrailSurrogate(code);
}
static inline uint16_t LeadSurrogate(uint32_t char_code) {
static inline uc16 LeadSurrogate(uint32_t char_code) {
return js::unicode::LeadSurrogate(char_code);
}
static inline uint16_t TrailSurrogate(uint32_t char_code) {
static inline uc16 TrailSurrogate(uint32_t char_code) {
return js::unicode::TrailSurrogate(char_code);
}
static inline uint32_t CombineSurrogatePair(char16_t lead, char16_t trail) {
@ -218,6 +250,8 @@ class Utf16 {
static const uchar kMaxNonSurrogateCharCode = 0xffff;
};
#ifndef V8_INTL_SUPPORT
// A cache used in case conversion. It caches the value for characters
// that either have no mapping or map to a single character independent
// of context. Characters that map to more than one character or that
@ -228,11 +262,37 @@ template <class T, int size = 256>
class Mapping {
public:
inline Mapping() = default;
int get(uchar c, uchar n, uchar* result);
inline int get(uchar c, uchar n, uchar* result) {
CacheEntry entry = entries_[c & kMask];
if (entry.code_point_ == c) {
if (entry.offset_ == 0) {
return 0;
} else {
result[0] = c + entry.offset_;
return 1;
}
} else {
return CalculateValue(c, n, result);
}
}
private:
friend class Test;
int CalculateValue(uchar c, uchar n, uchar* result);
int CalculateValue(uchar c, uchar n, uchar* result) {
bool allow_caching = true;
int length = T::Convert(c, n, result, &allow_caching);
if (allow_caching) {
if (length == 1) {
entries_[c & kMask] = CacheEntry(c, result[0] - c);
return 1;
} else {
entries_[c & kMask] = CacheEntry(c, 0);
return 0;
}
} else {
return length;
}
}
struct CacheEntry {
inline CacheEntry() : code_point_(kNoChar), offset_(0) {}
inline CacheEntry(uchar code_point, signed offset)
@ -253,7 +313,7 @@ struct Ecma262Canonicalize {
static int Convert(uchar c, uchar n, uchar* result, bool* allow_caching_ptr);
};
struct Ecma262UnCanonicalize {
static const int kMaxWidth = 1;
static const int kMaxWidth = 4;
static int Convert(uchar c, uchar n, uchar* result, bool* allow_caching_ptr);
};
struct CanonicalizationRange {
@ -261,6 +321,12 @@ struct CanonicalizationRange {
static int Convert(uchar c, uchar n, uchar* result, bool* allow_caching_ptr);
};
#endif // !V8_INTL_SUPPORT
struct Letter {
static bool Is(uchar c);
};
} // namespace unibrow
namespace internal {
@ -306,12 +372,6 @@ constexpr int kSystemPointerSize = sizeof(void*);
// representable as a Number value. ES6 section 20.1.2.6
constexpr double kMaxSafeInteger = 9007199254740991.0; // 2^53-1
// Latin1/UTF-16 constants
// Code-point values in Unicode 4.0 are 21 bits wide.
// Code units in UTF-16 are 16 bits wide.
using uc16 = uint16_t;
using uc32 = int32_t;
constexpr int kBitsPerByte = 8;
constexpr int kBitsPerByteLog2 = 3;
constexpr int kUInt32Size = sizeof(uint32_t);
@ -328,10 +388,10 @@ inline bool IsIdentifierPart(uc32 c) {
return js::unicode::IsIdentifierPart(uint32_t(c));
}
// Wrappers to disambiguate uint16_t and uc16.
// Wrappers to disambiguate char16_t and uc16.
struct AsUC16 {
explicit AsUC16(uint16_t v) : value(v) {}
uint16_t value;
explicit AsUC16(char16_t v) : value(v) {}
char16_t value;
};
struct AsUC32 {
@ -339,11 +399,21 @@ struct AsUC32 {
int32_t value;
};
class StdoutStream : public std::ostream {};
std::ostream& operator<<(std::ostream& os, const AsUC16& c);
std::ostream& operator<<(std::ostream& os, const AsUC32& c);
// This class is used for the output of trace-regexp-parser. V8 has
// an elaborate implementation to ensure that the output gets to the
// right place, even on Android. We just need something that will
// print output (ideally to stderr, to match the rest of our tracing
// code). This is an empty wrapper that will convert itself to
// std::cerr when used.
class StdoutStream {
public:
operator std::ostream&() const;
template <typename T> std::ostream& operator<<(T t);
};
// Reuse existing Maybe implementation
using mozilla::Maybe;
@ -357,6 +427,10 @@ mozilla::Nothing Nothing() {
return mozilla::Nothing();
}
template <typename T>
using PseudoHandle = mozilla::UniquePtr<T, JS::FreePolicy>;
// Origin:
// https://github.com/v8/v8/blob/855591a54d160303349a5f0a32fab15825c708d1/src/utils/utils.h#L600-L642
// Compare 8bit/16bit chars to 8bit/16bit chars.
@ -388,16 +462,16 @@ inline int CompareChars(const lchar* lhs, const rchar* rhs, size_t chars) {
reinterpret_cast<const uint8_t*>(rhs), chars);
} else {
return CompareCharsUnsigned(reinterpret_cast<const uint8_t*>(lhs),
reinterpret_cast<const uint16_t*>(rhs),
reinterpret_cast<const char16_t*>(rhs),
chars);
}
} else {
if (sizeof(rchar) == 1) {
return CompareCharsUnsigned(reinterpret_cast<const uint16_t*>(lhs),
return CompareCharsUnsigned(reinterpret_cast<const char16_t*>(lhs),
reinterpret_cast<const uint8_t*>(rhs), chars);
} else {
return CompareCharsUnsigned(reinterpret_cast<const uint16_t*>(lhs),
reinterpret_cast<const uint16_t*>(rhs),
return CompareCharsUnsigned(reinterpret_cast<const char16_t*>(lhs),
reinterpret_cast<const char16_t*>(rhs),
chars);
}
}
@ -434,14 +508,6 @@ class Object {
// idiom: return false iff we are throwing an exception.
inline bool IsException(Isolate*) const { return !value_.toBoolean(); }
// SpiderMonkey tries to avoid leaking the internal representation of its
// objects. V8 is not so strict. These functions are used when calling /
// being called by native code: objects are converted to Addresses for the
// call, then cast back to objects on the other side.
// We might be able to upstream a patch that eliminates the need for these.
Object(Address bits);
Address ptr() const;
protected:
JS::Value value_;
};
@ -460,77 +526,162 @@ class Smi : public Object {
// V8::HeapObject ~= JSObject
class HeapObject : public Object {
public:
// Only used for bookkeeping of total code generated in regexp-compiler.
// We may be able to refactor this away.
int Size() const;
};
// A fixed-size array with Objects (aka Values) as element types
// Implemented as a wrapper around a regular native object with dense elements.
class FixedArray : public HeapObject {
public:
inline void set(uint32_t index, Object value) {
JS::Value(*this).toObject().as<js::NativeObject>().setDenseElement(index,
value);
inline static HeapObject cast(Object object) {
HeapObject h;
h.value_ = JS::Value(object);
return h;
}
};
// A fixed-size array of bytes.
// TODO: figure out the best implementation for this. Uint8Array might work,
// but it's not currently visible outside of TypedArrayObject.cpp.
class ByteArray : public HeapObject {
// A fixed-size array with Objects (aka Values) as element types
// Only used for named captures. Allocated during parsing, so
// can't be a GC thing.
// TODO: implement.
class FixedArray : public HeapObject {
public:
uint8_t get(uint32_t index);
void set(uint32_t index, uint8_t val);
uint32_t length();
byte* GetDataStartAddress();
byte* GetDataEndAddress();
inline void set(uint32_t index, Object value) {}
inline static FixedArray cast(Object object) { MOZ_CRASH("TODO"); }
};
static ByteArray cast(Object object);
class ByteArrayData {
public:
uint32_t length;
uint8_t* data();
};
/*
* Conceptually, ByteArrayData is a variable-size structure. To
* implement this in a C++-approved way, we allocate a struct
* containing the 32-bit length field, followed by additional memory
* for the data. To access the data, we get a pointer to the next byte
* after the length field and cast it to the correct type.
*/
inline uint8_t* ByteArrayData::data() {
static_assert(alignof(uint8_t) <= alignof(ByteArrayData),
"The trailing data must be aligned to start immediately "
"after the header with no padding.");
ByteArrayData* immediatelyAfter = this + 1;
return reinterpret_cast<uint8_t*>(immediatelyAfter);
}
// A fixed-size array of bytes.
class ByteArray : public HeapObject {
ByteArrayData* inner() const {
return static_cast<ByteArrayData*>(value_.toPrivate());
}
PseudoHandle<ByteArrayData> takeOwnership(Isolate* isolate);
friend class SMRegExpMacroAssembler;
public:
byte get(uint32_t index) {
MOZ_ASSERT(index < length());
return inner()->data()[index];
}
void set(uint32_t index, byte val) {
MOZ_ASSERT(index < length());
inner()->data()[index] = val;
}
uint32_t length() const { return inner()->length; }
byte* GetDataStartAddress() { return inner()->data(); }
static ByteArray cast(Object object) {
ByteArray b;
b.value_ = JS::Value(object);
return b;
}
};
// Like Handles in SM, V8 handles are references to marked pointers.
// Unlike SM, where Rooted pointers are created individually on the
// stack, the target of a V8 handle lives in a HandleScope.
// HandleScopes are created on the stack and register themselves with
// the isolate (~= JSContext). Whenever a Handle is created, the
// outermost HandleScope is retrieved from the isolate, and a new root
// is created in that HandleScope. The Handle remains valid for the
// lifetime of the HandleScope.
class HandleScope {
public:
// stack, the target of a V8 handle lives in an arena on the isolate
// (~= JSContext). Whenever a Handle is created, a new "root" is
// created at the end of the arena.
//
// HandleScopes are used to manage the lifetimes of these handles. A
// HandleScope lives on the stack and stores the size of the arena at
// the time of its creation. When the function returns and the
// HandleScope is destroyed, the arena is truncated to its previous
// size, clearing all roots that were created since the creation of
// the HandleScope.
//
// In some cases, objects that are GC-allocated in V8 are not in SM.
// In particular, irregexp allocates ByteArrays during code generation
// to store lookup tables. This does not play nicely with the SM
// macroassembler's requirement that no GC allocations take place
// while it is on the stack. To work around this, this shim layer also
// provides the ability to create pseudo-handles, which are not
// managed by the GC but provide the same API to irregexp. The "root"
// of a pseudohandle is a unique pointer living in a second arena. If
// the allocated object should outlive the HandleScope, it must be
// manually moved out of the arena using takeOwnership.
class MOZ_STACK_CLASS HandleScope {
public:
HandleScope(Isolate* isolate);
~HandleScope();
private:
size_t level_;
size_t non_gc_level_;
Isolate* isolate_;
friend class Isolate;
};
// Origin:
// https://github.com/v8/v8/blob/5792f3587116503fc047d2f68c951c72dced08a5/src/handles/handles.h#L88-L171
template <typename T>
class Handle {
class MOZ_NONHEAP_CLASS Handle {
public:
Handle();
Handle() : location_(nullptr) {}
Handle(T object, Isolate* isolate);
Handle(JS::Value value, Isolate* isolate);
// Constructor for handling automatic up casting.
template <typename S, typename = typename std::enable_if<
std::is_convertible<S*, T*>::value>::type>
/*inline*/ Handle(Handle<S> handle);
inline Handle(Handle<S> handle) : location_(handle.location_) {}
template <typename S>
/*inline*/ static const Handle<T> cast(Handle<S> that);
inline static const Handle<T> cast(Handle<S> that) {
return Handle<T>(that.location_);
}
T* operator->() const;
T operator*() const;
inline bool is_null() const { return location_ == nullptr; }
bool is_null() const;
inline T operator*() const {
return T::cast(Object(*location_));
};
Address address();
// {ObjectRef} is returned by {Handle::operator->}. It should never be stored
// anywhere or used in any other code; no one should ever have to spell out
// {ObjectRef} in code. Its only purpose is to be dereferenced immediately by
// "operator-> chaining". Returning the address of the field is valid because
// this object's lifetime only ends at the end of the full statement.
// Origin:
// https://github.com/v8/v8/blob/03aaa4b3bf4cb01eee1f223b252e6869b04ab08c/src/handles/handles.h#L91-L105
class MOZ_TEMPORARY_CLASS ObjectRef {
public:
T* operator->() { return &object_; }
private:
friend class Handle;
explicit ObjectRef(T object) : object_(object) {}
T object_;
};
inline ObjectRef operator->() const { return ObjectRef{**this}; }
private:
Handle(JS::Value* location) : location_(location) {}
template <typename>
friend class Handle;
template <typename>
friend class MaybeHandle;
JS::Value* location_;
};
// A Handle can be converted into a MaybeHandle. Converting a MaybeHandle
@ -543,21 +694,35 @@ class Handle {
// Origin:
// https://github.com/v8/v8/blob/5792f3587116503fc047d2f68c951c72dced08a5/src/handles/maybe-handles.h#L15-L78
template <typename T>
class MaybeHandle final {
class MOZ_NONHEAP_CLASS MaybeHandle final {
public:
MaybeHandle() = default;
MaybeHandle() : location_(nullptr) {}
// Constructor for handling automatic up casting from Handle.
// Ex. Handle<JSArray> can be passed when MaybeHandle<Object> is expected.
template <typename S, typename = typename std::enable_if<
std::is_convertible<S*, T*>::value>::type>
MaybeHandle(Handle<S> handle);
MaybeHandle(Handle<S> handle) : location_(handle.location_) {}
/*inline*/ Handle<T> ToHandleChecked() const;
inline Handle<T> ToHandleChecked() const {
MOZ_RELEASE_ASSERT(location_);
return Handle<T>(location_);
}
// Convert to a Handle with a type that can be upcasted to.
template <typename S>
/*inline*/ bool ToHandle(Handle<S>* out) const;
inline bool ToHandle(Handle<S>* out) const {
if (location_) {
*out = Handle<T>(location_);
return true;
} else {
*out = Handle<T>();
return false;
}
}
private:
JS::Value* location_;
};
// From v8/src/handles/handles-inl.h
@ -590,14 +755,6 @@ class AllowHeapAllocation {
AllowHeapAllocation() {}
};
class DisallowJavascriptExecution {
public:
DisallowJavascriptExecution(Isolate* isolate);
private:
js::AutoAssertNoContentJS nojs_;
};
// Origin:
// https://github.com/v8/v8/blob/84f3877c15bc7f8956d21614da4311337525a3c8/src/objects/string.h#L83-L474
class String : public HeapObject {
@ -605,6 +762,9 @@ class String : public HeapObject {
JSString* str() const { return value_.toString(); }
public:
String() : HeapObject() {}
String(JSString* str) { value_ = JS::StringValue(str); }
operator JSString*() const { return str(); }
// Max char codes.
@ -614,21 +774,8 @@ class String : public HeapObject {
static const uc32 kMaxCodePoint = 0x10ffff;
MOZ_ALWAYS_INLINE int length() const { return str()->length(); }
uint16_t Get(uint32_t index);
bool IsFlat() { return str()->isLinear(); };
// These are only used in V8 functions that I want to rewrite.
// TODO: Rewrite those functions and delete this
bool IsConsString();
bool IsExternalString();
bool IsExternalOneByteString();
bool IsExternalTwoByteString();
bool IsSeqString();
bool IsSeqOneByteString();
bool IsSeqTwoByteString();
bool IsSlicedString();
bool IsThinString();
// Origin:
// https://github.com/v8/v8/blob/84f3877c15bc7f8956d21614da4311337525a3c8/src/objects/string.h#L95-L152
class FlatContent {
@ -643,14 +790,11 @@ class String : public HeapObject {
return Vector<const uint8_t>(string_->latin1Chars(no_gc_),
string_->length());
}
Vector<const uc16> ToUC16Vector() const;
// TODO: twoByteChars returns char16_t*, but uc16 is uint16_t, which is
// not compatible :( :( :(
// {
// MOZ_ASSERT(IsTwoByte());
// return Vector<const uc16>(string_->twoByteChars(no_gc_),
// string_->length());
// }
Vector<const uc16> ToUC16Vector() const {
MOZ_ASSERT(IsTwoByte());
return Vector<const uc16>(string_->twoByteChars(no_gc_),
string_->length());
}
private:
const JSLinearString* string_;
const JS::AutoAssertNoGC& no_gc_;
@ -681,6 +825,22 @@ class String : public HeapObject {
Vector<const Char> GetCharVector(const DisallowHeapAllocation& no_gc);
};
template <>
inline Vector<const uint8_t> String::GetCharVector(
const DisallowHeapAllocation& no_gc) {
String::FlatContent flat = GetFlatContent(no_gc);
MOZ_ASSERT(flat.IsOneByte());
return flat.ToOneByteVector();
}
template <>
inline Vector<const uc16> String::GetCharVector(
const DisallowHeapAllocation& no_gc) {
String::FlatContent flat = GetFlatContent(no_gc);
MOZ_ASSERT(flat.IsTwoByte());
return flat.ToUC16Vector();
}
// A flat string reader provides random access to the contents of a
// string independent of the character width of the string. The handle
// must be valid as long as the reader is being used.
@ -688,84 +848,66 @@ class String : public HeapObject {
// https://github.com/v8/v8/blob/84f3877c15bc7f8956d21614da4311337525a3c8/src/objects/string.h#L807-L825
class MOZ_STACK_CLASS FlatStringReader {
public:
FlatStringReader(JSAtom* string) : string_(string) {}
int length() { return string_->length(); }
FlatStringReader(JSLinearString* string)
: length_(string->length()),
is_latin1_(string->hasLatin1Chars()) {
if (is_latin1_) {
latin1_chars_ = string->latin1Chars(nogc_);
} else {
two_byte_chars_ = string->twoByteChars(nogc_);
}
}
FlatStringReader(const char16_t* chars, size_t length)
: two_byte_chars_(chars),
length_(length),
is_latin1_(false) {}
int length() { return length_; }
inline char16_t Get(size_t index) {
return string_->latin1OrTwoByteChar(index);
MOZ_ASSERT(index < length_);
if (is_latin1_) {
return latin1_chars_[index];
} else {
return two_byte_chars_[index];
}
}
private:
JSAtom* string_;
JS::AutoCheckCannotGC nogc;
union {
const JS::Latin1Char *latin1_chars_;
const char16_t* two_byte_chars_;
};
size_t length_;
bool is_latin1_;
JS::AutoCheckCannotGC nogc_;
};
//////////////////////////////////////////////////
// TODO: Refactor NativeRegExpMacroAssembler and delete all of these:
class ConsString : public String {
public:
String first();
String second();
static ConsString cast(Object object);
};
class ExternalOneByteString : public String {
public:
const uint8_t* GetChars();
static ExternalOneByteString cast(Object object);
};
class ExternalTwoByteString : public String {
public:
const uc16* GetChars();
static ExternalTwoByteString cast(Object object);
};
class SeqOneByteString : public String {
public:
uint8_t* GetChars(const DisallowHeapAllocation& no_gc);
static SeqOneByteString cast(Object object);
};
class SeqTwoByteString : public String {
public:
uc16* GetChars(const DisallowHeapAllocation& no_gc);
static SeqTwoByteString cast(Object object);
static constexpr size_t kMaxCharsSize = JSString::MAX_LENGTH * 2;
};
class SlicedString : public String {
public:
String parent();
int offset();
static SlicedString cast(Object object);
};
class ThinString : public String {
public:
String actual();
static ThinString cast(Object object);
};
class StringShape {
public:
explicit StringShape(const String s);
bool IsCons();
bool IsSliced();
bool IsThin();
};
// End of "TODO: Delete all of these"
//////////////////////////////////////////////////
class JSRegExp : public HeapObject {
public:
// ******************************************************
// Methods that are called from inside the implementation
// ******************************************************
void TierUpTick();
bool MarkedForTierUp() const;
void TierUpTick() { /*inner()->tierUpTick();*/ }
bool MarkedForTierUp() const {
return false; /*inner()->markedForTierUp();*/
}
Object Code(bool is_latin1) const;
Object Bytecode(bool is_latin1) const;
// TODO: hook these up
Object Code(bool is_latin1) const { return Object(JS::UndefinedValue()); }
Object Bytecode(bool is_latin1) const { return Object(JS::UndefinedValue()); }
uint32_t BacktrackLimit() const;
uint32_t BacktrackLimit() const {
return 0; /*inner()->backtrackLimit();*/
}
static JSRegExp cast(Object object);
static JSRegExp cast(Object object) {
JSRegExp regexp;
MOZ_ASSERT(JS::Value(object).toGCThing()->is<js::RegExpShared>());
regexp.value_ = JS::PrivateGCThingValue(JS::Value(object).toGCThing());
return regexp;
}
// ******************************
// Static constants
@ -807,6 +949,11 @@ class JSRegExp : public HeapObject {
static constexpr int kFlagCount = 6;
static constexpr int kNoBacktrackLimit = 0;
private:
js::RegExpShared* inner() {
return value_.toGCThing()->as<js::RegExpShared>();
}
};
class Histogram {
@ -841,11 +988,26 @@ class Isolate {
bool has_pending_exception() { return cx()->isExceptionPending(); }
void StackOverflow() { js::ReportOverRecursed(cx()); }
unibrow::Mapping<unibrow::Ecma262UnCanonicalize>* jsregexp_uncanonicalize();
#ifndef V8_INTL_SUPPORT
unibrow::Mapping<unibrow::Ecma262UnCanonicalize>* jsregexp_uncanonicalize() {
return &jsregexp_uncanonicalize_;
}
unibrow::Mapping<unibrow::Ecma262Canonicalize>*
regexp_macro_assembler_canonicalize();
unibrow::Mapping<unibrow::CanonicalizationRange>* jsregexp_canonrange();
regexp_macro_assembler_canonicalize() {
return &regexp_macro_assembler_canonicalize_;
}
unibrow::Mapping<unibrow::CanonicalizationRange>* jsregexp_canonrange() {
return &jsregexp_canonrange_;
}
private:
unibrow::Mapping<unibrow::Ecma262UnCanonicalize> jsregexp_uncanonicalize_;
unibrow::Mapping<unibrow::Ecma262Canonicalize>
regexp_macro_assembler_canonicalize_;
unibrow::Mapping<unibrow::CanonicalizationRange> jsregexp_canonrange_;
#endif // !V8_INTL_SUPPORT
public:
// An empty stub for telemetry we don't support
void IncreaseTotalRegexpCodeGenerated(int size) {}
@ -856,13 +1018,9 @@ class Isolate {
Handle<ByteArray> NewByteArray(
int length, AllocationType allocation = AllocationType::kYoung);
MOZ_MUST_USE MaybeHandle<String> NewStringFromOneByte(
const Vector<const uint8_t>& str,
AllocationType allocation = AllocationType::kYoung);
// Allocates a fixed array initialized with undefined values.
Handle<FixedArray> NewFixedArray(
int length, AllocationType allocation = AllocationType::kYoung);
Handle<FixedArray> NewFixedArray(int length);
template <typename Char>
Handle<String> InternalizeString(const Vector<const Char>& str);
@ -875,7 +1033,37 @@ class Isolate {
JSContext* cx() const { return cx_; }
void trace(JSTracer* trc);
//********** Handle code **********//
JS::Value* getHandleLocation(JS::Value value);
private:
mozilla::SegmentedVector<JS::Value> handleArena_;
mozilla::SegmentedVector<PseudoHandle<void>> uniquePtrArena_;
void* allocatePseudoHandle(size_t bytes);
public:
template <typename T>
PseudoHandle<T> takeOwnership(void* ptr);
private:
void openHandleScope(HandleScope& scope) {
scope.level_ = handleArena_.Length();
scope.non_gc_level_ = uniquePtrArena_.Length();
}
void closeHandleScope(size_t prevLevel, size_t prevUniqueLevel) {
size_t currLevel = handleArena_.Length();
handleArena_.PopLastN(currLevel - prevLevel);
size_t currUniqueLevel = uniquePtrArena_.Length();
uniquePtrArena_.PopLastN(currUniqueLevel - prevUniqueLevel);
}
friend class HandleScope;
JSContext* cx_;
RegExpStack* regexp_stack_;
Counters counters_;
@ -902,47 +1090,20 @@ class StackLimitCheck {
JSContext* cx_;
};
class Code {
class Code : public HeapObject {
public:
bool operator!=(Code& other) const;
uint8_t* raw_instruction_start() { return inner()->raw(); }
Address raw_instruction_start();
Address raw_instruction_end();
Address address();
static Code cast(Object object);
};
// GeneratedCode provides an interface for calling into jit code.
// It will probably require additional work to hook this up to the
// arm simulator.
// Origin:
// https://github.com/v8/v8/blob/abfbe7687edb5b2dffe0b33b24e0a41bb86a8214/src/execution/simulator.h#L96-L164
template <typename Return, typename... Args>
class GeneratedCode {
public:
using Signature = Return(Args...);
static GeneratedCode FromCode(Code code); // TODO: implement
Return Call(Args... args) { return fn_ptr_(args...); }
private:
friend class GeneratedCode<Return(Args...)>;
Isolate* isolate_;
Signature* fn_ptr_;
GeneratedCode(Isolate* isolate, Signature* fn_ptr)
: isolate_(isolate), fn_ptr_(fn_ptr) {}
};
// Allow to use {GeneratedCode<ret(arg1, arg2)>} instead of
// {GeneratedCode<ret, arg1, arg2>}.
template <typename Return, typename... Args>
class GeneratedCode<Return(Args...)> : public GeneratedCode<Return, Args...> {
public:
// Automatically convert from {GeneratedCode<ret, arg1, arg2>} to
// {GeneratedCode<ret(arg1, arg2)>}.
GeneratedCode(GeneratedCode<Return, Args...> other)
: GeneratedCode<Return, Args...>(other.isolate_, other.fn_ptr_) {}
static Code cast(Object object) {
Code c;
MOZ_ASSERT(JS::Value(object).toGCThing()->is<js::jit::JitCode>());
c.value_ = JS::PrivateGCThingValue(JS::Value(object).toGCThing());
return c;
}
private:
js::jit::JitCode* inner() {
return value_.toGCThing()->as<js::jit::JitCode>();
}
};
enum class MessageTemplate { kStackOverflow };
@ -993,6 +1154,8 @@ extern bool FLAG_trace_regexp_bytecodes;
extern bool FLAG_trace_regexp_parser;
extern bool FLAG_trace_regexp_peephole_optimization;
#define V8_USE_COMPUTED_GOTO 1
} // namespace internal
} // namespace v8

View file

@ -0,0 +1,38 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Automatically generated by regexp/gen-regexp-special-case.cc
// The following functions are used to build icu::UnicodeSet
// for specical cases different between Unicode and ECMA262.
#ifdef V8_INTL_SUPPORT
#include "regexp/special-case.h"
#include "unicode/uniset.h"
namespace v8 {
namespace internal {
icu::UnicodeSet BuildIgnoreSet() {
icu::UnicodeSet set;
set.add(0x3f4);
set.add(0x2126);
set.add(0x212a, 0x212b);
set.freeze();
return set;
}
icu::UnicodeSet BuildSpecialAddSet() {
icu::UnicodeSet set;
set.add(0xc5);
set.add(0xe5);
set.add(0x398);
set.add(0x3a9);
set.add(0x3b8);
set.add(0x3c9);
set.add(0x3d1);
set.freeze();
return set;
}
} // namespace internal
} // namespace v8
#endif // V8_INTL_SUPPORT

File diff suppressed because it is too large Load diff

View file

@ -12,6 +12,7 @@
#include <vector>
#include "ds/LifoAlloc.h"
#include "ds/Sort.h"
#include "regexp/util/vector.h"
namespace v8 {
@ -24,14 +25,22 @@ class Zone {
lifoAlloc_.setAsInfallibleByDefault();
}
void* New(size_t size) { return lifoAlloc_.alloc(size); }
void* New(size_t size) {
js::LifoAlloc::AutoFallibleScope fallible(&lifoAlloc_);
js::AutoEnterOOMUnsafeRegion oomUnsafe;
void* result = lifoAlloc_.alloc(size);
if (!result) {
oomUnsafe.crash("Irregexp Zone::new");
}
return result;
}
void DeleteAll() { lifoAlloc_.freeAll(); }
// Returns true if the total memory allocated exceeds a threshold.
static const size_t kExcessLimit = 256 * 1024 * 1024;
bool excess_allocation() const {
return lifoAlloc_.peakSizeOfExcludingThis() > kExcessLimit;
return lifoAlloc_.computedSizeOfExcludingThis() > kExcessLimit;
}
private:
js::LifoAlloc lifoAlloc_;
@ -194,9 +203,19 @@ class ZoneList final {
}
template <typename CompareFunction>
void StableSort(CompareFunction cmp, size_t s, size_t l) {
std::stable_sort(begin() + s, begin() + s + l,
[cmp](const T& a, const T& b) { return cmp(&a, &b) < 0; });
void StableSort(CompareFunction cmp, size_t start, size_t length) {
js::AutoEnterOOMUnsafeRegion oomUnsafe;
T* scratch = static_cast<T*>(js_malloc(length * sizeof(T)));
if (!scratch) {
oomUnsafe.crash("Irregexp stable sort scratch space");
}
auto comparator = [cmp](const T& a, const T& b, bool* lessOrEqual) {
*lessOrEqual = cmp(&a, &b) <= 0;
return true;
};
MOZ_ALWAYS_TRUE(js::MergeSort(begin() + start, length, scratch,
comparator));
js_free(scratch);
}
void operator delete(void* pointer) { MOZ_CRASH("unreachable"); }