| // 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 |