/*
* 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 <assert.h>
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<ir::DexFile> dex_ir) {
SLICER_CHECK(handlers_.empty());
SLICER_CHECK(tries_.empty());
// first, count the number of try blocks
struct TryBlockEndVisitor : public Visitor {
int tries_count = 0;
bool Visit(TryBlockEnd* try_end) override {
++tries_count;
return true;
}
};
TryBlockEndVisitor visitor;
for (auto instr : instructions_) {
instr->Accept(&visitor);
}
int tries_count = visitor.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<const dex::TryBlock>(0);
ir_code->try_blocks = slicer::ArrayView<const dex::TryBlock>(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