| // Copyright 2008-2009 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 "vm/regexp/regexp-bytecode-generator.h" | |
| #include <limits> | |
| #include <tuple> | |
| #include <type_traits> | |
| #include "vm/regexp/regexp-bytecode-generator-inl.h" | |
| #include "vm/regexp/regexp-bytecodes-inl.h" | |
| #include "vm/regexp/regexp-macro-assembler.h" | |
| #include "vm/regexp/regexp.h" | |
| namespace dart { | |
| // Used to decide whether we use the `Char` or `4Chars` variant of a bytecode. | |
| static constexpr int kMaxSingleCharValue = | |
| RegExpOperandTypeTraits<RegExpBytecodeOperandType::kChar>::kMaxValue; | |
| // TODO(jgruber): Move all Writer methods before Generator methods. | |
| RegExpBytecodeWriter::RegExpBytecodeWriter(Zone* zone) | |
| : buffer_(zone), | |
| pc_(0), | |
| jump_edges_(zone) | |
| #ifdef DEBUG | |
| , | |
| end_of_bc_(0), | |
| pc_within_bc_(0) | |
| #endif | |
| { | |
| } | |
| void RegExpBytecodeWriter::ExpandBuffer(size_t new_size) { | |
| // TODO(jgruber): It's not necessary to default-initialize new elements. | |
| buffer_.resize(new_size); | |
| } | |
| void RegExpBytecodeWriter::Reset() { | |
| // We keep the buffer_ storage; the next pass will overwrite its contents. | |
| jump_edges_.clear(); | |
| ResetPc(0); | |
| } | |
| void RegExpBytecodeWriter::EmitRawBytecodeStream(const uint8_t* data, int len) { | |
| EnsureCapacity(len); | |
| // Must start at a bytecode boundary. | |
| DCHECK_EQ(pc_within_bc_, end_of_bc_); | |
| // We cannot check whether we also end at a boundary since we don't know what | |
| // data contains. Let's at least verify alignment. | |
| // TODO(jgruber): We could use RegExpBytecodeIterator to verify in DEBUG. | |
| ASSERT(Utils::IsAligned(len, kBytecodeAlignment)); | |
| memcpy(buffer_.data() + pc_, data, len); | |
| // End at a bytecode boundary, update bookkeeping. | |
| pc_ += len; | |
| #ifdef DEBUG | |
| pc_within_bc_ = pc_; | |
| end_of_bc_ = pc_; | |
| #endif | |
| } | |
| void RegExpBytecodeWriter::EmitRawBytecodeStream( | |
| const RegExpBytecodeWriter* src_writer, | |
| int src_offset, | |
| int length) { | |
| const int start_pc = pc_; | |
| EmitRawBytecodeStream(src_writer->buffer().data() + src_offset, length); | |
| // Copy jumps in range. | |
| const auto& src_edges = src_writer->jump_edges(); | |
| auto jump_iter = src_edges.lower_bound(src_offset); | |
| // Iterate over all jumps that start in the copied range. | |
| while (jump_iter != src_edges.end() && | |
| jump_iter->first < src_offset + length) { | |
| int old_source = jump_iter->first; | |
| int old_target = jump_iter->second; | |
| int new_source = start_pc + (old_source - src_offset); | |
| jump_edges_.emplace(new_source, old_target); | |
| jump_iter++; | |
| } | |
| } | |
| void RegExpBytecodeWriter::Finalize(RegExpBytecode bc) { | |
| int size = RegExpBytecodes::Size(bc); | |
| EMIT_PADDING(size); | |
| pc_ += size; | |
| #ifdef DEBUG | |
| DCHECK_EQ(pc_within_bc_, end_of_bc_); | |
| pc_within_bc_ = pc_; | |
| end_of_bc_ = pc_; | |
| #endif | |
| } | |
| RegExpBytecodeGenerator::RegExpBytecodeGenerator(Isolate* isolate, | |
| Zone* zone, | |
| Mode mode) | |
| : RegExpMacroAssembler(isolate, zone, mode), | |
| RegExpBytecodeWriter(zone), | |
| isolate_(isolate) {} | |
| RegExpBytecodeGenerator::~RegExpBytecodeGenerator() { | |
| if (backtrack_.is_linked()) backtrack_.Unuse(); | |
| } | |
| RegExpBytecodeGenerator::IrregexpImplementation | |
| RegExpBytecodeGenerator::Implementation() { | |
| return kBytecodeImplementation; | |
| } | |
| template <RegExpBytecode bytecode, typename... Args> | |
| void RegExpBytecodeWriter::Emit(Args... args) { | |
| using Operands = RegExpBytecodeOperands<bytecode>; | |
| static_assert(sizeof...(Args) == Operands::kCount, | |
| "Wrong number of operands"); | |
| auto arguments_tuple = std::make_tuple(args...); | |
| EmitBytecode(bytecode); | |
| Operands::ForEachOperandWithIndex([&]<auto op, size_t index>() { | |
| constexpr RegExpBytecodeOperandType type = Operands::Type(op); | |
| constexpr int offset = Operands::Offset(op); | |
| auto value = std::get<index>(arguments_tuple); | |
| EmitOperand<type>(value, offset); | |
| }); | |
| Finalize(bytecode); | |
| } | |
| namespace { | |
| // Helper to get the underlying type of an enum, or the type itself if it isn't | |
| // an enum. | |
| template <typename T> | |
| struct get_underlying_or_self { | |
| using type = T; | |
| }; | |
| template <typename T> | |
| requires std::is_enum_v<T> | |
| struct get_underlying_or_self<T> { | |
| using type = std::underlying_type_t<T>; | |
| }; | |
| } // namespace | |
| template <RegExpBytecodeOperandType OperandType, typename T> | |
| auto RegExpBytecodeWriter::GetCheckedBasicOperandValue(T value) { | |
| static_assert(RegExpOperandTypeTraits<OperandType>::kIsBasic); | |
| using Traits = RegExpOperandTypeTraits<OperandType>; | |
| using EnumOrCType = Traits::kCType; | |
| using CType = get_underlying_or_self<EnumOrCType>::type; | |
| if constexpr (std::is_enum_v<EnumOrCType>) { | |
| static_assert(std::is_same_v<T, EnumOrCType>); | |
| } else { | |
| static_assert(std::is_convertible_v<T, CType>); | |
| } | |
| DCHECK_GE(value, Traits::kMinValue); | |
| DCHECK_LE(value, Traits::kMaxValue); | |
| return static_cast<CType>(value); | |
| } | |
| template <RegExpBytecodeOperandType OperandType, typename T> | |
| void RegExpBytecodeWriter::EmitOperand(T value, int offset) { | |
| if constexpr (OperandType == RegExpBytecodeOperandType::kJumpTarget) { | |
| jump_edges_.emplace(pc_ + offset, static_cast<int>(value)); | |
| } | |
| Emit(GetCheckedBasicOperandValue<OperandType>(value), offset); | |
| } | |
| void RegExpBytecodeWriter::PatchJump(int target, int absolute_offset) { | |
| ASSERT(jump_edges_.contains(absolute_offset)); | |
| OverwriteValue<uint32_t>(target, absolute_offset); | |
| jump_edges_[absolute_offset] = target; | |
| } | |
| template <typename T> | |
| void RegExpBytecodeWriter::EmitOperand(RegExpBytecodeOperandType type, | |
| T value, | |
| int offset) { | |
| switch (type) { | |
| #define CASE(Name, ...) \ | |
| case RegExpBytecodeOperandType::k##Name: \ | |
| return EmitOperand<ReBcOpType::k##Name>(value, offset); | |
| BYTECODE_OPERAND_TYPE_LIST(CASE) | |
| #undef CASE | |
| default: | |
| UNREACHABLE(); | |
| } | |
| } | |
| template <> | |
| void RegExpBytecodeWriter::EmitOperand<ReBcOpType::kJumpTarget>(V8Label* label, | |
| int offset) { | |
| DCHECK_NOT_NULL(label); | |
| const int current_pc = pc_ + offset; | |
| int pos = 0; | |
| if (label->is_bound()) { | |
| pos = label->pos(); | |
| jump_edges_.emplace(current_pc, pos); | |
| } else { | |
| if (label->is_linked()) { | |
| pos = label->pos(); | |
| } | |
| label->link_to(current_pc); | |
| } | |
| Emit<uint32_t>(pos, offset); | |
| } | |
| template <> | |
| void RegExpBytecodeWriter::EmitOperand<ReBcOpType::kBitTable>( | |
| const TypedData* table, | |
| int offset) { | |
| for (int i = 0; i < RegExpMacroAssembler::kTableSize; i += kBitsPerByte) { | |
| uint8_t byte = 0; | |
| for (int j = 0; j < kBitsPerByte; j++) { | |
| if (table->GetUint8(i + j) != 0) byte |= 1 << j; | |
| } | |
| Emit(byte, offset + i / kBitsPerByte); | |
| } | |
| } | |
| template <> | |
| void RegExpBytecodeWriter::EmitOperand<ReBcOpType::kBitTable>( | |
| const uint8_t* src, | |
| int offset) { | |
| // The emitted table operand is 16 bytes long. | |
| static_assert(RegExpMacroAssembler::kTableSize / kBitsPerByte == 16); | |
| const uint32_t* cursor = reinterpret_cast<const uint32_t*>(src); | |
| static constexpr int kWordCount = | |
| (RegExpMacroAssembler::kTableSize / (kBitsPerByte * kInt32Size)); | |
| for (int i = 0; i < kWordCount; i++) { | |
| Emit(cursor[i], offset + i * kInt32Size); | |
| } | |
| } | |
| template <RegExpBytecode bytecode, typename... Args> | |
| void RegExpBytecodeGenerator::Emit(Args... args) { | |
| // Converts nullptr labels into our internal backtrack_ label. | |
| DART_UNUSED auto fix_label = [this](auto arg) { | |
| if constexpr (std::is_convertible_v<decltype(arg), V8Label*>) { | |
| V8Label* l = static_cast<V8Label*>(arg); | |
| return l ? l : &backtrack_; | |
| } else { | |
| return arg; | |
| } | |
| }; | |
| RegExpBytecodeWriter::Emit<bytecode>(fix_label(args)...); | |
| } | |
| void RegExpBytecodeGenerator::Bind(V8Label* l) { | |
| ASSERT(!l->is_bound()); | |
| if (l->is_linked()) { | |
| int pos = l->pos(); | |
| while (pos != 0) { | |
| int fixup = pos; | |
| pos = *reinterpret_cast<int32_t*>(buffer_.data() + fixup); | |
| OverwriteValue<uint32_t>(pc_, fixup); | |
| jump_edges().emplace(fixup, pc_); | |
| } | |
| } | |
| l->bind_to(pc_); | |
| } | |
| void RegExpBytecodeGenerator::PopRegister(int register_index) { | |
| Emit<RegExpBytecode::kPopRegister>(register_index); | |
| } | |
| void RegExpBytecodeGenerator::PushRegister(int register_index, | |
| StackCheckFlag check_stack_limit) { | |
| Emit<RegExpBytecode::kPushRegister>(register_index, check_stack_limit); | |
| } | |
| void RegExpBytecodeGenerator::WriteCurrentPositionToRegister(int register_index, | |
| int cp_offset) { | |
| Emit<RegExpBytecode::kWriteCurrentPositionToRegister>(register_index, | |
| cp_offset); | |
| } | |
| void RegExpBytecodeGenerator::ClearRegisters(int reg_from, int reg_to) { | |
| DCHECK_LE(reg_from, reg_to); | |
| Emit<RegExpBytecode::kClearRegisters>(reg_from, reg_to); | |
| } | |
| void RegExpBytecodeGenerator::ReadCurrentPositionFromRegister( | |
| int register_index) { | |
| Emit<RegExpBytecode::kReadCurrentPositionFromRegister>(register_index); | |
| } | |
| void RegExpBytecodeGenerator::WriteStackPointerToRegister(int register_index) { | |
| Emit<RegExpBytecode::kWriteStackPointerToRegister>(register_index); | |
| } | |
| void RegExpBytecodeGenerator::ReadStackPointerFromRegister(int register_index) { | |
| Emit<RegExpBytecode::kReadStackPointerFromRegister>(register_index); | |
| } | |
| void RegExpBytecodeGenerator::SetCurrentPositionFromEnd(int by) { | |
| Emit<RegExpBytecode::kSetCurrentPositionFromEnd>(by); | |
| } | |
| void RegExpBytecodeGenerator::SetRegister(int register_index, int to) { | |
| Emit<RegExpBytecode::kSetRegister>(register_index, to); | |
| } | |
| void RegExpBytecodeGenerator::AdvanceRegister(int register_index, int by) { | |
| Emit<RegExpBytecode::kAdvanceRegister>(register_index, by); | |
| } | |
| void RegExpBytecodeGenerator::PopCurrentPosition() { | |
| Emit<RegExpBytecode::kPopCurrentPosition>(); | |
| } | |
| void RegExpBytecodeGenerator::PushCurrentPosition() { | |
| Emit<RegExpBytecode::kPushCurrentPosition>(); | |
| } | |
| void RegExpBytecodeGenerator::Backtrack() { | |
| int error_code = can_fallback() ? RegExpStatics::RE_FALLBACK_TO_EXPERIMENTAL | |
| : RegExpStatics::RE_FAILURE; | |
| Emit<RegExpBytecode::kBacktrack>(error_code); | |
| } | |
| void RegExpBytecodeGenerator::GoTo(V8Label* label) { | |
| Emit<RegExpBytecode::kGoTo>(label); | |
| } | |
| void RegExpBytecodeGenerator::PushBacktrack(V8Label* label) { | |
| Emit<RegExpBytecode::kPushBacktrack>(label); | |
| } | |
| bool RegExpBytecodeGenerator::Succeed() { | |
| Emit<RegExpBytecode::kSucceed>(); | |
| return false; // Restart matching for global regexp not supported. | |
| } | |
| void RegExpBytecodeGenerator::Fail() { | |
| Emit<RegExpBytecode::kFail>(); | |
| } | |
| void RegExpBytecodeGenerator::AdvanceCurrentPosition(int by) { | |
| Emit<RegExpBytecode::kAdvanceCurrentPosition>(by); | |
| } | |
| void RegExpBytecodeGenerator::CheckFixedLengthLoop( | |
| V8Label* on_tos_equals_current_position) { | |
| Emit<RegExpBytecode::kCheckFixedLengthLoop>(on_tos_equals_current_position); | |
| } | |
| void RegExpBytecodeGenerator::CheckPosition(int cp_offset, | |
| V8Label* on_outside_input) { | |
| Emit<RegExpBytecode::kCheckPosition>(cp_offset, on_outside_input); | |
| } | |
| void RegExpBytecodeGenerator::CheckSpecialClassRanges(StandardCharacterSet type, | |
| V8Label* on_no_match) { | |
| ASSERT(CanOptimizeSpecialClassRanges(type)); | |
| Emit<RegExpBytecode::kCheckSpecialClassRanges>(type, on_no_match); | |
| } | |
| void RegExpBytecodeGenerator::LoadCurrentCharacterImpl(int cp_offset, | |
| V8Label* on_failure, | |
| bool check_bounds, | |
| int characters, | |
| int eats_at_least) { | |
| DCHECK_GE(eats_at_least, characters); | |
| if (eats_at_least > characters && check_bounds) { | |
| Emit<RegExpBytecode::kCheckPosition>(cp_offset + eats_at_least - 1, | |
| on_failure); | |
| check_bounds = false; // Load below doesn't need to check. | |
| } | |
| CHECK(base::IsInRange(cp_offset, kMinCPOffset, kMaxCPOffset)); | |
| if (check_bounds) { | |
| if (characters == 4) { | |
| Emit<RegExpBytecode::kLoad4CurrentChars>(cp_offset, on_failure); | |
| } else if (characters == 2) { | |
| Emit<RegExpBytecode::kLoad2CurrentChars>(cp_offset, on_failure); | |
| } else { | |
| DCHECK_EQ(1, characters); | |
| Emit<RegExpBytecode::kLoadCurrentCharacter>(cp_offset, on_failure); | |
| } | |
| } else { | |
| if (characters == 4) { | |
| Emit<RegExpBytecode::kLoad4CurrentCharsUnchecked>(cp_offset); | |
| } else if (characters == 2) { | |
| Emit<RegExpBytecode::kLoad2CurrentCharsUnchecked>(cp_offset); | |
| } else { | |
| DCHECK_EQ(1, characters); | |
| Emit<RegExpBytecode::kLoadCurrentCharacterUnchecked>(cp_offset); | |
| } | |
| } | |
| } | |
| void RegExpBytecodeGenerator::CheckCharacterLT(uint16_t limit, | |
| V8Label* on_less) { | |
| Emit<RegExpBytecode::kCheckCharacterLT>(limit, on_less); | |
| } | |
| void RegExpBytecodeGenerator::CheckCharacterGT(uint16_t limit, | |
| V8Label* on_greater) { | |
| Emit<RegExpBytecode::kCheckCharacterGT>(limit, on_greater); | |
| } | |
| void RegExpBytecodeGenerator::CheckCharacter(uint32_t c, V8Label* on_equal) { | |
| if (c > kMaxSingleCharValue) { | |
| Emit<RegExpBytecode::kCheck4Chars>(c, on_equal); | |
| } else { | |
| Emit<RegExpBytecode::kCheckCharacter>(c, on_equal); | |
| } | |
| } | |
| void RegExpBytecodeGenerator::CheckAtStart(int cp_offset, | |
| V8Label* on_at_start) { | |
| Emit<RegExpBytecode::kCheckAtStart>(cp_offset, on_at_start); | |
| } | |
| void RegExpBytecodeGenerator::CheckNotAtStart(int cp_offset, | |
| V8Label* on_not_at_start) { | |
| Emit<RegExpBytecode::kCheckNotAtStart>(cp_offset, on_not_at_start); | |
| } | |
| void RegExpBytecodeGenerator::CheckNotCharacter(uint32_t c, | |
| V8Label* on_not_equal) { | |
| if (c > kMaxSingleCharValue) { | |
| Emit<RegExpBytecode::kCheckNot4Chars>(c, on_not_equal); | |
| } else { | |
| Emit<RegExpBytecode::kCheckNotCharacter>(c, on_not_equal); | |
| } | |
| } | |
| void RegExpBytecodeGenerator::CheckCharacterAfterAnd(uint32_t c, | |
| uint32_t mask, | |
| V8Label* on_equal) { | |
| // TODO(pthier): This is super hacky. We could still check for 4 characters | |
| // (with the last 2 being 0 after masking them), but not emit AndCheck4Chars. | |
| // This is rather confusing and should be changed. | |
| if (c > kMaxSingleCharValue) { | |
| Emit<RegExpBytecode::kAndCheck4Chars>(c, mask, on_equal); | |
| } else { | |
| Emit<RegExpBytecode::kCheckCharacterAfterAnd>(c, mask, on_equal); | |
| } | |
| } | |
| void RegExpBytecodeGenerator::CheckNotCharacterAfterAnd(uint32_t c, | |
| uint32_t mask, | |
| V8Label* on_not_equal) { | |
| // TODO(pthier): This is super hacky. We could still check for 4 characters | |
| // (with the last 2 being 0 after masking them), but not emit AndCheck4Chars. | |
| // This is rather confusing and should be changed. | |
| if (c > kMaxSingleCharValue) { | |
| Emit<RegExpBytecode::kAndCheckNot4Chars>(c, mask, on_not_equal); | |
| } else { | |
| Emit<RegExpBytecode::kCheckNotCharacterAfterAnd>(c, mask, on_not_equal); | |
| } | |
| } | |
| void RegExpBytecodeGenerator::CheckNotCharacterAfterMinusAnd( | |
| uint16_t c, | |
| uint16_t minus, | |
| uint16_t mask, | |
| V8Label* on_not_equal) { | |
| Emit<RegExpBytecode::kCheckNotCharacterAfterMinusAnd>(c, minus, mask, | |
| on_not_equal); | |
| } | |
| void RegExpBytecodeGenerator::CheckCharacterInRange(uint16_t from, | |
| uint16_t to, | |
| V8Label* on_in_range) { | |
| Emit<RegExpBytecode::kCheckCharacterInRange>(from, to, on_in_range); | |
| } | |
| void RegExpBytecodeGenerator::CheckCharacterNotInRange( | |
| uint16_t from, | |
| uint16_t to, | |
| V8Label* on_not_in_range) { | |
| Emit<RegExpBytecode::kCheckCharacterNotInRange>(from, to, on_not_in_range); | |
| } | |
| void RegExpBytecodeGenerator::CheckBitInTable(const TypedData& table, | |
| V8Label* on_bit_set) { | |
| Emit<RegExpBytecode::kCheckBitInTable>(on_bit_set, &table); | |
| } | |
| void RegExpBytecodeGenerator::SkipUntilBitInTable(int cp_offset, | |
| const TypedData& table, | |
| const TypedData& nibble_table, | |
| int advance_by, | |
| V8Label* on_match, | |
| V8Label* on_no_match) { | |
| Emit<RegExpBytecode::kSkipUntilBitInTable>(cp_offset, advance_by, &table, | |
| on_match, on_no_match); | |
| } | |
| void RegExpBytecodeGenerator::SkipUntilCharAnd(int cp_offset, | |
| int advance_by, | |
| unsigned character, | |
| unsigned mask, | |
| int eats_at_least, | |
| V8Label* on_match, | |
| V8Label* on_no_match) { | |
| Emit<RegExpBytecode::kSkipUntilCharAnd>(cp_offset, advance_by, character, | |
| mask, eats_at_least, on_match, | |
| on_no_match); | |
| } | |
| void RegExpBytecodeGenerator::SkipUntilChar(int cp_offset, | |
| int advance_by, | |
| unsigned character, | |
| V8Label* on_match, | |
| V8Label* on_no_match) { | |
| // Only generated by peephole optimization. | |
| UNREACHABLE(); | |
| } | |
| void RegExpBytecodeGenerator::SkipUntilCharPosChecked(int cp_offset, | |
| int advance_by, | |
| unsigned character, | |
| int eats_at_least, | |
| V8Label* on_match, | |
| V8Label* on_no_match) { | |
| // Only generated by peephole optimization. | |
| UNREACHABLE(); | |
| } | |
| void RegExpBytecodeGenerator::SkipUntilCharOrChar(int cp_offset, | |
| int advance_by, | |
| unsigned char1, | |
| unsigned char2, | |
| V8Label* on_match, | |
| V8Label* on_no_match) { | |
| // Only generated by peephole optimization. | |
| UNREACHABLE(); | |
| } | |
| void RegExpBytecodeGenerator::SkipUntilGtOrNotBitInTable(int cp_offset, | |
| int advance_by, | |
| unsigned character, | |
| const TypedData& table, | |
| V8Label* on_match, | |
| V8Label* on_no_match) { | |
| // Only generated by peephole optimization. | |
| UNREACHABLE(); | |
| } | |
| void RegExpBytecodeGenerator::SkipUntilOneOfMasked(int cp_offset, | |
| int advance_by, | |
| unsigned both_chars, | |
| unsigned both_mask, | |
| int max_offset, | |
| unsigned chars1, | |
| unsigned mask1, | |
| unsigned chars2, | |
| unsigned mask2, | |
| V8Label* on_match1, | |
| V8Label* on_match2, | |
| V8Label* on_failure) { | |
| // Only generated by peephole optimization. | |
| UNREACHABLE(); | |
| } | |
| void RegExpBytecodeGenerator::SkipUntilOneOfMasked3( | |
| const SkipUntilOneOfMasked3Args& args) { | |
| // Only generated by peephole optimization. | |
| UNREACHABLE(); | |
| } | |
| void RegExpBytecodeGenerator::CheckNotBackReference(int start_reg, | |
| bool read_backward, | |
| V8Label* on_not_equal) { | |
| if (read_backward) { | |
| Emit<RegExpBytecode::kCheckNotBackRefBackward>(start_reg, on_not_equal); | |
| } else { | |
| Emit<RegExpBytecode::kCheckNotBackRef>(start_reg, on_not_equal); | |
| } | |
| } | |
| void RegExpBytecodeGenerator::CheckNotBackReferenceIgnoreCase( | |
| int start_reg, | |
| bool read_backward, | |
| bool unicode, | |
| V8Label* on_not_equal) { | |
| if (read_backward) { | |
| if (unicode) { | |
| Emit<RegExpBytecode::kCheckNotBackRefNoCaseUnicodeBackward>(start_reg, | |
| on_not_equal); | |
| } else { | |
| Emit<RegExpBytecode::kCheckNotBackRefNoCaseBackward>(start_reg, | |
| on_not_equal); | |
| } | |
| } else { | |
| if (unicode) { | |
| Emit<RegExpBytecode::kCheckNotBackRefNoCaseUnicode>(start_reg, | |
| on_not_equal); | |
| } else { | |
| Emit<RegExpBytecode::kCheckNotBackRefNoCase>(start_reg, on_not_equal); | |
| } | |
| } | |
| } | |
| void RegExpBytecodeGenerator::IfRegisterLT(int register_index, | |
| int comparand, | |
| V8Label* on_less_than) { | |
| Emit<RegExpBytecode::kIfRegisterLT>(register_index, comparand, on_less_than); | |
| } | |
| void RegExpBytecodeGenerator::IfRegisterGE(int register_index, | |
| int comparand, | |
| V8Label* on_greater_or_equal) { | |
| Emit<RegExpBytecode::kIfRegisterGE>(register_index, comparand, | |
| on_greater_or_equal); | |
| } | |
| void RegExpBytecodeGenerator::IfRegisterEqPos(int register_index, | |
| V8Label* on_equal) { | |
| Emit<RegExpBytecode::kIfRegisterEqPos>(register_index, on_equal); | |
| } | |
| ObjectPtr RegExpBytecodeGenerator::GetCode(const String& source, | |
| RegExpFlags flags) { | |
| Bind(&backtrack_); | |
| Backtrack(); | |
| if (FLAG_regexp_peephole_optimization) { | |
| UNIMPLEMENTED(); | |
| // return RegExpBytecodePeepholeOptimization::OptimizeBytecode( | |
| // isolate_, zone(), source, this); | |
| return TypedData::null(); | |
| } else { | |
| const TypedData& array = | |
| TypedData::Handle(TypedData::New(kTypedDataUint8ArrayCid, length())); | |
| NoSafepointScope no_safepoint; | |
| CopyBufferTo((uint8_t*)array.DataAddr(0)); | |
| return array.ptr(); | |
| } | |
| } | |
| void RegExpBytecodeWriter::CopyBufferTo(uint8_t* a) const { | |
| base::MemCopy(a, buffer_.data(), length()); | |
| } | |
| // Instantiate template methods. | |
| #define CASE(Name, ...) \ | |
| template void \ | |
| RegExpBytecodeWriter::EmitOperand<RegExpBytecodeOperandType::k##Name>( \ | |
| RegExpOperandTypeTraits<RegExpBytecodeOperandType::k##Name>::kCType, \ | |
| int); | |
| BASIC_BYTECODE_OPERAND_TYPE_LIST(CASE) | |
| BASIC_BYTECODE_OPERAND_TYPE_LIMITS_LIST(CASE) | |
| #undef CASE | |
| } // namespace dart |