Merge remote-tracking branch 'origin/master' into custom

This commit is contained in:
roytam1 2020-11-13 09:15:56 +08:00
commit ee137dacc1
41 changed files with 6259 additions and 508 deletions

View file

@ -86,6 +86,7 @@ included_inclnames_to_ignore = set([
'unicode/udat.h', # ICU
'unicode/udatpg.h', # ICU
'unicode/uenum.h', # ICU
'unicode/uniset.h', # ICU
'unicode/unorm.h', # ICU
'unicode/unum.h', # ICU
'unicode/unumsys.h', # ICU

View file

@ -28,6 +28,7 @@
#include "js/HeapAPI.h"
#include "js/RootingAPI.h"
#include "js/TracingAPI.h"
#include "js/TraceKind.h"
struct JSRuntime;
@ -272,6 +273,23 @@ struct Cell
static MOZ_ALWAYS_INLINE bool needWriteBarrierPre(JS::Zone* zone);
template <class T>
inline bool is() const {
return getTraceKind() == JS::MapTypeToTraceKind<T>::kind;
}
template<class T>
inline T* as() {
MOZ_ASSERT(this->is<T>());
return static_cast<T*>(this);
}
template <class T>
inline const T* as() const {
MOZ_ASSERT(this->is<T>());
return static_cast<const T*>(this);
}
#ifdef DEBUG
inline bool isAligned() const;
void dump(FILE* fp) const;

View file

@ -0,0 +1,51 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* 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/. */
// This file forward-defines Irregexp classes that need to be visible
// to the rest of Spidermonkey and re-exports them into js::irregexp.
#ifndef regexp_RegExpTypes_h
#define regexp_RegExpTypes_h
namespace js {
class MatchPairs;
}
namespace v8 {
namespace internal {
struct InputOutputData {
const void* inputStart;
const void* inputEnd;
// Index into inputStart (in chars) at which to begin matching.
size_t startIndex;
js::MatchPairs* matches;
template <typename CharT>
InputOutputData(const CharT* inputStart, const CharT* inputEnd,
size_t startIndex, js::MatchPairs* matches)
: inputStart(inputStart),
inputEnd(inputEnd),
startIndex(startIndex),
matches(matches)
{}
};
} // namespace internal
} // namespace v8
namespace js {
namespace irregexp {
using InputOutputData = v8::internal::InputOutputData;
} // namespace irregexp
} // namespace js
#endif // regexp_RegExpTypes_h

View file

@ -1,3 +1,2 @@
This code was most recently imported from the following version of V8:
https://github.com/v8/v8/tree/2599d3cc208a3a4873be517285220abd8416c3d7/src/regexp
Imported using import-irregexp.py from:
https://github.com/v8/v8/tree/560f2d8bb3f3a72d78e1a7d7654235d53fdcc83c/src/regexp

View file

@ -1,4 +1,4 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Copyright 2020 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.
@ -7,18 +7,19 @@
#include <iostream>
#include <sstream>
#include "unicode/uchar.h"
#include "unicode/uniset.h"
#include "regexp/special-case.h"
namespace v8 {
namespace internal {
// The following code generates BuildSpecialAddSet() and BuildIgnoreSet()
// functions into "src/regexp/special-case.cc".
// See more details in http://shorturl.at/adfO5
void PrintSet(std::ofstream& out, const char* func_name,
static const uc32 kSurrogateStart = 0xd800;
static const uc32 kSurrogateEnd = 0xdfff;
static const uc32 kNonBmpStart = 0x10000;
// The following code generates "src/regexp/special-case.cc".
void PrintSet(std::ofstream& out, const char* name,
const icu::UnicodeSet& set) {
out << "icu::UnicodeSet " << func_name << "() {\n"
out << "icu::UnicodeSet Build" << name << "() {\n"
<< " icu::UnicodeSet set;\n";
for (int32_t i = 0; i < set.getRangeCount(); i++) {
if (set.getRangeStart(i) == set.getRangeEnd(i)) {
@ -30,73 +31,113 @@ void PrintSet(std::ofstream& out, const char* func_name,
}
out << " set.freeze();\n"
<< " return set;\n"
<< "}\n";
<< "}\n\n";
out << "struct " << name << "Data {\n"
<< " " << name << "Data() : set(Build" << name << "()) {}\n"
<< " const icu::UnicodeSet set;\n"
<< "};\n\n";
out << "//static\n"
<< "const icu::UnicodeSet& RegExpCaseFolding::" << name << "() {\n"
<< " static base::LazyInstance<" << name << "Data>::type set =\n"
<< " LAZY_INSTANCE_INITIALIZER;\n"
<< " return set.Pointer()->set;\n"
<< "}\n\n";
}
void PrintSpecial(std::ofstream& out) {
icu::UnicodeSet current;
icu::UnicodeSet processed(0xd800, 0xdbff); // Ignore surrogate range.
icu::UnicodeSet special_add;
icu::UnicodeSet ignore;
UErrorCode status = U_ZERO_ERROR;
icu::UnicodeSet upper("[\\p{Lu}]", status);
CHECK(U_SUCCESS(status));
// Iterate through all chars in BMP except ASCII and Surrogate.
for (UChar32 i = 0x80; i < 0x010000; i++) {
// Ignore those characters which is already processed.
if (!processed.contains(i)) {
current.set(i, i);
current.closeOver(USET_CASE_INSENSITIVE);
// Remember we already processed current.
processed.addAll(current);
// Iterate through all chars in BMP except surrogates.
for (UChar32 i = 0; i < kNonBmpStart; i++) {
if (i >= kSurrogateStart && i <= kSurrogateEnd) {
continue; // Ignore surrogate range
}
current.set(i, i);
current.closeOver(USET_CASE_INSENSITIVE);
// All uppercase characters in current.
icu::UnicodeSet keep_upper(current);
keep_upper.retainAll(upper);
// Check if we have more than one uppercase character in current.
// If there are more than one uppercase character, then it is a special
// set which need to be added into either "Special Add" set or "Ignore"
// set.
int32_t number_of_upper = 0;
for (int32_t i = 0; i < keep_upper.getRangeCount() && i <= 1; i++) {
number_of_upper +=
keep_upper.getRangeEnd(i) - keep_upper.getRangeStart(i) + 1;
// Check to see if all characters in the case-folding equivalence
// class as defined by UnicodeSet::closeOver all map to the same
// canonical value.
UChar32 canonical = RegExpCaseFolding::Canonicalize(i);
bool class_has_matching_canonical_char = false;
bool class_has_non_matching_canonical_char = false;
for (int32_t j = 0; j < current.getRangeCount(); j++) {
for (UChar32 c = current.getRangeStart(j); c <= current.getRangeEnd(j);
c++) {
if (c == i) {
continue;
}
UChar32 other_canonical = RegExpCaseFolding::Canonicalize(c);
if (canonical == other_canonical) {
class_has_matching_canonical_char = true;
} else {
class_has_non_matching_canonical_char = true;
}
}
if (number_of_upper > 1) {
// Add all non uppercase characters (could be Ll or Mn) to special add
// set.
current.removeAll(upper);
special_add.addAll(current);
// Add the uppercase characters of non uppercase character to
// special add set.
CHECK_GT(current.getRangeCount(), 0);
UChar32 main_upper = u_toupper(current.getRangeStart(0));
special_add.add(main_upper);
// Add all uppercase except the main upper to ignore set.
keep_upper.remove(main_upper);
ignore.addAll(keep_upper);
}
// If any other character in i's equivalence class has a
// different canonical value, then i needs special handling. If
// no other character shares a canonical value with i, we can
// ignore i when adding alternatives for case-independent
// comparison. If at least one other character shares a
// canonical value, then i needs special handling.
if (class_has_non_matching_canonical_char) {
if (class_has_matching_canonical_char) {
special_add.add(i);
} else {
ignore.add(i);
}
}
}
// Remove any ASCII
special_add.remove(0x0000, 0x007f);
PrintSet(out, "BuildIgnoreSet", ignore);
PrintSet(out, "BuildSpecialAddSet", special_add);
// Verify that no Unicode equivalence class contains two non-trivial
// JS equivalence classes. Every character in SpecialAddSet has the
// same canonical value as every other non-IgnoreSet character in
// its Unicode equivalence class. Therefore, if we call closeOver on
// a set containing no IgnoreSet characters, the only characters
// that must be removed from the result are in IgnoreSet. This fact
// is used in CharacterRange::AddCaseEquivalents.
for (int32_t i = 0; i < special_add.getRangeCount(); i++) {
for (UChar32 c = special_add.getRangeStart(i);
c <= special_add.getRangeEnd(i); c++) {
UChar32 canonical = RegExpCaseFolding::Canonicalize(c);
current.set(c, c);
current.closeOver(USET_CASE_INSENSITIVE);
current.removeAll(ignore);
for (int32_t j = 0; j < current.getRangeCount(); j++) {
for (UChar32 c2 = current.getRangeStart(j);
c2 <= current.getRangeEnd(j); c2++) {
CHECK_EQ(canonical, RegExpCaseFolding::Canonicalize(c2));
}
}
}
}
PrintSet(out, "IgnoreSet", ignore);
PrintSet(out, "SpecialAddSet", special_add);
}
void WriteHeader(const char* header_filename) {
std::ofstream out(header_filename);
out << std::hex << std::setfill('0') << std::setw(4);
out << "// Automatically generated by regexp/gen-regexp-special-case.cc\n"
<< "// The following functions are used to build icu::UnicodeSet\n"
<< "// for specical cases different between Unicode and ECMA262.\n"
out << "// Copyright 2020 the V8 project authors. All rights reserved.\n"
<< "// Use of this source code is governed by a BSD-style license that\n"
<< "// can be found in the LICENSE file.\n\n"
<< "// Automatically generated by regexp/gen-regexp-special-case.cc\n\n"
<< "// The following functions are used to build UnicodeSets\n"
<< "// for special cases where the case-folding algorithm used by\n"
<< "// UnicodeSet::closeOver(USET_CASE_INSENSITIVE) does not match\n"
<< "// the algorithm defined in ECMAScript 2020 21.2.2.8.2 (Runtime\n"
<< "// Semantics: Canonicalize) step 3.\n\n"
<< "#ifdef V8_INTL_SUPPORT\n"
<< "#include \"src/base/lazy-instance.h\"\n\n"
<< "#include \"src/regexp/special-case.h\"\n\n"
<< "#include \"unicode/uniset.h\"\n"
<< "namespace v8 {\n"

View file

@ -0,0 +1,143 @@
#!/usr/bin/env python3
# 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/.
# This script handles all the mechanical steps of importing irregexp from v8:
#
# 1. Acquire the source: either from github, or optionally from a local copy of v8.
# 2. Copy the contents of v8/src/regexp into js/src/regexp
# - Exclude files that we have chosen not to import.
# 3. While doing so, update #includes:
# - Change "src/regexp/*" to "regexp/*".
# - Remove other v8-specific headers completely.
# 4. Add '#include "regexp/regexp-shim.h" in the necessary places.
# 5. Update the VERSION file to include the correct git hash.
#
# Usage:
# cd path/to/js/src/regexp
# ./import-irregexp.py --path path/to/v8/src/regexp
#
# Alternatively, without the --path argument, import-irregexp.py will
# clone v8 from github into a temporary directory.
#
# After running this script, changes to the shim code may be necessary
# to account for changes in upstream irregexp.
import os
import re
import subprocess
import sys
from pathlib import Path
def get_hash(path):
# Get the hash for the current git revision
cwd = os.getcwd()
os.chdir(path)
command = ['git', 'rev-parse', 'HEAD']
result = subprocess.check_output(command, encoding='utf-8')
os.chdir(cwd)
return result.rstrip()
def copy_and_update_includes(src_path, dst_path):
# List of header files that need to include the shim header
need_shim = ['property-sequences.h',
'regexp-ast.h',
'regexp-bytecode-peephole.h',
'regexp-bytecodes.h',
'regexp-dotprinter.h',
'regexp.h',
'regexp-macro-assembler.h',
'regexp-stack.h',
'special-case.h']
src = open(str(src_path), 'r')
dst = open(str(dst_path), 'w')
# 1. Rewrite includes of V8 regexp headers:
regexp_include = re.compile('#include "src/regexp')
regexp_include_new = '#include "regexp'
# 2. Remove includes of other V8 headers
other_include = re.compile('#include "src/')
# 3. If needed, add '#include "regexp/regexp-shim.h"'.
# Note: We get a little fancy to ensure that header files are
# in alphabetic order. `need_to_add_shim` is true if we still
# have to add the shim header in this file. `adding_shim_now`
# is true if we have found a '#include "src/*' and we are just
# waiting to find something alphabetically smaller (or an empty
# line) so that we can insert the shim header in the right place.
need_to_add_shim = src_path.name in need_shim
adding_shim_now = False
for line in src:
if adding_shim_now:
if (line == '\n' or line > '#include "src/regexp/regexp-shim.h"'):
dst.write('#include "regexp/regexp-shim.h"\n')
need_to_add_shim = False
adding_shim_now = False
if regexp_include.search(line):
dst.write(re.sub(regexp_include, regexp_include_new, line))
elif other_include.search(line):
if need_to_add_shim:
adding_shim_now = True
else:
dst.write(line)
def import_from(srcdir, dstdir):
excluded = ['OWNERS',
'regexp.cc',
'regexp-utils.cc',
'regexp-utils.h',
'regexp-macro-assembler-arch.h']
for file in srcdir.iterdir():
if file.is_dir():
continue
if str(file.name) in excluded:
continue
copy_and_update_includes(file, dstdir / file.name)
# Update VERSION file
hash = get_hash(srcdir)
version_file = open(str(dstdir / 'VERSION'), 'w')
version_file.write('Imported using import-irregexp.py from:\n')
version_file.write('https://github.com/v8/v8/tree/%s/src/regexp\n' % hash)
if __name__ == '__main__':
import argparse
import tempfile
# This script should be run from js/src/regexp to work correctly.
current_path = Path(os.getcwd())
expected_path = 'js/src/regexp'
if not current_path.match(expected_path):
raise RuntimeError('%s must be run from %s' % (sys.argv[0],
expected_path))
parser = argparse.ArgumentParser(description='Import irregexp from v8')
parser.add_argument('-p', '--path', help='path to v8/src/regexp')
args = parser.parse_args()
if args.path:
src_path = Path(args.path)
if not (src_path / 'regexp.h').exists():
print('Usage:\n import-irregexp.py --path <path/to/v8/src/regexp>')
sys.exit(1)
import_from(src_path, current_path)
sys.exit(0)
with tempfile.TemporaryDirectory() as tempdir:
v8_git = 'https://github.com/v8/v8.git'
clone = 'git clone --depth 1 %s %s' % (v8_git, tempdir)
os.system(clone)
src_path = Path(tempdir) / 'src/regexp'
import_from(src_path, current_path)

View file

@ -22,10 +22,16 @@ SOURCES += [
'regexp-interpreter.cc',
'regexp-macro-assembler-tracer.cc',
'regexp-macro-assembler.cc',
'regexp-native-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

@ -7,6 +7,7 @@
#ifdef V8_INTL_SUPPORT
#include "regexp/regexp-shim.h"
namespace v8 {
namespace internal {

View file

@ -5,6 +5,7 @@
#ifndef V8_REGEXP_REGEXP_AST_H_
#define V8_REGEXP_REGEXP_AST_H_
#include "regexp/regexp-shim.h"
namespace v8 {
namespace internal {
@ -457,7 +458,11 @@ class RegExpQuantifier final : public RegExpTree {
class RegExpCapture final : public RegExpTree {
public:
explicit RegExpCapture(int index)
: body_(nullptr), index_(index), name_(nullptr) {}
: body_(nullptr),
index_(index),
min_match_(0),
max_match_(0),
name_(nullptr) {}
void* Accept(RegExpVisitor* visitor, void* data) override;
RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
static RegExpNode* ToNode(RegExpTree* body, int index,
@ -467,10 +472,14 @@ class RegExpCapture final : public RegExpTree {
bool IsAnchoredAtEnd() override;
Interval CaptureRegisters() override;
bool IsCapture() override;
int min_match() override { return body_->min_match(); }
int max_match() override { return body_->max_match(); }
int min_match() override { return min_match_; }
int max_match() override { return max_match_; }
RegExpTree* body() { return body_; }
void set_body(RegExpTree* body) { body_ = body; }
void set_body(RegExpTree* body) {
body_ = body;
min_match_ = body->min_match();
max_match_ = body->max_match();
}
int index() const { return index_; }
const ZoneVector<uc16>* name() const { return name_; }
void set_name(const ZoneVector<uc16>* name) { name_ = name; }
@ -480,12 +489,17 @@ class RegExpCapture final : public RegExpTree {
private:
RegExpTree* body_;
int index_;
int min_match_;
int max_match_;
const ZoneVector<uc16>* name_;
};
class RegExpGroup final : public RegExpTree {
public:
explicit RegExpGroup(RegExpTree* body) : body_(body) {}
explicit RegExpGroup(RegExpTree* body)
: body_(body),
min_match_(body->min_match()),
max_match_(body->max_match()) {}
void* Accept(RegExpVisitor* visitor, void* data) override;
RegExpNode* ToNode(RegExpCompiler* compiler,
RegExpNode* on_success) override {
@ -495,13 +509,15 @@ class RegExpGroup final : public RegExpTree {
bool IsAnchoredAtStart() override { return body_->IsAnchoredAtStart(); }
bool IsAnchoredAtEnd() override { return body_->IsAnchoredAtEnd(); }
bool IsGroup() override;
int min_match() override { return body_->min_match(); }
int max_match() override { return body_->max_match(); }
int min_match() override { return min_match_; }
int max_match() override { return max_match_; }
Interval CaptureRegisters() override { return body_->CaptureRegisters(); }
RegExpTree* body() { return body_; }
private:
RegExpTree* body_;
int min_match_;
int max_match_;
};
class RegExpLookaround final : public RegExpTree {

View file

@ -327,13 +327,11 @@ void RegExpBytecodeGenerator::CheckNotBackReference(int start_reg,
}
void RegExpBytecodeGenerator::CheckNotBackReferenceIgnoreCase(
int start_reg, bool read_backward, bool unicode, Label* on_not_equal) {
int start_reg, bool read_backward, Label* on_not_equal) {
DCHECK_LE(0, start_reg);
DCHECK_GE(kMaxRegister, start_reg);
Emit(read_backward ? (unicode ? BC_CHECK_NOT_BACK_REF_NO_CASE_UNICODE_BACKWARD
: BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD)
: (unicode ? BC_CHECK_NOT_BACK_REF_NO_CASE_UNICODE
: BC_CHECK_NOT_BACK_REF_NO_CASE),
Emit(read_backward ? BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD
: BC_CHECK_NOT_BACK_REF_NO_CASE,
start_reg);
EmitOrLink(on_not_equal);
}

View file

@ -69,7 +69,7 @@ class V8_EXPORT_PRIVATE RegExpBytecodeGenerator : public RegExpMacroAssembler {
virtual void CheckNotBackReference(int start_reg, bool read_backward,
Label* on_no_match);
virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
bool read_backward, bool unicode,
bool read_backward,
Label* on_no_match);
virtual void IfRegisterLT(int register_index, int comparand, Label* if_lt);
virtual void IfRegisterGE(int register_index, int comparand, Label* if_ge);

View file

@ -428,7 +428,6 @@ BytecodeArgumentMapping BytecodeSequenceNode::ArgumentMapping(
size_t index) const {
DCHECK(IsSequence());
DCHECK(argument_mapping_ != nullptr);
DCHECK_GE(index, 0);
DCHECK_LT(index, argument_mapping_->size());
return argument_mapping_->at(index);

View file

@ -5,6 +5,7 @@
#ifndef V8_REGEXP_REGEXP_BYTECODE_PEEPHOLE_H_
#define V8_REGEXP_REGEXP_BYTECODE_PEEPHOLE_H_
#include "regexp/regexp-shim.h"
namespace v8 {
namespace internal {

View file

@ -5,6 +5,7 @@
#ifndef V8_REGEXP_REGEXP_BYTECODES_H_
#define V8_REGEXP_REGEXP_BYTECODES_H_
#include "regexp/regexp-shim.h"
namespace v8 {
namespace internal {
@ -99,12 +100,12 @@ STATIC_ASSERT(1 << BYTECODE_SHIFT > BYTECODE_MASK);
V(CHECK_BIT_IN_TABLE, 34, 24) /* bc8 pad24 addr32 bits128 */ \
V(CHECK_LT, 35, 8) /* bc8 pad8 uc16 addr32 */ \
V(CHECK_GT, 36, 8) /* bc8 pad8 uc16 addr32 */ \
V(CHECK_NOT_BACK_REF, 37, 8) /* bc8 reg_idx24 addr32 */ \
V(CHECK_NOT_BACK_REF_NO_CASE, 38, 8) /* bc8 reg_idx24 addr32 */ \
V(CHECK_NOT_BACK_REF_NO_CASE_UNICODE, 39, 8) \
V(CHECK_NOT_BACK_REF, 37, 8) /* bc8 reg_idx24 addr32 */ \
V(CHECK_NOT_BACK_REF_NO_CASE, 38, 8) /* bc8 reg_idx24 addr32 */ \
V(CHECK_NOT_BACK_REF_NO_CASE_UNICODE, 39, 8) /* UNUSED */ \
V(CHECK_NOT_BACK_REF_BACKWARD, 40, 8) /* bc8 reg_idx24 addr32 */ \
V(CHECK_NOT_BACK_REF_NO_CASE_BACKWARD, 41, 8) /* bc8 reg_idx24 addr32 */ \
V(CHECK_NOT_BACK_REF_NO_CASE_UNICODE_BACKWARD, 42, 8) \
V(CHECK_NOT_BACK_REF_NO_CASE_UNICODE_BACKWARD, 42, 8) /* UNUSED */ \
V(CHECK_NOT_REGS_EQUAL, 43, 12) /* bc8 regidx24 reg_idx32 addr32 */ \
V(CHECK_REGISTER_LT, 44, 12) /* bc8 reg_idx24 value32 addr32 */ \
V(CHECK_REGISTER_GE, 45, 12) /* bc8 reg_idx24 value32 addr32 */ \

View file

@ -1137,39 +1137,6 @@ Vector<const int> CharacterRange::GetWordBounds() {
return Vector<const int>(kWordRanges, kWordRangeCount - 1);
}
#ifdef V8_INTL_SUPPORT
struct IgnoreSet {
IgnoreSet() : set(BuildIgnoreSet()) {}
const icu::UnicodeSet set;
};
struct SpecialAddSet {
SpecialAddSet() : set(BuildSpecialAddSet()) {}
const icu::UnicodeSet set;
};
icu::UnicodeSet BuildAsciiAToZSet() {
icu::UnicodeSet set('a', 'z');
set.add('A', 'Z');
set.freeze();
return set;
}
struct AsciiAToZSet {
AsciiAToZSet() : set(BuildAsciiAToZSet()) {}
const icu::UnicodeSet set;
};
static base::LazyInstance<IgnoreSet>::type ignore_set =
LAZY_INSTANCE_INITIALIZER;
static base::LazyInstance<SpecialAddSet>::type special_add_set =
LAZY_INSTANCE_INITIALIZER;
static base::LazyInstance<AsciiAToZSet>::type ascii_a_to_z_set =
LAZY_INSTANCE_INITIALIZER;
#endif // V8_INTL_SUPPORT
// static
void CharacterRange::AddCaseEquivalents(Isolate* isolate, Zone* zone,
ZoneList<CharacterRange>* ranges,
@ -1192,75 +1159,22 @@ void CharacterRange::AddCaseEquivalents(Isolate* isolate, Zone* zone,
others.add(from, to);
}
// Set of characters already added to ranges that do not need to be added
// again.
// Compute the set of additional characters that should be added,
// using UnicodeSet::closeOver. ECMA 262 defines slightly different
// case-folding rules than Unicode, so some characters that are
// added by closeOver do not match anything other than themselves in
// JS. For example, 'ſ' (U+017F LATIN SMALL LETTER LONG S) is the
// same case-insensitive character as 's' or 'S' according to
// Unicode, but does not match any other character in JS. To handle
// this case, we add such characters to the IgnoreSet and filter
// them out. We filter twice: once before calling closeOver (to
// prevent 'ſ' from adding 's'), and once after calling closeOver
// (to prevent 's' from adding 'ſ'). See regexp/special-case.h for
// more information.
icu::UnicodeSet already_added(others);
// Set of characters in ranges that are in the 52 ASCII characters [a-zA-Z].
icu::UnicodeSet in_ascii_a_to_z(others);
in_ascii_a_to_z.retainAll(ascii_a_to_z_set.Pointer()->set);
// Remove all chars in [a-zA-Z] from others.
others.removeAll(in_ascii_a_to_z);
// Set of characters in ranges that are overlapping with special add set.
icu::UnicodeSet in_special_add(others);
in_special_add.retainAll(special_add_set.Pointer()->set);
others.removeAll(in_special_add);
// Ignore all chars in ignore set.
others.removeAll(ignore_set.Pointer()->set);
// For most of the chars in ranges that is still in others, find the case
// equivlant set by calling closeOver(USET_CASE_INSENSITIVE).
others.removeAll(RegExpCaseFolding::IgnoreSet());
others.closeOver(USET_CASE_INSENSITIVE);
// Because closeOver(USET_CASE_INSENSITIVE) may add ASCII [a-zA-Z] to others,
// but ECMA262 "i" mode won't consider that, remove them from others.
// Ex: U+017F add 'S' and 's' to others.
others.removeAll(ascii_a_to_z_set.Pointer()->set);
// Special handling for in_ascii_a_to_z.
for (int32_t i = 0; i < in_ascii_a_to_z.getRangeCount(); i++) {
UChar32 start = in_ascii_a_to_z.getRangeStart(i);
UChar32 end = in_ascii_a_to_z.getRangeEnd(i);
// Check if it is uppercase A-Z by checking bit 6.
if (start & 0x0020) {
// Add the lowercases
others.add(start & 0x005F, end & 0x005F);
} else {
// Add the uppercases
others.add(start | 0x0020, end | 0x0020);
}
}
// Special handling for chars in "Special Add" set.
for (int32_t i = 0; i < in_special_add.getRangeCount(); i++) {
UChar32 end = in_special_add.getRangeEnd(i);
for (UChar32 ch = in_special_add.getRangeStart(i); ch <= end; ch++) {
// Add the uppercase of this character if itself is not an uppercase
// character.
// Note: The if condiction cannot be u_islower(ch) because ch could be
// neither uppercase nor lowercase but Mn.
if (!u_isupper(ch)) {
others.add(u_toupper(ch));
}
icu::UnicodeSet candidates(ch, ch);
candidates.closeOver(USET_CASE_INSENSITIVE);
for (int32_t j = 0; j < candidates.getRangeCount(); j++) {
UChar32 end2 = candidates.getRangeEnd(j);
for (UChar32 ch2 = candidates.getRangeStart(j); ch2 <= end2; ch2++) {
// Add character that is not uppercase to others.
if (!u_isupper(ch2)) {
others.add(ch2);
}
}
}
}
}
// Remove all characters which already in the ranges.
others.removeAll(RegExpCaseFolding::IgnoreSet());
others.removeAll(already_added);
// Add others to the ranges

View file

@ -5,7 +5,9 @@
#include "regexp/regexp-compiler.h"
#include "regexp/regexp-macro-assembler-arch.h"
#include "regexp/regexp-macro-assembler-tracer.h"
#ifdef V8_INTL_SUPPORT
#include "regexp/special-case.h"
#endif // V8_INTL_SUPPORT
#ifdef V8_INTL_SUPPORT
#include "unicode/locid.h"
@ -237,20 +239,15 @@ RegExpCompiler::RegExpCompiler(Isolate* isolate, Zone* zone, int capture_count,
RegExpCompiler::CompilationResult RegExpCompiler::Assemble(
Isolate* isolate, RegExpMacroAssembler* macro_assembler, RegExpNode* start,
int capture_count, Handle<String> pattern) {
#ifdef DEBUG
if (FLAG_trace_regexp_assembler)
macro_assembler_ = new RegExpMacroAssemblerTracer(isolate, macro_assembler);
else
#endif
macro_assembler_ = macro_assembler;
macro_assembler_ = macro_assembler;
std::vector<RegExpNode*> work_list;
ZoneVector<RegExpNode*> work_list(zone());
work_list_ = &work_list;
Label fail;
macro_assembler_->PushBacktrack(&fail);
Trace new_trace;
start->Emit(this, &new_trace);
macro_assembler_->Bind(&fail);
macro_assembler_->BindJumpTarget(&fail);
macro_assembler_->Fail();
while (!work_list.empty()) {
RegExpNode* node = work_list.back();
@ -264,14 +261,9 @@ RegExpCompiler::CompilationResult RegExpCompiler::Assemble(
}
Handle<HeapObject> code = macro_assembler_->GetCode(pattern);
isolate->IncreaseTotalRegexpCodeGenerated(code->Size());
isolate->IncreaseTotalRegexpCodeGenerated(code);
work_list_ = nullptr;
#ifdef DEBUG
if (FLAG_trace_regexp_assembler) {
delete macro_assembler_;
}
#endif
return {*code, next_register_};
}
@ -557,7 +549,7 @@ void Trace::Flush(RegExpCompiler* compiler, RegExpNode* successor) {
}
// On backtrack we need to restore state.
assembler->Bind(&undo);
assembler->BindJumpTarget(&undo);
RestoreAffectedRegisters(assembler, max_register, registers_to_pop,
registers_to_clear);
if (backtrack() == nullptr) {
@ -720,32 +712,34 @@ static int GetCaseIndependentLetters(Isolate* isolate, uc16 character,
unibrow::uchar* letters,
int letter_length) {
#ifdef V8_INTL_SUPPORT
// Special case for U+017F which has upper case in ASCII range.
if (character == 0x017f) {
if (RegExpCaseFolding::IgnoreSet().contains(character)) {
letters[0] = character;
return 1;
}
bool in_special_add_set =
RegExpCaseFolding::SpecialAddSet().contains(character);
icu::UnicodeSet set;
set.add(character);
set = set.closeOver(USET_CASE_INSENSITIVE);
UChar32 canon = 0;
if (in_special_add_set) {
canon = RegExpCaseFolding::Canonicalize(character);
}
int32_t range_count = set.getRangeCount();
int items = 0;
for (int32_t i = 0; i < range_count; i++) {
UChar32 start = set.getRangeStart(i);
UChar32 end = set.getRangeEnd(i);
CHECK(end - start + items <= letter_length);
// Only add to the output if character is not in ASCII range
// or the case equivalent character is in ASCII range.
// #sec-runtime-semantics-canonicalize-ch
// 3.g If the numeric value of ch ≥ 128 and the numeric value of cu < 128,
// return ch.
if (!((start >= 128) && (character < 128))) {
// No range have start and end span across code point 128.
DCHECK((start >= 128) == (end >= 128));
for (UChar32 cu = start; cu <= end; cu++) {
if (one_byte_subject && cu > String::kMaxOneByteCharCode) break;
letters[items++] = (unibrow::uchar)(cu);
for (UChar32 cu = start; cu <= end; cu++) {
if (one_byte_subject && cu > String::kMaxOneByteCharCode) break;
if (in_special_add_set && RegExpCaseFolding::Canonicalize(cu) != canon) {
continue;
}
letters[items++] = (unibrow::uchar)(cu);
}
}
return items;
@ -852,10 +846,6 @@ static bool ShortCutEmitCharacterPair(RegExpMacroAssembler* macro_assembler,
return false;
}
using EmitCharacterFunction = bool(Isolate* isolate, RegExpCompiler* compiler,
uc16 c, Label* on_failure, int cp_offset,
bool check, bool preloaded);
// Only emits letters (things that have case). Only used for case independent
// matches.
static inline bool EmitAtomLetter(Isolate* isolate, RegExpCompiler* compiler,
@ -1843,13 +1833,13 @@ RegExpNode* TextNode::FilterOneByte(int depth) {
if (elm.text_type() == TextElement::ATOM) {
Vector<const uc16> quarks = elm.atom()->data();
for (int j = 0; j < quarks.length(); j++) {
uint16_t c = quarks[j];
uc16 c = quarks[j];
if (elm.atom()->ignore_case()) {
c = unibrow::Latin1::TryConvertToLatin1(c);
}
if (c > unibrow::Latin1::kMaxChar) return set_replacement(nullptr);
// Replace quark in case we converted to Latin-1.
uint16_t* writable_quarks = const_cast<uint16_t*>(quarks.begin());
uc16* writable_quarks = const_cast<uc16*>(quarks.begin());
writable_quarks[j] = c;
}
} else {
@ -2304,7 +2294,6 @@ void TextNode::TextEmitPass(RegExpCompiler* compiler, TextEmitPassType pass,
for (int j = preloaded ? 0 : quarks.length() - 1; j >= 0; j--) {
if (first_element_checked && i == 0 && j == 0) continue;
if (DeterminedAlready(quick_check, elm.cp_offset() + j)) continue;
EmitCharacterFunction* emit_function = nullptr;
uc16 quark = quarks[j];
if (elm.atom()->ignore_case()) {
// Everywhere else we assume that a non-Latin-1 character cannot match
@ -2312,6 +2301,9 @@ void TextNode::TextEmitPass(RegExpCompiler* compiler, TextEmitPassType pass,
// invalid by using the Latin1 equivalent instead.
quark = unibrow::Latin1::TryConvertToLatin1(quark);
}
bool needs_bounds_check =
*checked_up_to < cp_offset + j || read_backward();
bool bounds_checked = false;
switch (pass) {
case NON_LATIN1_MATCH:
DCHECK(one_byte);
@ -2321,24 +2313,24 @@ void TextNode::TextEmitPass(RegExpCompiler* compiler, TextEmitPassType pass,
}
break;
case NON_LETTER_CHARACTER_MATCH:
emit_function = &EmitAtomNonLetter;
bounds_checked =
EmitAtomNonLetter(isolate, compiler, quark, backtrack,
cp_offset + j, needs_bounds_check, preloaded);
break;
case SIMPLE_CHARACTER_MATCH:
emit_function = &EmitSimpleCharacter;
bounds_checked = EmitSimpleCharacter(isolate, compiler, quark,
backtrack, cp_offset + j,
needs_bounds_check, preloaded);
break;
case CASE_CHARACTER_MATCH:
emit_function = &EmitAtomLetter;
bounds_checked =
EmitAtomLetter(isolate, compiler, quark, backtrack,
cp_offset + j, needs_bounds_check, preloaded);
break;
default:
break;
}
if (emit_function != nullptr) {
bool bounds_check = *checked_up_to < cp_offset + j || read_backward();
bool bound_checked =
emit_function(isolate, compiler, quark, backtrack, cp_offset + j,
bounds_check, preloaded);
if (bound_checked) UpdateBoundsCheck(cp_offset + j, checked_up_to);
}
if (bounds_checked) UpdateBoundsCheck(cp_offset + j, checked_up_to);
}
} else {
DCHECK_EQ(TextElement::CHAR_CLASS, elm.text_type());
@ -3424,8 +3416,8 @@ void BackReferenceNode::Emit(RegExpCompiler* compiler, Trace* trace) {
DCHECK_EQ(start_reg_ + 1, end_reg_);
if (IgnoreCase(flags_)) {
assembler->CheckNotBackReferenceIgnoreCase(
start_reg_, read_backward(), IsUnicode(flags_), trace->backtrack());
assembler->CheckNotBackReferenceIgnoreCase(start_reg_, read_backward(),
trace->backtrack());
} else {
assembler->CheckNotBackReference(start_reg_, read_backward(),
trace->backtrack());
@ -3597,12 +3589,17 @@ template <typename... Propagators>
class Analysis : public NodeVisitor {
public:
Analysis(Isolate* isolate, bool is_one_byte)
: isolate_(isolate), is_one_byte_(is_one_byte), error_message_(nullptr) {}
: isolate_(isolate),
is_one_byte_(is_one_byte),
error_(RegExpError::kNone) {}
void EnsureAnalyzed(RegExpNode* that) {
StackLimitCheck check(isolate());
if (check.HasOverflowed()) {
fail("Stack overflow");
if (FLAG_correctness_fuzzer_suppressions) {
FATAL("Analysis: Aborting on stack overflow");
}
fail(RegExpError::kAnalysisStackOverflow);
return;
}
if (that->info()->been_analyzed || that->info()->being_analyzed) return;
@ -3612,12 +3609,12 @@ class Analysis : public NodeVisitor {
that->info()->been_analyzed = true;
}
bool has_failed() { return error_message_ != nullptr; }
const char* error_message() {
DCHECK(error_message_ != nullptr);
return error_message_;
bool has_failed() { return error_ != RegExpError::kNone; }
RegExpError error() {
DCHECK(error_ != RegExpError::kNone);
return error_;
}
void fail(const char* error_message) { error_message_ = error_message; }
void fail(RegExpError error) { error_ = error; }
Isolate* isolate() const { return isolate_; }
@ -3702,19 +3699,19 @@ class Analysis : public NodeVisitor {
private:
Isolate* isolate_;
bool is_one_byte_;
const char* error_message_;
RegExpError error_;
DISALLOW_IMPLICIT_CONSTRUCTORS(Analysis);
};
const char* AnalyzeRegExp(Isolate* isolate, bool is_one_byte,
RegExpError AnalyzeRegExp(Isolate* isolate, bool is_one_byte,
RegExpNode* node) {
Analysis<AssertionPropagator, EatsAtLeastPropagator> analysis(isolate,
is_one_byte);
DCHECK_EQ(node->info()->been_analyzed, false);
analysis.EnsureAnalyzed(node);
DCHECK_IMPLIES(analysis.has_failed(), analysis.error_message() != nullptr);
return analysis.has_failed() ? analysis.error_message() : nullptr;
DCHECK_IMPLIES(analysis.has_failed(), analysis.error() != RegExpError::kNone);
return analysis.has_failed() ? analysis.error() : RegExpError::kNone;
}
void BackReferenceNode::FillInBMInfo(Isolate* isolate, int offset, int budget,

View file

@ -422,10 +422,7 @@ struct PreloadState {
// Analysis performs assertion propagation and computes eats_at_least_ values.
// See the comments on AssertionPropagator and EatsAtLeastPropagator for more
// details.
//
// This method returns nullptr on success or a null-terminated failure message
// on failure.
const char* AnalyzeRegExp(Isolate* isolate, bool is_one_byte, RegExpNode* node);
RegExpError AnalyzeRegExp(Isolate* isolate, bool is_one_byte, RegExpNode* node);
class FrequencyCollator {
public:
@ -502,18 +499,17 @@ class RegExpCompiler {
}
struct CompilationResult final {
explicit CompilationResult(const char* error_message)
: error_message(error_message) {}
explicit CompilationResult(RegExpError err) : error(err) {}
CompilationResult(Object code, int registers)
: code(code), num_registers(registers) {}
static CompilationResult RegExpTooBig() {
return CompilationResult("RegExp too big");
return CompilationResult(RegExpError::kTooLarge);
}
bool Succeeded() const { return error_message == nullptr; }
bool Succeeded() const { return error == RegExpError::kNone; }
const char* const error_message = nullptr;
const RegExpError error = RegExpError::kNone;
Object code;
int num_registers = 0;
};
@ -575,7 +571,7 @@ class RegExpCompiler {
int next_register_;
int unicode_lookaround_stack_register_;
int unicode_lookaround_position_register_;
std::vector<RegExpNode*>* work_list_;
ZoneVector<RegExpNode*>* work_list_;
int recursion_depth_;
RegExpMacroAssembler* macro_assembler_;
bool one_byte_;

View file

@ -5,6 +5,7 @@
#ifndef V8_REGEXP_REGEXP_DOTPRINTER_H_
#define V8_REGEXP_REGEXP_DOTPRINTER_H_
#include "regexp/regexp-shim.h"
namespace v8 {
namespace internal {

View file

@ -0,0 +1,22 @@
// Copyright 2020 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.
#include "regexp/regexp-error.h"
namespace v8 {
namespace internal {
const char* kRegExpErrorStrings[] = {
#define TEMPLATE(NAME, STRING) STRING,
REGEXP_ERROR_MESSAGES(TEMPLATE)
#undef TEMPLATE
};
const char* RegExpErrorString(RegExpError error) {
DCHECK_LT(error, RegExpError::NumErrors);
return kRegExpErrorStrings[static_cast<int>(error)];
}
} // namespace internal
} // namespace v8

View file

@ -0,0 +1,57 @@
// Copyright 2020 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.
#ifndef V8_REGEXP_REGEXP_ERROR_H_
#define V8_REGEXP_REGEXP_ERROR_H_
#include "regexp-shim.h"
namespace v8 {
namespace internal {
#define REGEXP_ERROR_MESSAGES(T) \
T(None, "") \
T(StackOverflow, "Maximum call stack size exceeded") \
T(AnalysisStackOverflow, "Stack overflow") \
T(TooLarge, "Regular expression too large") \
T(UnterminatedGroup, "Unterminated group") \
T(UnmatchedParen, "Unmatched ')'") \
T(EscapeAtEndOfPattern, "\\ at end of pattern") \
T(InvalidPropertyName, "Invalid property name") \
T(InvalidEscape, "Invalid escape") \
T(InvalidDecimalEscape, "Invalid decimal escape") \
T(InvalidUnicodeEscape, "Invalid Unicode escape") \
T(NothingToRepeat, "Nothing to repeat") \
T(LoneQuantifierBrackets, "Lone quantifier brackets") \
T(RangeOutOfOrder, "numbers out of order in {} quantifier") \
T(IncompleteQuantifier, "Incomplete quantifier") \
T(InvalidQuantifier, "Invalid quantifier") \
T(InvalidGroup, "Invalid group") \
T(MultipleFlagDashes, "Multiple dashes in flag group") \
T(RepeatedFlag, "Repeated flag in flag group") \
T(InvalidFlagGroup, "Invalid flag group") \
T(TooManyCaptures, "Too many captures") \
T(InvalidCaptureGroupName, "Invalid capture group name") \
T(DuplicateCaptureGroupName, "Duplicate capture group name") \
T(InvalidNamedReference, "Invalid named reference") \
T(InvalidNamedCaptureReference, "Invalid named capture referenced") \
T(InvalidClassEscape, "Invalid class escape") \
T(InvalidClassPropertyName, "Invalid property name in character class") \
T(InvalidCharacterClass, "Invalid character class") \
T(UnterminatedCharacterClass, "Unterminated character class") \
T(OutOfOrderCharacterClass, "Range out of order in character class")
enum class RegExpError : uint32_t {
#define TEMPLATE(NAME, STRING) k##NAME,
REGEXP_ERROR_MESSAGES(TEMPLATE)
#undef TEMPLATE
NumErrors
};
V8_EXPORT_PRIVATE const char* RegExpErrorString(RegExpError error);
} // namespace internal
} // namespace v8
#endif // V8_REGEXP_REGEXP_ERROR_H_

View file

@ -28,18 +28,18 @@ namespace internal {
namespace {
bool BackRefMatchesNoCase(Isolate* isolate, int from, int current, int len,
Vector<const uc16> subject, bool unicode) {
Vector<const uc16> subject) {
Address offset_a =
reinterpret_cast<Address>(const_cast<uc16*>(&subject.at(from)));
Address offset_b =
reinterpret_cast<Address>(const_cast<uc16*>(&subject.at(current)));
size_t length = len * kUC16Size;
return RegExpMacroAssembler::CaseInsensitiveCompareUC16(
offset_a, offset_b, length, unicode ? nullptr : isolate) == 1;
return RegExpMacroAssembler::CaseInsensitiveCompareUC16(offset_a, offset_b,
length, isolate) == 1;
}
bool BackRefMatchesNoCase(Isolate* isolate, int from, int current, int len,
Vector<const uint8_t> subject, bool unicode) {
Vector<const uint8_t> subject) {
// For Latin1 characters the unicode flag makes no difference.
for (int i = 0; i < len; i++) {
unsigned int old_char = subject[from++];
@ -82,11 +82,17 @@ int32_t Load32Aligned(const byte* pc) {
return *reinterpret_cast<const int32_t*>(pc);
}
int32_t Load16Aligned(const byte* pc) {
// TODO(jgruber): Rename to Load16AlignedUnsigned.
uint32_t Load16Aligned(const byte* pc) {
DCHECK_EQ(0, reinterpret_cast<intptr_t>(pc) & 1);
return *reinterpret_cast<const uint16_t*>(pc);
}
int32_t Load16AlignedSigned(const byte* pc) {
DCHECK_EQ(0, reinterpret_cast<intptr_t>(pc) & 1);
return *reinterpret_cast<const int16_t*>(pc);
}
// A simple abstraction over the backtracking stack used by the interpreter.
//
// Despite the name 'backtracking' stack, it's actually used as a generic stack
@ -734,26 +740,14 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
DISPATCH();
}
BYTECODE(CHECK_NOT_BACK_REF_NO_CASE_UNICODE) {
int from = registers[insn >> BYTECODE_SHIFT];
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
if (from >= 0 && len > 0) {
if (current + len > subject.length() ||
!BackRefMatchesNoCase(isolate, from, current, len, subject, true)) {
SET_PC_FROM_OFFSET(Load32Aligned(pc + 4));
DISPATCH();
}
current += len;
}
ADVANCE(CHECK_NOT_BACK_REF_NO_CASE_UNICODE);
DISPATCH();
UNREACHABLE(); // TODO(jgruber): Remove this unused bytecode.
}
BYTECODE(CHECK_NOT_BACK_REF_NO_CASE) {
int from = registers[insn >> BYTECODE_SHIFT];
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
if (from >= 0 && len > 0) {
if (current + len > subject.length() ||
!BackRefMatchesNoCase(isolate, from, current, len, subject,
false)) {
!BackRefMatchesNoCase(isolate, from, current, len, subject)) {
SET_PC_FROM_OFFSET(Load32Aligned(pc + 4));
DISPATCH();
}
@ -763,27 +757,14 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
DISPATCH();
}
BYTECODE(CHECK_NOT_BACK_REF_NO_CASE_UNICODE_BACKWARD) {
int from = registers[insn >> BYTECODE_SHIFT];
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
if (from >= 0 && len > 0) {
if (current - len < 0 ||
!BackRefMatchesNoCase(isolate, from, current - len, len, subject,
true)) {
SET_PC_FROM_OFFSET(Load32Aligned(pc + 4));
DISPATCH();
}
current -= len;
}
ADVANCE(CHECK_NOT_BACK_REF_NO_CASE_UNICODE_BACKWARD);
DISPATCH();
UNREACHABLE(); // TODO(jgruber): Remove this unused bytecode.
}
BYTECODE(CHECK_NOT_BACK_REF_NO_CASE_BACKWARD) {
int from = registers[insn >> BYTECODE_SHIFT];
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
if (from >= 0 && len > 0) {
if (current - len < 0 ||
!BackRefMatchesNoCase(isolate, from, current - len, len, subject,
false)) {
!BackRefMatchesNoCase(isolate, from, current - len, len, subject)) {
SET_PC_FROM_OFFSET(Load32Aligned(pc + 4));
DISPATCH();
}
@ -828,7 +809,7 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
}
BYTECODE(SKIP_UNTIL_CHAR) {
int load_offset = (insn >> BYTECODE_SHIFT);
uint32_t advance = Load16Aligned(pc + 4);
int32_t advance = Load16AlignedSigned(pc + 4);
uint32_t c = Load16Aligned(pc + 6);
while (static_cast<uintptr_t>(current + load_offset) <
static_cast<uintptr_t>(subject.length())) {
@ -844,7 +825,7 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
}
BYTECODE(SKIP_UNTIL_CHAR_AND) {
int load_offset = (insn >> BYTECODE_SHIFT);
uint16_t advance = Load16Aligned(pc + 4);
int32_t advance = Load16AlignedSigned(pc + 4);
uint16_t c = Load16Aligned(pc + 6);
uint32_t mask = Load32Aligned(pc + 8);
int32_t maximum_offset = Load32Aligned(pc + 12);
@ -862,7 +843,7 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
}
BYTECODE(SKIP_UNTIL_CHAR_POS_CHECKED) {
int load_offset = (insn >> BYTECODE_SHIFT);
uint16_t advance = Load16Aligned(pc + 4);
int32_t advance = Load16AlignedSigned(pc + 4);
uint16_t c = Load16Aligned(pc + 6);
int32_t maximum_offset = Load32Aligned(pc + 8);
while (static_cast<uintptr_t>(current + maximum_offset) <=
@ -879,7 +860,7 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
}
BYTECODE(SKIP_UNTIL_BIT_IN_TABLE) {
int load_offset = (insn >> BYTECODE_SHIFT);
uint32_t advance = Load16Aligned(pc + 4);
int32_t advance = Load16AlignedSigned(pc + 4);
const byte* table = pc + 8;
while (static_cast<uintptr_t>(current + load_offset) <
static_cast<uintptr_t>(subject.length())) {
@ -895,7 +876,7 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
}
BYTECODE(SKIP_UNTIL_GT_OR_NOT_BIT_IN_TABLE) {
int load_offset = (insn >> BYTECODE_SHIFT);
uint16_t advance = Load16Aligned(pc + 4);
int32_t advance = Load16AlignedSigned(pc + 4);
uint16_t limit = Load16Aligned(pc + 6);
const byte* table = pc + 8;
while (static_cast<uintptr_t>(current + load_offset) <
@ -916,7 +897,7 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
}
BYTECODE(SKIP_UNTIL_CHAR_OR_CHAR) {
int load_offset = (insn >> BYTECODE_SHIFT);
uint32_t advance = Load32Aligned(pc + 4);
int32_t advance = Load32Aligned(pc + 4);
uint16_t c = Load16Aligned(pc + 8);
uint16_t c2 = Load16Aligned(pc + 10);
while (static_cast<uintptr_t>(current + load_offset) <
@ -1016,6 +997,8 @@ IrregexpInterpreter::Result IrregexpInterpreter::MatchInternal(
}
}
#ifndef COMPILING_IRREGEXP_FOR_EXTERNAL_EMBEDDER
// This method is called through an external reference from RegExpExecInternal
// builtin.
IrregexpInterpreter::Result IrregexpInterpreter::MatchForCallFromJs(
@ -1042,6 +1025,8 @@ IrregexpInterpreter::Result IrregexpInterpreter::MatchForCallFromJs(
start_position, call_origin);
}
#endif // !COMPILING_IRREGEXP_FOR_EXTERNAL_EMBEDDER
IrregexpInterpreter::Result IrregexpInterpreter::MatchForCallFromRuntime(
Isolate* isolate, Handle<JSRegExp> regexp, Handle<String> subject_string,
int* registers, int registers_length, int start_position) {

View file

@ -0,0 +1,291 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* 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/. */
// Copyright 2020 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.
// This file implements the NativeRegExpMacroAssembler interface for
// SpiderMonkey. It provides the same interface as each of V8's
// architecture-specific implementations.
#ifndef RegexpMacroAssemblerArch_h
#define RegexpMacroAssemblerArch_h
#include "jit/MacroAssembler.h"
#include "regexp/regexp-macro-assembler.h"
namespace v8 {
namespace internal {
struct FrameData {
// Character position at the start of the input, stored as a
// negative offset from the end of the string (input_end_pointer_).
size_t inputStart;
// The backtrack_stack_pointer_ register points to the top of the stack.
// This points to the bottom of the backtrack stack.
void* backtrackStackBase;
// Copy of the input MatchPairs.
int32_t* matches; // pointer to capture array
int32_t numMatches; // size of capture array
};
class SMRegExpMacroAssembler final : public NativeRegExpMacroAssembler {
public:
SMRegExpMacroAssembler(JSContext* cx, Isolate* isolate,
js::jit::StackMacroAssembler& masm, Zone* zone,
Mode mode, uint32_t num_capture_registers);
virtual ~SMRegExpMacroAssembler() {} // Nothing to do here
virtual int stack_limit_slack();
virtual IrregexpImplementation Implementation();
virtual bool Succeed();
virtual void Fail();
virtual void AdvanceCurrentPosition(int by);
virtual void PopCurrentPosition();
virtual void PushCurrentPosition();
virtual void SetCurrentPositionFromEnd(int by);
virtual void Backtrack();
virtual void Bind(Label* label);
virtual void GoTo(Label* label);
virtual void PushBacktrack(Label* label);
virtual void CheckCharacter(uint32_t c, Label* on_equal);
virtual void CheckNotCharacter(uint32_t c, Label* on_not_equal);
virtual void CheckCharacterGT(uc16 limit, Label* on_greater);
virtual void CheckCharacterLT(uc16 limit, Label* on_less);
virtual void CheckCharacterAfterAnd(uint32_t c, uint32_t mask,
Label* on_equal);
virtual void CheckNotCharacterAfterAnd(uint32_t c, uint32_t mask,
Label* on_not_equal);
virtual void CheckNotCharacterAfterMinusAnd(uc16 c, uc16 minus, uc16 mask,
Label* on_not_equal);
virtual void CheckGreedyLoop(Label* on_tos_equals_current_position);
virtual void CheckCharacterInRange(uc16 from, uc16 to, Label* on_in_range);
virtual void CheckCharacterNotInRange(uc16 from, uc16 to,
Label* on_not_in_range);
virtual void CheckAtStart(int cp_offset, Label* on_at_start);
virtual void CheckNotAtStart(int cp_offset, Label* on_not_at_start);
virtual void CheckPosition(int cp_offset, Label* on_outside_input);
virtual void CheckBitInTable(Handle<ByteArray> table, Label* on_bit_set);
virtual bool CheckSpecialCharacterClass(uc16 type, Label* on_no_match);
virtual void CheckNotBackReference(int start_reg, bool read_backward,
Label* on_no_match);
virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
bool read_backward,
Label* on_no_match);
virtual void LoadCurrentCharacterImpl(int cp_offset, Label* on_end_of_input,
bool check_bounds, int characters,
int eats_at_least);
virtual void AdvanceRegister(int reg, int by);
virtual void IfRegisterGE(int reg, int comparand, Label* if_ge);
virtual void IfRegisterLT(int reg, int comparand, Label* if_lt);
virtual void IfRegisterEqPos(int reg, Label* if_eq);
virtual void PopRegister(int register_index);
virtual void PushRegister(int register_index,
StackCheckFlag check_stack_limit);
virtual void ReadCurrentPositionFromRegister(int reg);
virtual void WriteCurrentPositionToRegister(int reg, int cp_offset);
virtual void ReadStackPointerFromRegister(int reg);
virtual void WriteStackPointerToRegister(int reg);
virtual void SetRegister(int register_index, int to);
virtual void ClearRegisters(int reg_from, int reg_to);
virtual Handle<HeapObject> GetCode(Handle<String> source);
private:
size_t frameSize_ = 0;
void createStackFrame();
void initFrameAndRegs();
void successHandler();
void exitHandler();
void backtrackHandler();
void stackOverflowHandler();
// Push a register on the backtrack stack.
void Push(js::jit::Register value);
// Pop a value from the backtrack stack.
void Pop(js::jit::Register target);
void CheckAtStartImpl(int cp_offset, Label* on_cond,
js::jit::Assembler::Condition cond);
void CheckCharacterImpl(js::jit::Imm32 c, Label* on_cond,
js::jit::Assembler::Condition cond);
void CheckCharacterAfterAndImpl(uint32_t c, uint32_t and_with, Label* on_cond,
bool negate);
void CheckCharacterInRangeImpl(uc16 from, uc16 to, Label* on_cond,
js::jit::Assembler::Condition cond);
void CheckNotBackReferenceImpl(int start_reg, bool read_backward,
Label* on_no_match, bool ignore_case);
void LoadCurrentCharacterUnchecked(int cp_offset, int characters);
void JumpOrBacktrack(Label* to);
// MacroAssembler methods that take a Label can be called with a
// null label, which means that we should backtrack if we would jump
// to that label. This is a helper to avoid writing out the same
// logic a dozen times.
inline js::jit::Label* LabelOrBacktrack(Label* to) {
return to ? to->inner() : &backtrack_label_;
}
void CheckBacktrackStackLimit();
static bool GrowBacktrackStack(RegExpStack* regexp_stack);
static uint32_t CaseInsensitiveCompareStrings(const char16_t* substring1,
const char16_t* substring2,
size_t byteLength);
static uint32_t CaseInsensitiveCompareUCStrings(const char16_t* substring1,
const char16_t* substring2,
size_t byteLength);
inline int char_size() { return static_cast<int>(mode_); }
inline js::jit::Scale factor() {
return mode_ == UC16 ? js::jit::TimesTwo : js::jit::TimesOne;
}
js::jit::Address inputStart() {
return js::jit::Address(masm_.getStackPointer(),
offsetof(FrameData, inputStart));
}
js::jit::Address backtrackStackBase() {
return js::jit::Address(masm_.getStackPointer(),
offsetof(FrameData, backtrackStackBase));
}
js::jit::Address matches() {
return js::jit::Address(masm_.getStackPointer(),
offsetof(FrameData, matches));
}
js::jit::Address numMatches() {
return js::jit::Address(masm_.getStackPointer(),
offsetof(FrameData, numMatches));
}
// The stack-pointer-relative location of a regexp register.
js::jit::Address register_location(int register_index) {
return js::jit::Address(masm_.getStackPointer(),
register_offset(register_index));
}
int32_t register_offset(int register_index) {
MOZ_ASSERT(register_index >= 0 && register_index <= kMaxRegister);
if (num_registers_ <= register_index) {
num_registers_ = register_index + 1;
}
static_assert(alignof(uintptr_t) <= alignof(FrameData));
return sizeof(FrameData) + register_index * sizeof(uintptr_t*);
}
JSContext* cx_;
js::jit::StackMacroAssembler& masm_;
/*
* This assembler uses the following registers:
*
* - current_character_:
* Contains the character (or characters) currently being examined.
* Must be loaded using LoadCurrentCharacter before using any of the
* dispatch methods. After a matching pass for a global regexp,
* temporarily stores the index of capture start.
* - current_position_:
* Current position in input *as negative byte offset from end of string*.
* - input_end_pointer_:
* Points to byte after last character in the input. current_position_ is
* relative to this.
* - backtrack_stack_pointer_:
* Points to tip of the (heap-allocated) backtrack stack. The stack grows
* downward (like the native stack).
* - temp0_, temp1_, temp2_:
* Scratch registers.
*
* The native stack pointer is used to access arguments (InputOutputData),
* local variables (FrameData), and irregexp's internal virtual registers
* (see register_location).
*/
js::jit::Register current_character_;
js::jit::Register current_position_;
js::jit::Register input_end_pointer_;
js::jit::Register backtrack_stack_pointer_;
js::jit::Register temp0_, temp1_, temp2_;
js::jit::Label entry_label_;
js::jit::Label start_label_;
js::jit::Label backtrack_label_;
js::jit::Label success_label_;
js::jit::Label exit_label_;
js::jit::Label stack_overflow_label_;
js::jit::Label exit_with_exception_label_;
// When we generate the code to push a backtrack label's address
// onto the backtrack stack, we don't know its final address. We
// have to patch it after linking. This is slightly delicate, as the
// Label itself (which is allocated on the stack) may not exist by
// the time we link. The approach is as follows:
//
// 1. When we push a label on the backtrack stack (PushBacktrack),
// we bind the label's patchOffset_ field to the offset within
// the code that should be overwritten. This works because each
// label is only pushed by a single instruction.
//
// 2. When we bind a label (Bind), we check to see if it has a
// bound patchOffset_. If it does, we create a LabelPatch mapping
// its patch offset to the offset of the label itself.
//
// 3. While linking the code, we walk the list of label patches
// and patch the code accordingly.
class LabelPatch {
public:
LabelPatch(js::jit::CodeOffset patchOffset, size_t labelOffset)
: patchOffset_(patchOffset), labelOffset_(labelOffset) {}
js::jit::CodeOffset patchOffset_;
size_t labelOffset_ = 0;
};
js::Vector<LabelPatch, 4, js::SystemAllocPolicy> labelPatches_;
void AddLabelPatch(js::jit::CodeOffset patchOffset, size_t labelOffset) {
js::AutoEnterOOMUnsafeRegion oomUnsafe;
if (!labelPatches_.emplaceBack(patchOffset, labelOffset)) {
oomUnsafe.crash("Irregexp label patch");
}
}
Mode mode_;
int num_registers_;
int num_capture_registers_;
js::jit::LiveGeneralRegisterSet savedRegisters_;
public:
using TableVector =
js::Vector<PseudoHandle<ByteArrayData>, 4, js::SystemAllocPolicy>;
TableVector& tables() { return tables_; }
private:
TableVector tables_;
void AddTable(PseudoHandle<ByteArrayData> table) {
js::AutoEnterOOMUnsafeRegion oomUnsafe;
if (!tables_.append(std::move(table))) {
oomUnsafe.crash("Irregexp table append");
}
}
};
} // namespace internal
} // namespace v8
#endif // RegexpMacroAssemblerArch_h

View file

@ -349,17 +349,15 @@ void RegExpMacroAssemblerTracer::CheckNotBackReference(int start_reg,
assembler_->CheckNotBackReference(start_reg, read_backward, on_no_match);
}
void RegExpMacroAssemblerTracer::CheckNotBackReferenceIgnoreCase(
int start_reg, bool read_backward, bool unicode, Label* on_no_match) {
PrintF(" CheckNotBackReferenceIgnoreCase(register=%d, %s %s, label[%08x]);\n",
int start_reg, bool read_backward, Label* on_no_match) {
PrintF(" CheckNotBackReferenceIgnoreCase(register=%d, %s, label[%08x]);\n",
start_reg, read_backward ? "backward" : "forward",
unicode ? "unicode" : "non-unicode", LabelToInt(on_no_match));
assembler_->CheckNotBackReferenceIgnoreCase(start_reg, read_backward, unicode,
LabelToInt(on_no_match));
assembler_->CheckNotBackReferenceIgnoreCase(start_reg, read_backward,
on_no_match);
}
void RegExpMacroAssemblerTracer::CheckPosition(int cp_offset,
Label* on_outside_input) {
PrintF(" CheckPosition(cp_offset=%d, label[%08x]);\n", cp_offset,

View file

@ -33,7 +33,6 @@ class RegExpMacroAssemblerTracer: public RegExpMacroAssembler {
void CheckNotBackReference(int start_reg, bool read_backward,
Label* on_no_match) override;
void CheckNotBackReferenceIgnoreCase(int start_reg, bool read_backward,
bool unicode,
Label* on_no_match) override;
void CheckNotCharacter(unsigned c, Label* on_not_equal) override;
void CheckNotCharacterAfterAnd(unsigned c, unsigned and_with,

View file

@ -110,34 +110,7 @@ bool NativeRegExpMacroAssembler::CanReadUnaligned() {
return FLAG_enable_regexp_unaligned_accesses && !slow_safe();
}
const byte* NativeRegExpMacroAssembler::StringCharacterPosition(
String subject, int start_index, const DisallowHeapAllocation& no_gc) {
if (subject.IsConsString()) {
subject = ConsString::cast(subject).first();
} else if (subject.IsSlicedString()) {
start_index += SlicedString::cast(subject).offset();
subject = SlicedString::cast(subject).parent();
}
if (subject.IsThinString()) {
subject = ThinString::cast(subject).actual();
}
DCHECK_LE(0, start_index);
DCHECK_LE(start_index, subject.length());
if (subject.IsSeqOneByteString()) {
return reinterpret_cast<const byte*>(
SeqOneByteString::cast(subject).GetChars(no_gc) + start_index);
} else if (subject.IsSeqTwoByteString()) {
return reinterpret_cast<const byte*>(
SeqTwoByteString::cast(subject).GetChars(no_gc) + start_index);
} else if (subject.IsExternalOneByteString()) {
return reinterpret_cast<const byte*>(
ExternalOneByteString::cast(subject).GetChars() + start_index);
} else {
DCHECK(subject.IsExternalTwoByteString());
return reinterpret_cast<const byte*>(
ExternalTwoByteString::cast(subject).GetChars() + start_index);
}
}
#ifndef COMPILING_IRREGEXP_FOR_EXTERNAL_EMBEDDER
// This method may only be called after an interrupt.
int NativeRegExpMacroAssembler::CheckStackGuardState(
@ -145,9 +118,10 @@ int NativeRegExpMacroAssembler::CheckStackGuardState(
Address* return_address, Code re_code, Address* subject,
const byte** input_start, const byte** input_end) {
DisallowHeapAllocation no_gc;
Address old_pc = PointerAuthentication::AuthenticatePC(return_address, 0);
DCHECK_LE(re_code.raw_instruction_start(), old_pc);
DCHECK_LE(old_pc, re_code.raw_instruction_end());
DCHECK(re_code.raw_instruction_start() <= *return_address);
DCHECK(*return_address <= re_code.raw_instruction_end());
StackLimitCheck check(isolate);
bool js_has_overflowed = check.JsHasOverflowed();
@ -189,9 +163,11 @@ int NativeRegExpMacroAssembler::CheckStackGuardState(
}
if (*code_handle != re_code) { // Return address no longer valid
intptr_t delta = code_handle->address() - re_code.address();
// Overwrite the return address on the stack.
*return_address += delta;
intptr_t delta = code_handle->address() - re_code.address();
Address new_pc = old_pc + delta;
// TODO(v8:10026): avoid replacing a signed pointer.
PointerAuthentication::ReplacePC(return_address, new_pc, 0);
}
// If we continue, we need to update the subject string addresses.
@ -206,8 +182,7 @@ int NativeRegExpMacroAssembler::CheckStackGuardState(
} else {
*subject = subject_handle->ptr();
intptr_t byte_length = *input_end - *input_start;
*input_start =
StringCharacterPosition(*subject_handle, start_index, no_gc);
*input_start = subject_handle->AddressOfCharacterAt(start_index, no_gc);
*input_end = *input_start + byte_length;
}
}
@ -255,7 +230,7 @@ int NativeRegExpMacroAssembler::Match(Handle<JSRegExp> regexp,
DisallowHeapAllocation no_gc;
const byte* input_start =
StringCharacterPosition(subject_ptr, start_offset + slice_offset, no_gc);
subject_ptr.AddressOfCharacterAt(start_offset + slice_offset, no_gc);
int byte_length = char_length << char_size_shift;
const byte* input_end = input_start + byte_length;
return Execute(*subject, start_offset, input_start, input_end, offsets_vector,
@ -301,6 +276,8 @@ int NativeRegExpMacroAssembler::Execute(
return result;
}
#endif // !COMPILING_IRREGEXP_FOR_EXTERNAL_EMBEDDER
// clang-format off
const byte NativeRegExpMacroAssembler::word_character_map[] = {
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,

View file

@ -6,6 +6,7 @@
#define V8_REGEXP_REGEXP_MACRO_ASSEMBLER_H_
#include "regexp/regexp-ast.h"
#include "regexp/regexp-shim.h"
#include "regexp/regexp.h"
namespace v8 {
@ -86,7 +87,7 @@ class RegExpMacroAssembler {
virtual void CheckNotBackReference(int start_reg, bool read_backward,
Label* on_no_match) = 0;
virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
bool read_backward, bool unicode,
bool read_backward,
Label* on_no_match) = 0;
// Check the current character for a match with a literal character. If we
// fail to match then goto the on_failure label. End of input always
@ -121,6 +122,11 @@ class RegExpMacroAssembler {
// not have custom support.
// May clobber the current loaded character.
virtual bool CheckSpecialCharacterClass(uc16 type, Label* on_no_match);
// Control-flow integrity:
// Define a jump target and bind a label.
virtual void BindJumpTarget(Label* label) { Bind(label); }
virtual void Fail() = 0;
virtual Handle<HeapObject> GetCode(Handle<String> source) = 0;
virtual void GoTo(Label* label) = 0;
@ -245,9 +251,6 @@ class NativeRegExpMacroAssembler: public RegExpMacroAssembler {
static Address GrowStack(Address stack_pointer, Address* stack_top,
Isolate* isolate);
static const byte* StringCharacterPosition(
String subject, int start_index, const DisallowHeapAllocation& no_gc);
static int CheckStackGuardState(Isolate* isolate, int start_index,
RegExp::CallOrigin call_origin,
Address* return_address, Code re_code,

File diff suppressed because it is too large Load diff

View file

@ -17,11 +17,10 @@
namespace v8 {
namespace internal {
RegExpParser::RegExpParser(FlatStringReader* in, Handle<String>* error,
JSRegExp::Flags flags, Isolate* isolate, Zone* zone)
RegExpParser::RegExpParser(FlatStringReader* in, JSRegExp::Flags flags,
Isolate* isolate, Zone* zone)
: isolate_(isolate),
zone_(zone),
error_(error),
captures_(nullptr),
named_captures_(nullptr),
named_back_references_(nullptr),
@ -74,13 +73,12 @@ void RegExpParser::Advance() {
if (FLAG_correctness_fuzzer_suppressions) {
FATAL("Aborting on stack overflow");
}
ReportError(CStrVector(
MessageFormatter::TemplateString(MessageTemplate::kStackOverflow)));
ReportError(RegExpError::kStackOverflow);
} else if (zone()->excess_allocation()) {
if (FLAG_correctness_fuzzer_suppressions) {
FATAL("Aborting on excess zone allocation");
}
ReportError(CStrVector("Regular expression too large"));
ReportError(RegExpError::kTooLarge);
} else {
current_ = ReadNext<true>();
}
@ -132,15 +130,12 @@ bool RegExpParser::IsSyntaxCharacterOrSlash(uc32 c) {
return false;
}
RegExpTree* RegExpParser::ReportError(Vector<const char> message) {
RegExpTree* RegExpParser::ReportError(RegExpError error) {
if (failed_) return nullptr; // Do not overwrite any existing error.
failed_ = true;
*error_ = isolate()
->factory()
->NewStringFromOneByte(Vector<const uint8_t>::cast(message))
.ToHandleChecked();
// Zip to the end to make sure the no more input is read.
error_ = error;
error_pos_ = position();
// Zip to the end to make sure no more input is read.
current_ = kEndMarker;
next_pos_ = in()->length();
return nullptr;
@ -187,14 +182,14 @@ RegExpTree* RegExpParser::ParseDisjunction() {
case kEndMarker:
if (state->IsSubexpression()) {
// Inside a parenthesized group when hitting end of input.
return ReportError(CStrVector("Unterminated group"));
return ReportError(RegExpError::kUnterminatedGroup);
}
DCHECK_EQ(INITIAL, state->group_type());
// Parsing completed successfully.
return builder->ToRegExp();
case ')': {
if (!state->IsSubexpression()) {
return ReportError(CStrVector("Unmatched ')'"));
return ReportError(RegExpError::kUnmatchedParen);
}
DCHECK_NE(INITIAL, state->group_type());
@ -245,7 +240,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
case '*':
case '+':
case '?':
return ReportError(CStrVector("Nothing to repeat"));
return ReportError(RegExpError::kNothingToRepeat);
case '^': {
Advance();
if (builder->multiline()) {
@ -300,7 +295,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
case '\\':
switch (Next()) {
case kEndMarker:
return ReportError(CStrVector("\\ at end of pattern"));
return ReportError(RegExpError::kEscapeAtEndOfPattern);
case 'b':
Advance(2);
builder->AddAssertion(new (zone()) RegExpAssertion(
@ -340,7 +335,8 @@ RegExpTree* RegExpParser::ParseDisjunction() {
if (unicode()) {
ZoneList<CharacterRange>* ranges =
new (zone()) ZoneList<CharacterRange>(2, zone());
std::vector<char> name_1, name_2;
ZoneVector<char> name_1(zone());
ZoneVector<char> name_2(zone());
if (ParsePropertyClassName(&name_1, &name_2)) {
if (AddPropertyClassRange(ranges, p == 'P', name_1, name_2)) {
RegExpCharacterClass* cc = new (zone())
@ -356,7 +352,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
}
}
}
return ReportError(CStrVector("Invalid property name"));
return ReportError(RegExpError::kInvalidPropertyName);
} else {
builder->AddCharacter(p);
}
@ -392,7 +388,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
// With /u, no identity escapes except for syntax characters
// are allowed. Otherwise, all identity escapes are allowed.
if (unicode()) {
return ReportError(CStrVector("Invalid escape"));
return ReportError(RegExpError::kInvalidEscape);
}
uc32 first_digit = Next();
if (first_digit == '8' || first_digit == '9') {
@ -406,7 +402,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
Advance();
if (unicode() && Next() >= '0' && Next() <= '9') {
// With /u, decimal escape with leading 0 are not parsed as octal.
return ReportError(CStrVector("Invalid decimal escape"));
return ReportError(RegExpError::kInvalidDecimalEscape);
}
uc32 octal = ParseOctalLiteral();
builder->AddCharacter(octal);
@ -447,7 +443,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
// ES#prod-annexB-ExtendedPatternCharacter
if (unicode()) {
// With /u, invalid escapes are not treated as identity escapes.
return ReportError(CStrVector("Invalid unicode escape"));
return ReportError(RegExpError::kInvalidUnicodeEscape);
}
builder->AddCharacter('\\');
} else {
@ -465,7 +461,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
builder->AddCharacter('x');
} else {
// With /u, invalid escapes are not treated as identity escapes.
return ReportError(CStrVector("Invalid escape"));
return ReportError(RegExpError::kInvalidEscape);
}
break;
}
@ -478,7 +474,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
builder->AddCharacter('u');
} else {
// With /u, invalid escapes are not treated as identity escapes.
return ReportError(CStrVector("Invalid Unicode escape"));
return ReportError(RegExpError::kInvalidUnicodeEscape);
}
break;
}
@ -502,7 +498,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
builder->AddCharacter(current());
Advance();
} else {
return ReportError(CStrVector("Invalid escape"));
return ReportError(RegExpError::kInvalidEscape);
}
break;
}
@ -510,13 +506,13 @@ RegExpTree* RegExpParser::ParseDisjunction() {
case '{': {
int dummy;
bool parsed = ParseIntervalQuantifier(&dummy, &dummy CHECK_FAILED);
if (parsed) return ReportError(CStrVector("Nothing to repeat"));
if (parsed) return ReportError(RegExpError::kNothingToRepeat);
V8_FALLTHROUGH;
}
case '}':
case ']':
if (unicode()) {
return ReportError(CStrVector("Lone quantifier brackets"));
return ReportError(RegExpError::kLoneQuantifierBrackets);
}
V8_FALLTHROUGH;
default:
@ -551,13 +547,12 @@ RegExpTree* RegExpParser::ParseDisjunction() {
case '{':
if (ParseIntervalQuantifier(&min, &max)) {
if (max < min) {
return ReportError(
CStrVector("numbers out of order in {} quantifier"));
return ReportError(RegExpError::kRangeOutOfOrder);
}
break;
} else if (unicode()) {
// With /u, incomplete quantifiers are not allowed.
return ReportError(CStrVector("Incomplete quantifier"));
return ReportError(RegExpError::kIncompleteQuantifier);
}
continue;
default:
@ -573,7 +568,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
Advance();
}
if (!builder->AddQuantifierToAtom(min, max, quantifier_type)) {
return ReportError(CStrVector("Invalid quantifier"));
return ReportError(RegExpError::kInvalidQuantifier);
}
}
}
@ -608,7 +603,7 @@ RegExpParser::RegExpParserState* RegExpParser::ParseOpenParenthesis(
case 's':
case 'm': {
if (!FLAG_regexp_mode_modifiers) {
ReportError(CStrVector("Invalid group"));
ReportError(RegExpError::kInvalidGroup);
return nullptr;
}
Advance();
@ -617,7 +612,7 @@ RegExpParser::RegExpParserState* RegExpParser::ParseOpenParenthesis(
switch (current()) {
case '-':
if (!flags_sense) {
ReportError(CStrVector("Multiple dashes in flag group"));
ReportError(RegExpError::kMultipleFlagDashes);
return nullptr;
}
flags_sense = false;
@ -631,7 +626,7 @@ RegExpParser::RegExpParserState* RegExpParser::ParseOpenParenthesis(
if (current() == 'm') bit = JSRegExp::kMultiline;
if (current() == 's') bit = JSRegExp::kDotAll;
if (((switch_on | switch_off) & bit) != 0) {
ReportError(CStrVector("Repeated flag in flag group"));
ReportError(RegExpError::kRepeatedFlag);
return nullptr;
}
if (flags_sense) {
@ -659,7 +654,7 @@ RegExpParser::RegExpParserState* RegExpParser::ParseOpenParenthesis(
subexpr_type = GROUPING; // Will break us out of the outer loop.
continue;
default:
ReportError(CStrVector("Invalid flag group"));
ReportError(RegExpError::kInvalidFlagGroup);
return nullptr;
}
}
@ -683,13 +678,13 @@ RegExpParser::RegExpParserState* RegExpParser::ParseOpenParenthesis(
Advance();
break;
default:
ReportError(CStrVector("Invalid group"));
ReportError(RegExpError::kInvalidGroup);
return nullptr;
}
}
if (subexpr_type == CAPTURE) {
if (captures_started_ >= JSRegExp::kMaxCaptures) {
ReportError(CStrVector("Too many captures"));
ReportError(RegExpError::kTooManyCaptures);
return nullptr;
}
captures_started_++;
@ -838,20 +833,20 @@ const ZoneVector<uc16>* RegExpParser::ParseCaptureGroupName() {
if (c == '\\' && current() == 'u') {
Advance();
if (!ParseUnicodeEscape(&c)) {
ReportError(CStrVector("Invalid Unicode escape sequence"));
ReportError(RegExpError::kInvalidUnicodeEscape);
return nullptr;
}
}
// The backslash char is misclassified as both ID_Start and ID_Continue.
if (c == '\\') {
ReportError(CStrVector("Invalid capture group name"));
ReportError(RegExpError::kInvalidCaptureGroupName);
return nullptr;
}
if (at_start) {
if (!IsIdentifierStart(c)) {
ReportError(CStrVector("Invalid capture group name"));
ReportError(RegExpError::kInvalidCaptureGroupName);
return nullptr;
}
push_code_unit(name, c);
@ -862,7 +857,7 @@ const ZoneVector<uc16>* RegExpParser::ParseCaptureGroupName() {
} else if (IsIdentifierPart(c)) {
push_code_unit(name, c);
} else {
ReportError(CStrVector("Invalid capture group name"));
ReportError(RegExpError::kInvalidCaptureGroupName);
return nullptr;
}
}
@ -889,7 +884,7 @@ bool RegExpParser::CreateNamedCaptureAtIndex(const ZoneVector<uc16>* name,
const auto& named_capture_it = named_captures_->find(capture);
if (named_capture_it != named_captures_->end()) {
ReportError(CStrVector("Duplicate capture group name"));
ReportError(RegExpError::kDuplicateCaptureGroupName);
return false;
}
}
@ -903,7 +898,7 @@ bool RegExpParser::ParseNamedBackReference(RegExpBuilder* builder,
RegExpParserState* state) {
// The parser is assumed to be on the '<' in \k<name>.
if (current() != '<') {
ReportError(CStrVector("Invalid named reference"));
ReportError(RegExpError::kInvalidNamedReference);
return false;
}
@ -936,7 +931,7 @@ void RegExpParser::PatchNamedBackReferences() {
if (named_back_references_ == nullptr) return;
if (named_captures_ == nullptr) {
ReportError(CStrVector("Invalid named capture referenced"));
ReportError(RegExpError::kInvalidNamedCaptureReference);
return;
}
@ -957,7 +952,7 @@ void RegExpParser::PatchNamedBackReferences() {
if (capture_it != named_captures_->end()) {
index = (*capture_it)->index();
} else {
ReportError(CStrVector("Invalid named capture referenced"));
ReportError(RegExpError::kInvalidNamedCaptureReference);
return;
}
@ -1378,8 +1373,8 @@ bool IsUnicodePropertyValueCharacter(char c) {
} // anonymous namespace
bool RegExpParser::ParsePropertyClassName(std::vector<char>* name_1,
std::vector<char>* name_2) {
bool RegExpParser::ParsePropertyClassName(ZoneVector<char>* name_1,
ZoneVector<char>* name_2) {
DCHECK(name_1->empty());
DCHECK(name_2->empty());
// Parse the property class as follows:
@ -1418,8 +1413,8 @@ bool RegExpParser::ParsePropertyClassName(std::vector<char>* name_1,
bool RegExpParser::AddPropertyClassRange(ZoneList<CharacterRange>* add_to,
bool negate,
const std::vector<char>& name_1,
const std::vector<char>& name_2) {
const ZoneVector<char>& name_1,
const ZoneVector<char>& name_2) {
if (name_2.empty()) {
// First attempt to interpret as general category property value name.
const char* name = name_1.data();
@ -1456,7 +1451,7 @@ bool RegExpParser::AddPropertyClassRange(ZoneList<CharacterRange>* add_to,
}
}
RegExpTree* RegExpParser::GetPropertySequence(const std::vector<char>& name_1) {
RegExpTree* RegExpParser::GetPropertySequence(const ZoneVector<char>& name_1) {
if (!FLAG_harmony_regexp_sequence) return nullptr;
const char* name = name_1.data();
const uc32* sequence_list = nullptr;
@ -1522,19 +1517,19 @@ RegExpTree* RegExpParser::GetPropertySequence(const std::vector<char>& name_1) {
#else // V8_INTL_SUPPORT
bool RegExpParser::ParsePropertyClassName(std::vector<char>* name_1,
std::vector<char>* name_2) {
bool RegExpParser::ParsePropertyClassName(ZoneVector<char>* name_1,
ZoneVector<char>* name_2) {
return false;
}
bool RegExpParser::AddPropertyClassRange(ZoneList<CharacterRange>* add_to,
bool negate,
const std::vector<char>& name_1,
const std::vector<char>& name_2) {
const ZoneVector<char>& name_1,
const ZoneVector<char>& name_2) {
return false;
}
RegExpTree* RegExpParser::GetPropertySequence(const std::vector<char>& name) {
RegExpTree* RegExpParser::GetPropertySequence(const ZoneVector<char>& name) {
return nullptr;
}
@ -1598,7 +1593,7 @@ uc32 RegExpParser::ParseClassCharacterEscape() {
}
if (unicode()) {
// With /u, invalid escapes are not treated as identity escapes.
ReportError(CStrVector("Invalid class escape"));
ReportError(RegExpError::kInvalidClassEscape);
return 0;
}
if ((controlLetter >= '0' && controlLetter <= '9') ||
@ -1631,7 +1626,7 @@ uc32 RegExpParser::ParseClassCharacterEscape() {
// ES#prod-annexB-LegacyOctalEscapeSequence
if (unicode()) {
// With /u, decimal escape is not interpreted as octal character code.
ReportError(CStrVector("Invalid class escape"));
ReportError(RegExpError::kInvalidClassEscape);
return 0;
}
return ParseOctalLiteral();
@ -1641,7 +1636,7 @@ uc32 RegExpParser::ParseClassCharacterEscape() {
if (ParseHexEscape(2, &value)) return value;
if (unicode()) {
// With /u, invalid escapes are not treated as identity escapes.
ReportError(CStrVector("Invalid escape"));
ReportError(RegExpError::kInvalidEscape);
return 0;
}
// If \x is not followed by a two-digit hexadecimal, treat it
@ -1654,7 +1649,7 @@ uc32 RegExpParser::ParseClassCharacterEscape() {
if (ParseUnicodeEscape(&value)) return value;
if (unicode()) {
// With /u, invalid escapes are not treated as identity escapes.
ReportError(CStrVector("Invalid unicode escape"));
ReportError(RegExpError::kInvalidUnicodeEscape);
return 0;
}
// If \u is not followed by a two-digit hexadecimal, treat it
@ -1669,11 +1664,11 @@ uc32 RegExpParser::ParseClassCharacterEscape() {
Advance();
return result;
}
ReportError(CStrVector("Invalid escape"));
ReportError(RegExpError::kInvalidEscape);
return 0;
}
}
return 0;
UNREACHABLE();
}
void RegExpParser::ParseClassEscape(ZoneList<CharacterRange>* ranges,
@ -1696,17 +1691,18 @@ void RegExpParser::ParseClassEscape(ZoneList<CharacterRange>* ranges,
return;
}
case kEndMarker:
ReportError(CStrVector("\\ at end of pattern"));
ReportError(RegExpError::kEscapeAtEndOfPattern);
return;
case 'p':
case 'P':
if (unicode()) {
bool negate = Next() == 'P';
Advance(2);
std::vector<char> name_1, name_2;
ZoneVector<char> name_1(zone);
ZoneVector<char> name_2(zone);
if (!ParsePropertyClassName(&name_1, &name_2) ||
!AddPropertyClassRange(ranges, negate, name_1, name_2)) {
ReportError(CStrVector("Invalid property name in character class"));
ReportError(RegExpError::kInvalidClassPropertyName);
}
*is_class_escape = true;
return;
@ -1725,10 +1721,6 @@ void RegExpParser::ParseClassEscape(ZoneList<CharacterRange>* ranges,
}
RegExpTree* RegExpParser::ParseCharacterClass(const RegExpBuilder* builder) {
static const char* kUnterminated = "Unterminated character class";
static const char* kRangeInvalid = "Invalid character class";
static const char* kRangeOutOfOrder = "Range out of order in character class";
DCHECK_EQ(current(), '[');
Advance();
bool is_negated = false;
@ -1761,7 +1753,7 @@ RegExpTree* RegExpParser::ParseCharacterClass(const RegExpBuilder* builder) {
// Either end is an escaped character class. Treat the '-' verbatim.
if (unicode()) {
// ES2015 21.2.2.15.1 step 1.
return ReportError(CStrVector(kRangeInvalid));
return ReportError(RegExpError::kInvalidCharacterClass);
}
if (!is_class_1) ranges->Add(CharacterRange::Singleton(char_1), zone());
ranges->Add(CharacterRange::Singleton('-'), zone());
@ -1770,7 +1762,7 @@ RegExpTree* RegExpParser::ParseCharacterClass(const RegExpBuilder* builder) {
}
// ES2015 21.2.2.15.1 step 6.
if (char_1 > char_2) {
return ReportError(CStrVector(kRangeOutOfOrder));
return ReportError(RegExpError::kOutOfOrderCharacterClass);
}
ranges->Add(CharacterRange::Range(char_1, char_2), zone());
} else {
@ -1778,7 +1770,7 @@ RegExpTree* RegExpParser::ParseCharacterClass(const RegExpBuilder* builder) {
}
}
if (!has_more()) {
return ReportError(CStrVector(kUnterminated));
return ReportError(RegExpError::kUnterminatedCharacterClass);
}
Advance();
RegExpCharacterClass::CharacterClassFlags character_class_flags;
@ -1795,14 +1787,16 @@ bool RegExpParser::ParseRegExp(Isolate* isolate, Zone* zone,
FlatStringReader* input, JSRegExp::Flags flags,
RegExpCompileData* result) {
DCHECK(result != nullptr);
RegExpParser parser(input, &result->error, flags, isolate, zone);
RegExpParser parser(input, flags, isolate, zone);
RegExpTree* tree = parser.ParsePattern();
if (parser.failed()) {
DCHECK(tree == nullptr);
DCHECK(!result->error.is_null());
DCHECK(parser.error_ != RegExpError::kNone);
result->error = parser.error_;
result->error_pos = parser.error_pos_;
} else {
DCHECK(tree != nullptr);
DCHECK(result->error.is_null());
DCHECK(parser.error_ == RegExpError::kNone);
if (FLAG_trace_regexp_parser) {
StdoutStream os;
tree->Print(os, zone);

View file

@ -6,6 +6,7 @@
#define V8_REGEXP_REGEXP_PARSER_H_
#include "regexp/regexp-ast.h"
#include "regexp/regexp-error.h"
namespace v8 {
namespace internal {
@ -150,8 +151,8 @@ class RegExpBuilder : public ZoneObject {
class V8_EXPORT_PRIVATE RegExpParser {
public:
RegExpParser(FlatStringReader* in, Handle<String>* error,
JSRegExp::Flags flags, Isolate* isolate, Zone* zone);
RegExpParser(FlatStringReader* in, JSRegExp::Flags flags, Isolate* isolate,
Zone* zone);
static bool ParseRegExp(Isolate* isolate, Zone* zone, FlatStringReader* input,
JSRegExp::Flags flags, RegExpCompileData* result);
@ -174,13 +175,13 @@ class V8_EXPORT_PRIVATE RegExpParser {
bool ParseUnicodeEscape(uc32* value);
bool ParseUnlimitedLengthHexNumber(int max_value, uc32* value);
bool ParsePropertyClassName(std::vector<char>* name_1,
std::vector<char>* name_2);
bool ParsePropertyClassName(ZoneVector<char>* name_1,
ZoneVector<char>* name_2);
bool AddPropertyClassRange(ZoneList<CharacterRange>* add_to, bool negate,
const std::vector<char>& name_1,
const std::vector<char>& name_2);
const ZoneVector<char>& name_1,
const ZoneVector<char>& name_2);
RegExpTree* GetPropertySequence(const std::vector<char>& name_1);
RegExpTree* GetPropertySequence(const ZoneVector<char>& name_1);
RegExpTree* ParseCharacterClass(const RegExpBuilder* state);
uc32 ParseOctalLiteral();
@ -199,7 +200,7 @@ class V8_EXPORT_PRIVATE RegExpParser {
char ParseClassEscape();
RegExpTree* ReportError(Vector<const char> message);
RegExpTree* ReportError(RegExpError error);
void Advance();
void Advance(int dist);
void Reset(int pos);
@ -332,7 +333,8 @@ class V8_EXPORT_PRIVATE RegExpParser {
Isolate* isolate_;
Zone* zone_;
Handle<String>* error_;
RegExpError error_ = RegExpError::kNone;
int error_pos_ = 0;
ZoneList<RegExpCapture*>* captures_;
ZoneSet<RegExpCapture*, RegExpCaptureNameLess>* named_captures_;
ZoneList<RegExpBackReference*>* named_back_references_;

View file

@ -0,0 +1,212 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* 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/. */
// 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.
#include <iostream>
#include "regexp/regexp-shim.h"
#include "regexp/regexp-stack.h"
namespace v8 {
namespace internal {
void PrintF(const char* format, ...) {
va_list arguments;
va_start(arguments, format);
vprintf(format, arguments);
va_end(arguments);
}
void PrintF(FILE* out, const char* format, ...) {
va_list arguments;
va_start(arguments, format);
vfprintf(out, format, arguments);
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) {
uc16 v = c.value;
bool isPrint = 0x20 < v && v <= 0x7e;
char buf[10];
const char* format = isPrint ? "%c" : (v <= 0xFF) ? "\\x%02x" : "\\u%04x";
SprintfLiteral(buf, format, v);
return os << buf;
}
std::ostream& operator<<(std::ostream& os, const AsUC32& c) {
int32_t v = c.value;
if (v <= String::kMaxUtf16CodeUnit) {
return os << AsUC16(v);
}
char buf[13];
SprintfLiteral(buf, "\\u{%06x}", v);
return os << buf;
}
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<HeapObject>::Handle(JS::Value v, Isolate* isolate);
template Handle<JSRegExp>::Handle(JSRegExp re, 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[]>();
}
byte* Isolate::top_of_regexp_stack() const {
return reinterpret_cast<byte*>(regexpStack_->memory_top_address_address());
}
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;
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 = 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;
bool FLAG_trace_regexp_bytecodes = false;
bool FLAG_trace_regexp_parser = false;
bool FLAG_trace_regexp_peephole_optimization = false;
} // namespace internal
} // namespace v8

1181
js/src/regexp/regexp-shim.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -5,6 +5,7 @@
#ifndef V8_REGEXP_REGEXP_STACK_H_
#define V8_REGEXP_REGEXP_STACK_H_
#include "regexp/regexp-shim.h"
namespace v8 {
namespace internal {
@ -35,6 +36,9 @@ class RegExpStackScope {
class RegExpStack {
public:
RegExpStack();
~RegExpStack();
// Number of allocated locations on the stack below the limit.
// No sequence of pushes must be longer that this without doing a stack-limit
// check.
@ -74,9 +78,6 @@ class RegExpStack {
static constexpr size_t kMaximumStackSize = 64 * MB;
private:
RegExpStack();
~RegExpStack();
// Artificial limit used when the thread-local state has been destroyed.
static const Address kMemoryTop =
static_cast<Address>(static_cast<uintptr_t>(-1));

View file

@ -5,6 +5,8 @@
#ifndef V8_REGEXP_REGEXP_H_
#define V8_REGEXP_REGEXP_H_
#include "regexp/regexp-error.h"
#include "regexp/regexp-shim.h"
namespace v8 {
namespace internal {
@ -41,7 +43,11 @@ struct RegExpCompileData {
// The error message. Only used if an error occurred during parsing or
// compilation.
Handle<String> error;
RegExpError error = RegExpError::kNone;
// The position at which the error was detected. Only used if an
// error occurred.
int error_pos = 0;
// The number of capture groups, without the global capture \0.
int capture_count = 0;

View file

@ -0,0 +1,88 @@
// Copyright 2020 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 UnicodeSets
// for special cases where the case-folding algorithm used by
// UnicodeSet::closeOver(USET_CASE_INSENSITIVE) does not match
// the algorithm defined in ECMAScript 2020 21.2.2.8.2 (Runtime
// Semantics: Canonicalize) step 3.
#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(0xdf);
set.add(0x17f);
set.add(0x390);
set.add(0x3b0);
set.add(0x3f4);
set.add(0x1e9e);
set.add(0x1f80, 0x1faf);
set.add(0x1fb3);
set.add(0x1fbc);
set.add(0x1fc3);
set.add(0x1fcc);
set.add(0x1fd3);
set.add(0x1fe3);
set.add(0x1ff3);
set.add(0x1ffc);
set.add(0x2126);
set.add(0x212a, 0x212b);
set.add(0xfb05, 0xfb06);
set.freeze();
return set;
}
struct IgnoreSetData {
IgnoreSetData() : set(BuildIgnoreSet()) {}
const icu::UnicodeSet set;
};
//static
const icu::UnicodeSet& RegExpCaseFolding::IgnoreSet() {
static base::LazyInstance<IgnoreSetData>::type set =
LAZY_INSTANCE_INITIALIZER;
return set.Pointer()->set;
}
icu::UnicodeSet BuildSpecialAddSet() {
icu::UnicodeSet set;
set.add(0x4b);
set.add(0x53);
set.add(0x6b);
set.add(0x73);
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;
}
struct SpecialAddSetData {
SpecialAddSetData() : set(BuildSpecialAddSet()) {}
const icu::UnicodeSet set;
};
//static
const icu::UnicodeSet& RegExpCaseFolding::SpecialAddSet() {
static base::LazyInstance<SpecialAddSetData>::type set =
LAZY_INSTANCE_INITIALIZER;
return set.Pointer()->set;
}
} // namespace internal
} // namespace v8
#endif // V8_INTL_SUPPORT

View file

@ -6,70 +6,108 @@
#define V8_REGEXP_SPECIAL_CASE_H_
#ifdef V8_INTL_SUPPORT
#include "unicode/uversion.h"
namespace U_ICU_NAMESPACE {
class UnicodeSet;
} // namespace U_ICU_NAMESPACE
#include "regexp/regexp-shim.h"
#include "unicode/uchar.h"
#include "unicode/uniset.h"
#include "unicode/unistr.h"
namespace v8 {
namespace internal {
// Functions to build special sets of Unicode characters that need special
// handling under "i" mode that cannot use closeOver(USET_CASE_INSENSITIVE).
//
// For the characters in the "ignore set", the process should not treat other
// characters in the result of closeOver(USET_CASE_INSENSITIVE) as case
// equivlant under the ECMA262 RegExp "i" mode because these characters are
// uppercase themselves that no other characters in the set uppercase to.
//
// For the characters in the "special add set", the proecess should add only
// those characters in the result of closeOver(USET_CASE_INSENSITIVE) which is
// not uppercase characters as case equivlant under the ECMA262 RegExp "i" mode
// and also that ONE uppercase character that other non uppercase character
// uppercase into to the set. Other uppercase characters in the result of
// closeOver(USET_CASE_INSENSITIVE) should not be considered because ECMA262
// RegExp "i" mode consider two characters as "case equivlant" if both
// characters uppercase to the same character.
//
// For example, consider the following case equivalent set defined by Unicode
// standard. Notice there are more than one uppercase characters in this set:
// U+212B Å Angstrom Sign - an uppercase character.
// U+00C5 Å Latin Capital Letter A with Ring Above - an uppercase character.
// U+00E5 å Latin Small Letter A with Ring Above - a lowercase character which
// uppercase to U+00C5.
// In this case equivlant set is a special set and need special handling while
// considering "case equivlant" under the ECMA262 RegExp "i" mode which is
// different than Unicode Standard:
// * U+212B should be included into the "ignore" set because there are no other
// characters, under the ECMA262 "i" mode, are considered as "case equivlant"
// to it because U+212B is itself an uppercase but neither U+00C5 nor U+00E5
// uppercase to U+212B.
// * U+00C5 and U+00E5 will both be included into the "special add" set. While
// calculate the "equivlant set" under ECMA262 "i" mode, the process will
// add U+00E5, because it is not an uppercase character in the set. The
// process will also add U+00C5, because it is the uppercase character which
// other non uppercase character, U+00C5, uppercase into.
//
// For characters not included in "ignore set" and "special add set", the
// process will just use closeOver(USET_CASE_INSENSITIVE) to calcualte, which is
// much faster.
//
// Under Unicode 12.0, there are only 7 characters in the "special add set" and
// 4 characters in "ignore set" so even the special add process is slower, it is
// limited to a small set of cases only.
//
// The implementation of these two function will be generated by calling ICU
// icu::UnicodeSet during the build time into gen/src/regexp/special-case.cc by
// the code in src/regexp/gen-regexp-special-case.cc.
//
// These two function will be used with LazyInstance<> template to generate
// global sharable set to reduce memory usage and speed up performance.
// Sets of Unicode characters that need special handling under "i" mode
// Function to build and return the Ignore set.
icu::UnicodeSet BuildIgnoreSet();
// For non-unicode ignoreCase matches (aka "i", not "iu"), ECMA 262
// defines slightly different case-folding rules than Unicode. An
// input character should match a pattern character if the result of
// the Canonicalize algorithm is the same for both characters.
//
// Roughly speaking, for "i" regexps, Canonicalize(c) is the same as
// c.toUpperCase(), unless a) c.toUpperCase() is a multi-character
// string, or b) c is non-ASCII, and c.toUpperCase() is ASCII. See
// https://tc39.es/ecma262/#sec-runtime-semantics-canonicalize-ch for
// the precise definition.
//
// While compiling such regular expressions, we need to compute the
// set of characters that should match a given input character. (See
// GetCaseIndependentLetters and CharacterRange::AddCaseEquivalents.)
// For almost all characters, this can be efficiently computed using
// UnicodeSet::closeOver(USET_CASE_INSENSITIVE). These sets represent
// the remaining special cases.
//
// For a character c, the rules are as follows:
//
// 1. If c is in neither IgnoreSet nor SpecialAddSet, then calling
// UnicodeSet::closeOver(USET_CASE_INSENSITIVE) on a UnicodeSet
// containing c will produce the set of characters that should
// match /c/i (or /[c]/i), and only those characters.
//
// 2. If c is in IgnoreSet, then the only character it should match is
// itself. However, closeOver will add additional incorrect
// matches. For example, consider SHARP S: 'ß' (U+00DF) and 'ẞ'
// (U+1E9E). Although closeOver('ß') = "ßẞ", uppercase('ß') is
// "SS". Step 3.e therefore requires that 'ß' canonicalizes to
// itself, and should not match 'ẞ'. In these cases, we can skip
// the closeOver entirely, because it will never add an equivalent
// character.
//
// 3. If c is in SpecialAddSet, then it should match at least one
// character other than itself. However, closeOver will add at
// least one additional incorrect match. For example, consider the
// letter 'k'. Closing over 'k' gives "kK" (lowercase k, uppercase
// K, U+212A KELVIN SIGN). However, because of step 3.g, KELVIN
// SIGN should not match either of the other two characters. As a
// result, "k" and "K" are in SpecialAddSet (and KELVIN SIGN is in
// IgnoreSet). To find the correct matches for characters in
// SpecialAddSet, we closeOver the original character, but filter
// out the results that do not have the same canonical value.
//
// The contents of these sets are calculated at build time by
// src/regexp/gen-regexp-special-case.cc, which generates
// gen/src/regexp/special-case.cc. This is done by iterating over the
// result of closeOver for each BMP character, and finding sets for
// which at least one character has a different canonical value than
// another character. Characters that match no other characters in
// their equivalence class are added to IgnoreSet. Characters that
// match at least one other character are added to SpecialAddSet.
// Function to build and return the Special Add set.
icu::UnicodeSet BuildSpecialAddSet();
class RegExpCaseFolding final : public AllStatic {
public:
static const icu::UnicodeSet& IgnoreSet();
static const icu::UnicodeSet& SpecialAddSet();
// This implements ECMAScript 2020 21.2.2.8.2 (Runtime Semantics:
// Canonicalize) step 3, which is used to determine whether
// characters match when ignoreCase is true and unicode is false.
static UChar32 Canonicalize(UChar32 ch) {
// a. Assert: ch is a UTF-16 code unit.
CHECK_LE(ch, 0xffff);
// b. Let s be the String value consisting of the single code unit ch.
icu::UnicodeString s(ch);
// c. Let u be the same result produced as if by performing the algorithm
// for String.prototype.toUpperCase using s as the this value.
// d. Assert: Type(u) is String.
icu::UnicodeString& u = s.toUpper();
// e. If u does not consist of a single code unit, return ch.
if (u.length() != 1) {
return ch;
}
// f. Let cu be u's single code unit element.
UChar32 cu = u.char32At(0);
// g. If the value of ch >= 128 and the value of cu < 128, return ch.
if (ch >= 128 && cu < 128) {
return ch;
}
// h. Return cu.
return cu;
}
};
} // namespace internal
} // namespace v8

View file

@ -1,38 +0,0 @@
#!/usr/bin/env python
# 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/.
#
# This script modifies V8 regexp source files to make them suitable for
# inclusion in SpiderMonkey. Specifically, it:
#
# 1. Rewrites all #includes of V8 regexp headers to point to their location in
# the SM tree: src/regexp/* --> regexp/*
# 2. Removes all #includes of other V8 src/* headers. The required definitions
# will be provided by regexp-shim.h.
#
# Usage:
# cd js/src/regexp
# find . -name "*.h" -o -name "*.cc" | xargs ./update_headers.py
#
import fileinput
import re
import sys
# 1. Rewrite includes of V8 regexp headers
regexp_include = re.compile('#include "src/regexp')
regexp_include_new = '#include "regexp'
# 2. Remove includes of other V8 headers
other_include = re.compile('#include "src/')
for line in fileinput.input(inplace=1):
if regexp_include.search(line):
sys.stdout.write(re.sub(regexp_include, regexp_include_new, line))
elif other_include.search(line):
pass
else:
sys.stdout.write(line)

View file

@ -0,0 +1,93 @@
// Copyright 2014 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.
#ifndef V8_UTIL_FLAGS_H_
#define V8_UTIL_FLAGS_H_
// Origin: https://github.com/v8/v8/blob/1bafcc6b999b23ea1d394f5d267a08183e3c4e19/src/base/flags.h#L15-L90
namespace v8 {
namespace base {
// The Flags class provides a type-safe way of storing OR-combinations of enum
// values. The Flags<T, S> class is a template class, where T is an enum type,
// and S is the underlying storage type (usually int).
//
// The traditional C++ approach for storing OR-combinations of enum values is to
// use an int or unsigned int variable. The inconvenience with this approach is
// that there's no type checking at all; any enum value can be OR'd with any
// other enum value and passed on to a function that takes an int or unsigned
// int.
template <typename T, typename S = int>
class Flags final {
public:
using flag_type = T;
using mask_type = S;
constexpr Flags() : mask_(0) {}
constexpr Flags(flag_type flag)
: mask_(static_cast<S>(flag)) {}
constexpr explicit Flags(mask_type mask) : mask_(static_cast<S>(mask)) {}
constexpr bool operator==(flag_type flag) const {
return mask_ == static_cast<S>(flag);
}
constexpr bool operator!=(flag_type flag) const {
return mask_ != static_cast<S>(flag);
}
Flags& operator&=(const Flags& flags) {
mask_ &= flags.mask_;
return *this;
}
Flags& operator|=(const Flags& flags) {
mask_ |= flags.mask_;
return *this;
}
Flags& operator^=(const Flags& flags) {
mask_ ^= flags.mask_;
return *this;
}
constexpr Flags operator&(const Flags& flags) const {
return Flags(mask_ & flags.mask_);
}
constexpr Flags operator|(const Flags& flags) const {
return Flags(mask_ | flags.mask_);
}
constexpr Flags operator^(const Flags& flags) const {
return Flags(mask_ ^ flags.mask_);
}
Flags& operator&=(flag_type flag) { return operator&=(Flags(flag)); }
Flags& operator|=(flag_type flag) { return operator|=(Flags(flag)); }
Flags& operator^=(flag_type flag) { return operator^=(Flags(flag)); }
constexpr Flags operator&(flag_type flag) const {
return operator&(Flags(flag));
}
constexpr Flags operator|(flag_type flag) const {
return operator|(Flags(flag));
}
constexpr Flags operator^(flag_type flag) const {
return operator^(Flags(flag));
}
constexpr Flags operator~() const { return Flags(~mask_); }
constexpr operator mask_type() const { return mask_; }
constexpr bool operator!() const { return !mask_; }
Flags without(flag_type flag) { return *this & (~Flags(flag)); }
friend size_t hash_value(const Flags& flags) { return flags.mask_; }
private:
mask_type mask_;
};
} // namespace base
} // namespace v8
#endif // V8_UTIL_FLAG_H_

File diff suppressed because it is too large Load diff

204
js/src/regexp/util/vector.h Normal file
View file

@ -0,0 +1,204 @@
// Copyright 2014 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.
#ifndef V8_UTIL_VECTOR_H_
#define V8_UTIL_VECTOR_H_
#include <algorithm>
#include <cstring>
#include <iterator>
#include <memory>
#include "js/Utility.h"
namespace v8 {
namespace internal {
//////////////////////////////////////////////////
// Adapted from: https://github.com/v8/v8/blob/5f69bbc233c2d1baf149faf869a7901603929914/src/utils/allocation.h#L36-L58
template <typename T>
T* NewArray(size_t size) {
static_assert(std::is_pod<T>::value, "");
js::AutoEnterOOMUnsafeRegion oomUnsafe;
T* result = static_cast<T*>(js_malloc(size * sizeof(T)));
if (!result) {
oomUnsafe.crash("Irregexp NewArray");
}
return result;
}
template <typename T>
void DeleteArray(T* array) {
js_free(array);
}
//////////////////////////////////////////////////
// A non-resizable vector containing a pointer and a length.
// The Vector may or may not own the pointer, depending on context.
// Origin:
// https://github.com/v8/v8/blob/5f69bbc233c2d1baf149faf869a7901603929914/src/utils/vector.h#L20-L134
template <typename T>
class Vector {
public:
constexpr Vector() : start_(nullptr), length_(0) {}
constexpr Vector(T* data, size_t length) : start_(data), length_(length) {
MOZ_ASSERT_IF(length != 0, data != nullptr);
}
static Vector<T> New(size_t length) {
return Vector<T>(NewArray<T>(length), length);
}
// Returns a vector using the same backing storage as this one,
// spanning from and including 'from', to but not including 'to'.
Vector<T> SubVector(size_t from, size_t to) const {
MOZ_ASSERT(from < to);
MOZ_ASSERT(to < length_);
return Vector<T>(begin() + from, to - from);
}
// Returns the length of the vector. Only use this if you really need an
// integer return value. Use {size()} otherwise.
int length() const {
MOZ_ASSERT(length_ <= std::numeric_limits<int>::max());
return static_cast<int>(length_);
}
// Returns the length of the vector as a size_t.
constexpr size_t size() const { return length_; }
// Returns whether or not the vector is empty.
constexpr bool empty() const { return length_ == 0; }
// Access individual vector elements - checks bounds in debug mode.
T& operator[](size_t index) const {
MOZ_ASSERT(index < length_);
return start_[index];
}
const T& at(size_t index) const { return operator[](index); }
T& first() { return start_[0]; }
T& last() {
MOZ_ASSERT(length_ > 0);
return start_[length_ - 1];
}
// Returns a pointer to the start of the data in the vector.
constexpr T* begin() const { return start_; }
// Returns a pointer past the end of the data in the vector.
constexpr T* end() const { return start_ + length_; }
// Returns a clone of this vector with a new backing store.
Vector<T> Clone() const {
T* result = NewArray<T>(length_);
for (size_t i = 0; i < length_; i++) result[i] = start_[i];
return Vector<T>(result, length_);
}
void Truncate(size_t length) {
MOZ_ASSERT(length <= length_);
length_ = length;
}
// Releases the array underlying this vector. Once disposed the
// vector is empty.
void Dispose() {
DeleteArray(start_);
start_ = nullptr;
length_ = 0;
}
Vector<T> operator+(size_t offset) {
MOZ_ASSERT(offset <= length_);
return Vector<T>(start_ + offset, length_ - offset);
}
Vector<T> operator+=(size_t offset) {
MOZ_ASSERT(offset <= length_);
start_ += offset;
length_ -= offset;
return *this;
}
// Implicit conversion from Vector<T> to Vector<const T>.
inline operator Vector<const T>() const {
return Vector<const T>::cast(*this);
}
template <typename S>
static constexpr Vector<T> cast(Vector<S> input) {
return Vector<T>(reinterpret_cast<T*>(input.begin()),
input.length() * sizeof(S) / sizeof(T));
}
bool operator==(const Vector<const T> other) const {
if (length_ != other.length_) return false;
if (start_ == other.start_) return true;
for (size_t i = 0; i < length_; ++i) {
if (start_[i] != other.start_[i]) {
return false;
}
}
return true;
}
private:
T* start_;
size_t length_;
};
// The resulting vector does not contain a null-termination byte. If you want
// the null byte, use ArrayVector("foo").
inline Vector<const char> CStrVector(const char* data) {
return Vector<const char>(data, strlen(data));
}
} // namespace internal
namespace base {
// SmallVector uses inline storage first, and reallocates when full.
// It is basically equivalent to js::Vector, and is implemented
// as a thin wrapper.
// V8's implementation: https://github.com/v8/v8/blob/master/src/base/small-vector.h
template <typename T, size_t kSize>
class SmallVector {
public:
inline bool empty() const { return inner_.empty(); }
inline const T& back() const { return inner_.back(); }
inline void pop_back() { inner_.popBack(); };
template <typename... Args>
inline void emplace_back(Args&&... args) {
js::AutoEnterOOMUnsafeRegion oomUnsafe;
if (!inner_.emplaceBack(args...)) {
oomUnsafe.crash("Irregexp SmallVector emplace_back");
}
};
inline size_t size() const { return inner_.length(); }
inline const T& at(size_t index) const { return inner_[index]; }
void resize_no_init(size_t new_size) {
js::AutoEnterOOMUnsafeRegion oomUnsafe;
if (!inner_.resizeUninitialized(new_size)) {
oomUnsafe.crash("Irregexp SmallVector resize");
}
}
private:
js::Vector<T, kSize, js::SystemAllocPolicy> inner_;
};
} // namespace base
} // namespace v8
#endif // V8_UTIL_VECTOR_H_

375
js/src/regexp/util/zone.h Normal file
View file

@ -0,0 +1,375 @@
// 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.
#ifndef V8_UTIL_ZONE_H_
#define V8_UTIL_ZONE_H_
#include <list>
#include <map>
#include <set>
#include <unordered_map>
#include <vector>
#include "ds/LifoAlloc.h"
#include "ds/Sort.h"
#include "regexp/util/vector.h"
namespace v8 {
namespace internal {
// V8::Zone ~= LifoAlloc
class Zone {
public:
Zone(size_t defaultChunkSize) : lifoAlloc_(defaultChunkSize) {
lifoAlloc_.setAsInfallibleByDefault();
}
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_.computedSizeOfExcludingThis() > kExcessLimit;
}
private:
js::LifoAlloc lifoAlloc_;
};
// Superclass for classes allocated in a Zone.
// Origin: https://github.com/v8/v8/blob/7b3332844212d78ee87a9426f3a6f7f781a8fbfa/src/zone/zone.h#L138-L155
class ZoneObject {
public:
// Allocate a new ZoneObject of 'size' bytes in the Zone.
void* operator new(size_t size, Zone* zone) { return zone->New(size); }
// Ideally, the delete operator should be private instead of
// public, but unfortunately the compiler sometimes synthesizes
// (unused) destructors for classes derived from ZoneObject, which
// require the operator to be visible. MSVC requires the delete
// operator to be public.
// ZoneObjects should never be deleted individually; use
// Zone::DeleteAll() to delete all zone objects in one go.
void operator delete(void*, size_t) { MOZ_CRASH("unreachable"); }
void operator delete(void* pointer, Zone* zone) { MOZ_CRASH("unreachable"); }
};
// ZoneLists are growable lists with constant-time access to the
// elements. The list itself and all its elements are allocated in the
// Zone. ZoneLists cannot be deleted individually; you can delete all
// objects in the Zone by calling Zone::DeleteAll().
// Used throughout irregexp.
// Origin: https://github.com/v8/v8/blob/5e514a969376dc63517d575b062758efd36cd757/src/zone/zone.h#L173-L318
// Inlines: https://github.com/v8/v8/blob/5e514a969376dc63517d575b062758efd36cd757/src/zone/zone-list-inl.h#L17-L155
template <typename T>
class ZoneList final {
public:
// Construct a new ZoneList with the given capacity; the length is
// always zero. The capacity must be non-negative.
ZoneList(int capacity, Zone* zone) { Initialize(capacity, zone); }
// Construct a new ZoneList from a std::initializer_list
ZoneList(std::initializer_list<T> list, Zone* zone) {
Initialize(static_cast<int>(list.size()), zone);
for (auto& i : list) Add(i, zone);
}
// Construct a new ZoneList by copying the elements of the given ZoneList.
ZoneList(const ZoneList<T>& other, Zone* zone) {
Initialize(other.length(), zone);
AddAll(other, zone);
}
void* operator new(size_t size, Zone* zone) { return zone->New(size); }
// Returns a reference to the element at index i. This reference is not safe
// to use after operations that can change the list's backing store
// (e.g. Add).
inline T& operator[](int i) const {
MOZ_ASSERT(0 < i);
MOZ_ASSERT(static_cast<unsigned>(i) < static_cast<unsigned>(length_));
return data_[i];
}
inline T& at(int i) const { return operator[](i); }
inline T& last() const { return at(length_ - 1); }
inline T& first() const { return at(0); }
using iterator = T*;
inline iterator begin() const { return &data_[0]; }
inline iterator end() const { return &data_[length_]; }
inline bool is_empty() const { return length_ == 0; }
inline int length() const { return length_; }
inline int capacity() const { return capacity_; }
Vector<T> ToVector() const { return Vector<T>(data_, length_); }
Vector<T> ToVector(int start, int length) const {
return Vector<T>(data_ + start, std::min(length_ - start, length));
}
Vector<const T> ToConstVector() const {
return Vector<const T>(data_, length_);
}
inline void Initialize(int capacity, Zone* zone) {
MOZ_ASSERT(capacity >= 0);
data_ = (capacity > 0) ? NewData(capacity, zone) : nullptr;
capacity_ = capacity;
length_ = 0;
}
// Adds a copy of the given 'element' to the end of the list,
// expanding the list if necessary.
void Add(const T& element, Zone* zone) {
if (length_ < capacity_) {
data_[length_++] = element;
} else {
ZoneList<T>::ResizeAdd(element, zone);
}
}
// Add all the elements from the argument list to this list.
void AddAll(const ZoneList<T>& other, Zone* zone) {
AddAll(other.ToVector(), zone);
}
// Add all the elements from the vector to this list.
void AddAll(const Vector<T>& other, Zone* zone) {
int result_length = length_ + other.length();
if (capacity_ < result_length) {
Resize(result_length, zone);
}
if (std::is_fundamental<T>()) {
memcpy(data_ + length_, other.begin(), sizeof(*data_) * other.length());
} else {
for (int i = 0; i < other.length(); i++) {
data_[length_ + i] = other.at(i);
}
}
length_ = result_length;
}
// Overwrites the element at the specific index.
void Set(int index, const T& element) {
MOZ_ASSERT(index >= 0 && index <= length_);
data_[index] = element;
}
// Removes the i'th element without deleting it even if T is a
// pointer type; moves all elements above i "down". Returns the
// removed element. This function's complexity is linear in the
// size of the list.
T Remove(int i) {
T element = at(i);
length_--;
while (i < length_) {
data_[i] = data_[i + 1];
i++;
}
return element;
}
// Removes the last element without deleting it even if T is a
// pointer type. Returns the removed element.
inline T RemoveLast() { return Remove(length_ - 1); }
// Clears the list by freeing the storage memory. If you want to keep the
// memory, use Rewind(0) instead. Be aware, that even if T is a
// pointer type, clearing the list doesn't delete the entries.
inline void Clear() {
data_ = nullptr;
capacity_ = 0;
length_ = 0;
}
// Drops all but the first 'pos' elements from the list.
inline void Rewind(int pos) {
MOZ_ASSERT(0 <= pos && pos <= length_);
length_ = pos;
}
inline bool Contains(const T& elm) const {
for (int i = 0; i < length_; i++) {
if (data_[i] == elm) return true;
}
return false;
}
template <typename CompareFunction>
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"); }
void operator delete(void* pointer, Zone* zone) { MOZ_CRASH("unreachable"); }
private:
T* data_;
int capacity_;
int length_;
inline T* NewData(int n, Zone* zone) {
return static_cast<T*>(zone->New(n * sizeof(T)));
}
// Increase the capacity of a full list, and add an element.
// List must be full already.
void ResizeAdd(const T& element, Zone* zone) {
MOZ_ASSERT(length_ >= capacity_);
// Grow the list capacity by 100%, but make sure to let it grow
// even when the capacity is zero (possible initial case).
int new_capacity = 1 + 2 * capacity_;
// Since the element reference could be an element of the list, copy
// it out of the old backing storage before resizing.
T temp = element;
Resize(new_capacity, zone);
data_[length_++] = temp;
}
// Resize the list.
void Resize(int new_capacity, Zone* zone) {
MOZ_ASSERT(length_ <= new_capacity);
T* new_data = NewData(new_capacity, zone);
if (length_ > 0) {
memcpy(new_data, data_, length_ * sizeof(T));
}
data_ = new_data;
capacity_ = new_capacity;
}
ZoneList& operator=(const ZoneList&) = delete;
ZoneList() = delete;
ZoneList(const ZoneList&) = delete;
};
// Origin: https://github.com/v8/v8/blob/5e514a969376dc63517d575b062758efd36cd757/src/zone/zone-allocator.h#L14-L77
template <typename T>
class ZoneAllocator {
public:
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using value_type = T;
using size_type = size_t;
using difference_type = ptrdiff_t;
template <class O>
struct rebind {
using other = ZoneAllocator<O>;
};
explicit ZoneAllocator(Zone* zone) : zone_(zone) {}
template <typename U>
ZoneAllocator(const ZoneAllocator<U>& other)
: ZoneAllocator<T>(other.zone_) {}
template <typename U>
friend class ZoneAllocator;
T* allocate(size_t n) { return static_cast<T*>(zone_->New(n * sizeof(T))); }
void deallocate(T* p, size_t) {} // noop for zones
bool operator==(ZoneAllocator const& other) const {
return zone_ == other.zone_;
}
bool operator!=(ZoneAllocator const& other) const {
return zone_ != other.zone_;
}
private:
Zone* zone_;
};
// Zone wrappers for std containers:
// Origin: https://github.com/v8/v8/blob/5e514a969376dc63517d575b062758efd36cd757/src/zone/zone-containers.h#L25-L169
// A wrapper subclass for std::vector to make it easy to construct one
// that uses a zone allocator.
// Used throughout irregexp
template <typename T>
class ZoneVector : public std::vector<T, ZoneAllocator<T>> {
public:
ZoneVector(Zone* zone)
: std::vector<T, ZoneAllocator<T>>(ZoneAllocator<T>(zone)) {}
// Constructs a new vector and fills it with the contents of the range
// [first, last).
template <class Iter>
ZoneVector(Iter first, Iter last, Zone* zone)
: std::vector<T, ZoneAllocator<T>>(first, last, ZoneAllocator<T>(zone)) {}
};
// A wrapper subclass for std::list to make it easy to construct one
// that uses a zone allocator.
// Used in regexp-bytecode-peephole.cc
template <typename T>
class ZoneLinkedList : public std::list<T, ZoneAllocator<T>> {
public:
// Constructs an empty list.
explicit ZoneLinkedList(Zone* zone)
: std::list<T, ZoneAllocator<T>>(ZoneAllocator<T>(zone)) {}
};
// A wrapper subclass for std::set to make it easy to construct one that uses
// a zone allocator.
// Used in regexp-parser.cc
template <typename K, typename Compare = std::less<K>>
class ZoneSet : public std::set<K, Compare, ZoneAllocator<K>> {
public:
// Constructs an empty set.
explicit ZoneSet(Zone* zone)
: std::set<K, Compare, ZoneAllocator<K>>(Compare(),
ZoneAllocator<K>(zone)) {}
};
// A wrapper subclass for std::map to make it easy to construct one that uses
// a zone allocator.
// Used in regexp-bytecode-peephole.cc
template <typename K, typename V, typename Compare = std::less<K>>
class ZoneMap
: public std::map<K, V, Compare, ZoneAllocator<std::pair<const K, V>>> {
public:
// Constructs an empty map.
explicit ZoneMap(Zone* zone)
: std::map<K, V, Compare, ZoneAllocator<std::pair<const K, V>>>(
Compare(), ZoneAllocator<std::pair<const K, V>>(zone)) {}
};
// A wrapper subclass for std::unordered_map to make it easy to construct one
// that uses a zone allocator.
// Used in regexp-bytecode-peephole.cc
template <typename K, typename V, typename Hash = std::hash<K>,
typename KeyEqual = std::equal_to<K>>
class ZoneUnorderedMap
: public std::unordered_map<K, V, Hash, KeyEqual,
ZoneAllocator<std::pair<const K, V>>> {
public:
// Constructs an empty map.
explicit ZoneUnorderedMap(Zone* zone, size_t bucket_count = 100)
: std::unordered_map<K, V, Hash, KeyEqual,
ZoneAllocator<std::pair<const K, V>>>(
bucket_count, Hash(), KeyEqual(),
ZoneAllocator<std::pair<const K, V>>(zone)) {}
};
} // namespace internal
} // namespace v8
#endif // V8_UTIL_FLAG_H_

View file

@ -313,7 +313,7 @@ public:
return *this;
}
T& operator*() const { return *get(); }
typename AddLvalueReference<T>::Type operator*() const { return *get(); }
Pointer operator->() const
{
MOZ_ASSERT(get(), "dereferencing a UniquePtr containing nullptr");