/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "slicer/tryblocks_encoder.h" #include "slicer/chronometer.h" #include "slicer/common.h" #include namespace lir { bool TryBlocksEncoder::Visit(TryBlockEnd* try_end) { const dex::u4 begin_offset = try_end->try_begin->offset; const dex::u4 end_offset = try_end->offset; SLICER_CHECK(end_offset > begin_offset); SLICER_CHECK(end_offset - begin_offset < (1 << 16)); // generate the "try_item" dex::TryBlock try_block = {}; try_block.start_addr = begin_offset; try_block.insn_count = end_offset - begin_offset; try_block.handler_off = handlers_.size(); tries_.Push(try_block); // generate the "encoded_catch_handler" dex::s4 catch_count = try_end->handlers.size(); handlers_.PushSLeb128(try_end->catch_all ? -catch_count : catch_count); for (int catch_index = 0; catch_index < catch_count; ++catch_index) { const CatchHandler& handler = try_end->handlers[catch_index]; // type_idx handlers_.PushULeb128(handler.ir_type->orig_index); // address SLICER_CHECK(handler.label->offset != kInvalidOffset); handlers_.PushULeb128(handler.label->offset); } if (try_end->catch_all != nullptr) { // address SLICER_CHECK(try_end->catch_all->offset != kInvalidOffset); handlers_.PushULeb128(try_end->catch_all->offset); } return true; } void TryBlocksEncoder::Encode(ir::Code* ir_code, std::shared_ptr dex_ir) { SLICER_CHECK(handlers_.empty()); SLICER_CHECK(tries_.empty()); // first, count the number of try blocks int tries_count = 0; for (auto instr : instructions_) { if (instr->IsA()) { ++tries_count; } } SLICER_CHECK(tries_count < (1 << 16)); // no try blocks? if (tries_count == 0) { ir_code->try_blocks = {}; ir_code->catch_handlers = {}; return; } // "encoded_catch_handler_list.size" handlers_.PushULeb128(tries_count); // generate the try blocks & encoded catch handlers // // NOTE: try_item[tries_count] : // "Elements of the array must be non-overlapping in range and // in order from low to high address. This element is only present // if tries_size is non-zero" // // NOTE: we're not de-duplicating catch_handlers // (generate one catch_handler for each try block) // for (auto instr : instructions_) { instr->Accept(this); } SLICER_CHECK(!tries_.empty()); SLICER_CHECK(!handlers_.empty()); tries_.Seal(1); handlers_.Seal(1); // update ir::Code auto tries_ptr = tries_.ptr(0); ir_code->try_blocks = slicer::ArrayView(tries_ptr, tries_count); ir_code->catch_handlers = slicer::MemView(handlers_.data(), handlers_.size()); // attach the generated try/catch blocks to the dex IR dex_ir->AttachBuffer(std::move(tries_)); dex_ir->AttachBuffer(std::move(handlers_)); } } // namespace lir