You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
116 lines
3.6 KiB
116 lines
3.6 KiB
/*
|
|
* 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
|