mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
![Stevie Strickland](/assets/img/avatar_default.png)
This work pulls in v8 support for these features with appropriate changes for Dart and closes https://github.com/dart-lang/sdk/issues/34935. This adds support for the following features: * Interpreting patterns as Unicode patterns instead of BMP patterns * the dotAll flag (`/s`) for changing the behavior of '.' to also match line terminators * Escapes for character classes described by Unicode property groups (e.g., \p{Greek} to match all Greek characters, or \P{Greek} for all non-Greek characters). The following TC39 proposals describe some of the added features: * https://github.com/tc39/proposal-regexp-dotall-flag * https://github.com/tc39/proposal-regexp-unicode-property-escapes These additional changes are included: * Extends named capture group names to include the full range of identifier characters supported by ECMAScript, not just ASCII. * Changing the RegExp interface to return RegExpMatch objects, not Match objects, so that downcasting is not necessary to use named capture groups from Dart **Note**: The changes to the RegExp interface are a breaking change for implementers of the RegExp interface. Current users of the RegExp interface (i.e., code using Dart RegExp objects) will not be affected. Change-Id: Ie62e6082a0e2fedc1680ef2576ce0c6db80fc19a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/100641 Reviewed-by: Martin Kustermann <kustermann@google.com> Commit-Queue: Stevie Strickland <sstrickl@google.com>
265 lines
11 KiB
C++
265 lines
11 KiB
C++
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#ifndef RUNTIME_VM_REGEXP_ASSEMBLER_H_
|
|
#define RUNTIME_VM_REGEXP_ASSEMBLER_H_
|
|
|
|
#include "vm/compiler/assembler/assembler.h"
|
|
#include "vm/compiler/backend/il.h"
|
|
#include "vm/object.h"
|
|
|
|
namespace dart {
|
|
|
|
// Utility function for the DotPrinter
|
|
void PrintUtf16(uint16_t c);
|
|
// Compares two-byte strings case insensitively as UCS2.
|
|
// Called from generated RegExp code.
|
|
RawBool* CaseInsensitiveCompareUCS2(RawString* str_raw,
|
|
RawSmi* lhs_index_raw,
|
|
RawSmi* rhs_index_raw,
|
|
RawSmi* length_raw);
|
|
|
|
// Compares two-byte strings case insensitively as UTF16.
|
|
// Called from generated RegExp code.
|
|
RawBool* CaseInsensitiveCompareUTF16(RawString* str_raw,
|
|
RawSmi* lhs_index_raw,
|
|
RawSmi* rhs_index_raw,
|
|
RawSmi* length_raw);
|
|
|
|
/// Convenience wrapper around a BlockEntryInstr pointer.
|
|
class BlockLabel : public ValueObject {
|
|
// Used by the IR assembler.
|
|
public:
|
|
BlockLabel();
|
|
|
|
JoinEntryInstr* block() const { return block_; }
|
|
|
|
bool IsBound() const { return is_bound_; }
|
|
void SetBound(intptr_t block_id) {
|
|
ASSERT(!is_bound_);
|
|
block_->set_block_id(block_id);
|
|
is_bound_ = true;
|
|
}
|
|
|
|
bool IsLinked() const { return !is_bound_ && is_linked_; }
|
|
void SetLinked() { is_linked_ = true; }
|
|
|
|
intptr_t Position() const {
|
|
ASSERT(IsBound());
|
|
return block_->block_id();
|
|
}
|
|
|
|
private:
|
|
JoinEntryInstr* block_;
|
|
|
|
bool is_bound_;
|
|
bool is_linked_;
|
|
|
|
// Used by the bytecode assembler.
|
|
public:
|
|
~BlockLabel() { ASSERT(!is_linked()); }
|
|
|
|
intptr_t pos() const { return pos_; }
|
|
bool is_bound() const { return IsBound(); }
|
|
bool is_linked() const { return IsLinked(); }
|
|
|
|
void Unuse() {
|
|
pos_ = 0;
|
|
is_bound_ = false;
|
|
is_linked_ = false;
|
|
}
|
|
|
|
void bind_to(intptr_t pos) {
|
|
pos_ = pos;
|
|
is_bound_ = true;
|
|
is_linked_ = false;
|
|
ASSERT(is_bound());
|
|
}
|
|
|
|
void link_to(intptr_t pos) {
|
|
pos_ = pos;
|
|
is_bound_ = false;
|
|
is_linked_ = true;
|
|
ASSERT(is_linked());
|
|
}
|
|
|
|
private:
|
|
intptr_t pos_;
|
|
};
|
|
|
|
class RegExpMacroAssembler : public ZoneAllocated {
|
|
public:
|
|
// The implementation must be able to handle at least:
|
|
static const intptr_t kMaxRegister = (1 << 16) - 1;
|
|
static const intptr_t kMaxCPOffset = (1 << 15) - 1;
|
|
static const intptr_t kMinCPOffset = -(1 << 15);
|
|
|
|
static const intptr_t kTableSizeBits = 7;
|
|
static const intptr_t kTableSize = 1 << kTableSizeBits;
|
|
static const intptr_t kTableMask = kTableSize - 1;
|
|
|
|
enum {
|
|
kParamRegExpIndex = 0,
|
|
kParamStringIndex,
|
|
kParamStartOffsetIndex,
|
|
kParamCount
|
|
};
|
|
|
|
enum IrregexpImplementation { kBytecodeImplementation, kIRImplementation };
|
|
|
|
explicit RegExpMacroAssembler(Zone* zone);
|
|
virtual ~RegExpMacroAssembler();
|
|
// The maximal number of pushes between stack checks. Users must supply
|
|
// kCheckStackLimit flag to push operations (instead of kNoStackLimitCheck)
|
|
// at least once for every stack_limit() pushes that are executed.
|
|
virtual intptr_t stack_limit_slack() = 0;
|
|
virtual bool CanReadUnaligned() = 0;
|
|
virtual void AdvanceCurrentPosition(intptr_t by) = 0; // Signed cp change.
|
|
virtual void AdvanceRegister(intptr_t reg, intptr_t by) = 0; // r[reg] += by.
|
|
// Continues execution from the position pushed on the top of the backtrack
|
|
// stack by an earlier PushBacktrack(BlockLabel*).
|
|
virtual void Backtrack() = 0;
|
|
virtual void BindBlock(BlockLabel* label) = 0;
|
|
virtual void CheckAtStart(BlockLabel* on_at_start) = 0;
|
|
// Dispatch after looking the current character up in a 2-bits-per-entry
|
|
// map. The destinations vector has up to 4 labels.
|
|
virtual void CheckCharacter(unsigned c, BlockLabel* on_equal) = 0;
|
|
// Bitwise and the current character with the given constant and then
|
|
// check for a match with c.
|
|
virtual void CheckCharacterAfterAnd(unsigned c,
|
|
unsigned and_with,
|
|
BlockLabel* on_equal) = 0;
|
|
virtual void CheckCharacterGT(uint16_t limit, BlockLabel* on_greater) = 0;
|
|
virtual void CheckCharacterLT(uint16_t limit, BlockLabel* on_less) = 0;
|
|
virtual void CheckGreedyLoop(BlockLabel* on_tos_equals_current_position) = 0;
|
|
virtual void CheckNotAtStart(intptr_t cp_offset,
|
|
BlockLabel* on_not_at_start) = 0;
|
|
virtual void CheckNotBackReference(intptr_t start_reg,
|
|
bool read_backward,
|
|
BlockLabel* on_no_match) = 0;
|
|
virtual void CheckNotBackReferenceIgnoreCase(intptr_t start_reg,
|
|
bool read_backward,
|
|
bool unicode,
|
|
BlockLabel* 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
|
|
// matches. If the label is NULL then we should pop a backtrack address off
|
|
// the stack and go to that.
|
|
virtual void CheckNotCharacter(unsigned c, BlockLabel* on_not_equal) = 0;
|
|
virtual void CheckNotCharacterAfterAnd(unsigned c,
|
|
unsigned and_with,
|
|
BlockLabel* on_not_equal) = 0;
|
|
// Subtract a constant from the current character, then and with the given
|
|
// constant and then check for a match with c.
|
|
virtual void CheckNotCharacterAfterMinusAnd(uint16_t c,
|
|
uint16_t minus,
|
|
uint16_t and_with,
|
|
BlockLabel* on_not_equal) = 0;
|
|
virtual void CheckCharacterInRange(uint16_t from,
|
|
uint16_t to, // Both inclusive.
|
|
BlockLabel* on_in_range) = 0;
|
|
virtual void CheckCharacterNotInRange(uint16_t from,
|
|
uint16_t to, // Both inclusive.
|
|
BlockLabel* on_not_in_range) = 0;
|
|
|
|
// The current character (modulus the kTableSize) is looked up in the byte
|
|
// array, and if the found byte is non-zero, we jump to the on_bit_set label.
|
|
virtual void CheckBitInTable(const TypedData& table,
|
|
BlockLabel* on_bit_set) = 0;
|
|
|
|
// Checks for preemption and serves as an OSR entry.
|
|
virtual void CheckPreemption(bool is_backtrack) {}
|
|
|
|
// Checks whether the given offset from the current position is before
|
|
// the end of the string. May overwrite the current character.
|
|
virtual void CheckPosition(intptr_t cp_offset, BlockLabel* on_outside_input) {
|
|
LoadCurrentCharacter(cp_offset, on_outside_input, true);
|
|
}
|
|
// Check whether a standard/default character class matches the current
|
|
// character. Returns false if the type of special character class does
|
|
// not have custom support.
|
|
// May clobber the current loaded character.
|
|
virtual bool CheckSpecialCharacterClass(uint16_t type,
|
|
BlockLabel* on_no_match) {
|
|
return false;
|
|
}
|
|
virtual void Fail() = 0;
|
|
// Check whether a register is >= a given constant and go to a label if it
|
|
// is. Backtracks instead if the label is NULL.
|
|
virtual void IfRegisterGE(intptr_t reg,
|
|
intptr_t comparand,
|
|
BlockLabel* if_ge) = 0;
|
|
// Check whether a register is < a given constant and go to a label if it is.
|
|
// Backtracks instead if the label is NULL.
|
|
virtual void IfRegisterLT(intptr_t reg,
|
|
intptr_t comparand,
|
|
BlockLabel* if_lt) = 0;
|
|
// Check whether a register is == to the current position and go to a
|
|
// label if it is.
|
|
virtual void IfRegisterEqPos(intptr_t reg, BlockLabel* if_eq) = 0;
|
|
virtual IrregexpImplementation Implementation() = 0;
|
|
// The assembler is closed, iff there is no current instruction assigned.
|
|
virtual bool IsClosed() const = 0;
|
|
// Jump to the target label without setting it as the current instruction.
|
|
virtual void GoTo(BlockLabel* to) = 0;
|
|
virtual void LoadCurrentCharacter(intptr_t cp_offset,
|
|
BlockLabel* on_end_of_input,
|
|
bool check_bounds = true,
|
|
intptr_t characters = 1) = 0;
|
|
virtual void PopCurrentPosition() = 0;
|
|
virtual void PopRegister(intptr_t register_index) = 0;
|
|
// Prints string within the generated code. Used for debugging.
|
|
virtual void Print(const char* str) = 0;
|
|
// Prints all emitted blocks.
|
|
virtual void PrintBlocks() = 0;
|
|
// Pushes the label on the backtrack stack, so that a following Backtrack
|
|
// will go to this label. Always checks the backtrack stack limit.
|
|
virtual void PushBacktrack(BlockLabel* label) = 0;
|
|
virtual void PushCurrentPosition() = 0;
|
|
virtual void PushRegister(intptr_t register_index) = 0;
|
|
virtual void ReadCurrentPositionFromRegister(intptr_t reg) = 0;
|
|
virtual void ReadStackPointerFromRegister(intptr_t reg) = 0;
|
|
virtual void SetCurrentPositionFromEnd(intptr_t by) = 0;
|
|
virtual void SetRegister(intptr_t register_index, intptr_t to) = 0;
|
|
// Return whether the matching (with a global regexp) will be restarted.
|
|
virtual bool Succeed() = 0;
|
|
virtual void WriteCurrentPositionToRegister(intptr_t reg,
|
|
intptr_t cp_offset) = 0;
|
|
virtual void ClearRegisters(intptr_t reg_from, intptr_t reg_to) = 0;
|
|
virtual void WriteStackPointerToRegister(intptr_t reg) = 0;
|
|
|
|
// Check that we are not in the middle of a surrogate pair.
|
|
void CheckNotInSurrogatePair(intptr_t cp_offset, BlockLabel* on_failure);
|
|
|
|
// Controls the generation of large inlined constants in the code.
|
|
void set_slow_safe(bool ssc) { slow_safe_compiler_ = ssc; }
|
|
bool slow_safe() { return slow_safe_compiler_; }
|
|
|
|
enum GlobalMode {
|
|
NOT_GLOBAL,
|
|
GLOBAL,
|
|
GLOBAL_NO_ZERO_LENGTH_CHECK,
|
|
GLOBAL_UNICODE
|
|
};
|
|
// Set whether the regular expression has the global flag. Exiting due to
|
|
// a failure in a global regexp may still mean success overall.
|
|
inline void set_global_mode(GlobalMode mode) { global_mode_ = mode; }
|
|
inline bool global() { return global_mode_ != NOT_GLOBAL; }
|
|
inline bool global_with_zero_length_check() {
|
|
return global_mode_ == GLOBAL || global_mode_ == GLOBAL_UNICODE;
|
|
}
|
|
inline bool global_unicode() { return global_mode_ == GLOBAL_UNICODE; }
|
|
|
|
Zone* zone() const { return zone_; }
|
|
|
|
private:
|
|
bool slow_safe_compiler_;
|
|
GlobalMode global_mode_;
|
|
Zone* zone_;
|
|
};
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_REGEXP_ASSEMBLER_H_
|