in libredex/TypeInference.cpp [385:1077]
void TypeInference::analyze_instruction(const IRInstruction* insn,
TypeEnvironment* current_state,
const cfg::Block* current_block) const {
switch (insn->opcode()) {
case IOPCODE_LOAD_PARAM:
case IOPCODE_LOAD_PARAM_OBJECT:
case IOPCODE_LOAD_PARAM_WIDE: {
// IOPCODE_LOAD_PARAM_* instructions have been processed before the
// analysis.
break;
}
case OPCODE_NOP: {
break;
}
case OPCODE_MOVE: {
refine_scalar(current_state, insn->src(0));
set_type(current_state, insn->dest(),
current_state->get_type(insn->src(0)));
break;
}
case OPCODE_MOVE_OBJECT: {
refine_reference(current_state, insn->src(0));
if (current_state->get_type(insn->src(0)) == TypeDomain(REFERENCE)) {
const auto dex_type = current_state->get_type_domain(insn->src(0));
set_reference(current_state, insn->dest(), dex_type);
} else {
set_type(current_state, insn->dest(),
current_state->get_type(insn->src(0)));
}
break;
}
case OPCODE_MOVE_WIDE: {
refine_wide_scalar(current_state, insn->src(0));
TypeDomain td1 = current_state->get_type(insn->src(0));
TypeDomain td2 = current_state->get_type(insn->src(0) + 1);
set_type(current_state, insn->dest(), td1);
set_type(current_state, insn->dest() + 1, td2);
break;
}
case IOPCODE_MOVE_RESULT_PSEUDO:
case OPCODE_MOVE_RESULT: {
refine_scalar(current_state, RESULT_REGISTER);
set_type(current_state, insn->dest(),
current_state->get_type(RESULT_REGISTER));
break;
}
case IOPCODE_MOVE_RESULT_PSEUDO_OBJECT:
case OPCODE_MOVE_RESULT_OBJECT: {
refine_reference(current_state, RESULT_REGISTER);
set_reference(current_state,
insn->dest(),
current_state->get_type_domain(RESULT_REGISTER));
break;
}
case IOPCODE_MOVE_RESULT_PSEUDO_WIDE:
case OPCODE_MOVE_RESULT_WIDE: {
refine_wide_scalar(current_state, RESULT_REGISTER);
set_type(current_state, insn->dest(),
current_state->get_type(RESULT_REGISTER));
set_type(current_state,
insn->dest() + 1,
current_state->get_type(RESULT_REGISTER + 1));
break;
}
case OPCODE_MOVE_EXCEPTION: {
if (!current_block) {
// We bail just in case the current block is dangling.
TRACE(TYPE, 2,
"Warning: Can't infer exception type from unknown catch block.");
set_reference(current_state, insn->dest(), type::java_lang_Throwable());
break;
}
// The block that contained this instruction must be a catch block.
const auto& preds = current_block->preds();
if (preds.empty()) {
// We bail just in case the current block is dangling.
TRACE(TYPE, 2,
"Warning: Catch block doesn't have at least one predecessor.");
set_reference(current_state, insn->dest(), type::java_lang_Throwable());
break;
}
std::unordered_set<DexType*> catch_types;
for (cfg::Edge* edge : preds) {
if (edge->type() != cfg::EDGE_THROW) {
continue;
}
DexType* catch_type = edge->throw_info()->catch_type;
if (catch_type) {
catch_types.emplace(catch_type);
} else {
// catch all
catch_types.emplace(type::java_lang_Throwable());
}
}
auto merged_catch_type =
merge_dex_types(catch_types.begin(), catch_types.end(),
/* default */ type::java_lang_Throwable());
set_reference(current_state, insn->dest(), merged_catch_type);
break;
}
case OPCODE_RETURN_VOID: {
break;
}
case OPCODE_RETURN: {
refine_scalar(current_state, insn->src(0));
break;
}
case OPCODE_RETURN_WIDE: {
refine_wide_scalar(current_state, insn->src(0));
break;
}
case OPCODE_RETURN_OBJECT: {
refine_reference(current_state, insn->src(0));
break;
}
case OPCODE_CONST: {
if (insn->get_literal() == 0) {
current_state->set_dex_type(insn->dest(), DexTypeDomain::null());
set_type(current_state, insn->dest(), TypeDomain(ZERO));
} else {
set_type(current_state, insn->dest(), TypeDomain(CONST));
}
break;
}
case OPCODE_CONST_WIDE: {
set_type(current_state, insn->dest(), TypeDomain(CONST1));
set_type(current_state, insn->dest() + 1, TypeDomain(CONST2));
break;
}
case OPCODE_CONST_STRING: {
set_reference(current_state, RESULT_REGISTER, type::java_lang_String());
break;
}
case OPCODE_CONST_CLASS: {
set_reference(current_state, RESULT_REGISTER, type::java_lang_Class());
break;
}
case OPCODE_MONITOR_ENTER:
case OPCODE_MONITOR_EXIT: {
refine_reference(current_state, insn->src(0));
break;
}
case OPCODE_CHECK_CAST: {
refine_reference(current_state, insn->src(0));
auto to_type = insn->get_type();
auto to_cls = type_class(to_type);
if (m_skip_check_cast_to_intf && to_cls && is_interface(to_cls)) {
set_reference(current_state, RESULT_REGISTER,
current_state->get_type_domain(insn->src(0)));
} else {
set_reference(current_state, RESULT_REGISTER, insn->get_type());
}
break;
}
case OPCODE_INSTANCE_OF:
case OPCODE_ARRAY_LENGTH: {
refine_reference(current_state, insn->src(0));
set_integer(current_state, RESULT_REGISTER);
break;
}
case OPCODE_NEW_INSTANCE: {
set_reference(current_state, RESULT_REGISTER, insn->get_type());
break;
}
case OPCODE_NEW_ARRAY: {
refine_integer(current_state, insn->src(0));
set_reference(current_state, RESULT_REGISTER, insn->get_type());
break;
}
case OPCODE_FILLED_NEW_ARRAY: {
const DexType* element_type =
type::get_array_component_type(insn->get_type());
// We assume that structural constraints on the bytecode are satisfied,
// i.e., the type is indeed an array type.
always_assert(element_type != nullptr);
bool is_array_of_references = type::is_object(element_type);
for (size_t i = 0; i < insn->srcs_size(); ++i) {
if (is_array_of_references) {
refine_reference(current_state, insn->src(i));
} else {
refine_scalar(current_state, insn->src(i));
}
}
set_reference(current_state, RESULT_REGISTER, insn->get_type());
break;
}
case OPCODE_FILL_ARRAY_DATA: {
break;
}
case OPCODE_THROW: {
refine_reference(current_state, insn->src(0));
break;
}
case OPCODE_GOTO: {
break;
}
case OPCODE_SWITCH: {
refine_integer(current_state, insn->src(0));
break;
}
case OPCODE_CMPL_FLOAT:
case OPCODE_CMPG_FLOAT: {
refine_float(current_state, insn->src(0));
refine_float(current_state, insn->src(1));
set_integer(current_state, insn->dest());
break;
}
case OPCODE_CMPL_DOUBLE:
case OPCODE_CMPG_DOUBLE: {
refine_double(current_state, insn->src(0));
refine_double(current_state, insn->src(1));
set_integer(current_state, insn->dest());
break;
}
case OPCODE_CMP_LONG: {
refine_long(current_state, insn->src(0));
refine_long(current_state, insn->src(1));
set_integer(current_state, insn->dest());
break;
}
case OPCODE_IF_EQ:
case OPCODE_IF_NE: {
refine_comparable(current_state, insn->src(0), insn->src(1));
break;
}
case OPCODE_IF_LT:
case OPCODE_IF_GE:
case OPCODE_IF_GT:
case OPCODE_IF_LE: {
refine_integer(current_state, insn->src(0));
refine_integer(current_state, insn->src(1));
break;
}
case OPCODE_IF_EQZ:
case OPCODE_IF_NEZ: {
refine_comparable_with_zero(current_state, insn->src(0));
break;
}
case OPCODE_IF_LTZ:
case OPCODE_IF_GEZ:
case OPCODE_IF_GTZ:
case OPCODE_IF_LEZ: {
refine_integer(current_state, insn->src(0));
break;
}
case OPCODE_AGET: {
refine_reference(current_state, insn->src(0));
refine_integer(current_state, insn->src(1));
set_scalar(current_state, RESULT_REGISTER);
break;
}
case OPCODE_AGET_BOOLEAN:
case OPCODE_AGET_BYTE:
case OPCODE_AGET_CHAR:
case OPCODE_AGET_SHORT: {
refine_reference(current_state, insn->src(0));
refine_integer(current_state, insn->src(1));
set_integer(current_state, RESULT_REGISTER);
break;
}
case OPCODE_AGET_WIDE: {
refine_reference(current_state, insn->src(0));
refine_integer(current_state, insn->src(1));
set_wide_scalar(current_state, RESULT_REGISTER);
break;
}
case OPCODE_AGET_OBJECT: {
refine_reference(current_state, insn->src(0));
refine_integer(current_state, insn->src(1));
const auto dex_type_opt = current_state->get_dex_type(insn->src(0));
if (dex_type_opt && *dex_type_opt && type::is_array(*dex_type_opt)) {
const auto etype = type::get_array_component_type(*dex_type_opt);
set_reference(current_state, RESULT_REGISTER, etype);
} else {
set_reference(current_state, RESULT_REGISTER, DexTypeDomain::top());
}
break;
}
case OPCODE_APUT: {
refine_scalar(current_state, insn->src(0));
refine_reference(current_state, insn->src(1));
refine_integer(current_state, insn->src(2));
break;
}
case OPCODE_APUT_BOOLEAN:
case OPCODE_APUT_BYTE:
case OPCODE_APUT_CHAR:
case OPCODE_APUT_SHORT: {
refine_integer(current_state, insn->src(0));
refine_reference(current_state, insn->src(1));
refine_integer(current_state, insn->src(2));
break;
}
case OPCODE_APUT_WIDE: {
refine_wide_scalar(current_state, insn->src(0));
refine_reference(current_state, insn->src(1));
refine_integer(current_state, insn->src(2));
break;
}
case OPCODE_APUT_OBJECT: {
refine_reference(current_state, insn->src(0));
refine_reference(current_state, insn->src(1));
refine_integer(current_state, insn->src(2));
break;
}
case OPCODE_IGET: {
refine_reference(current_state, insn->src(0));
const DexType* type = insn->get_field()->get_type();
if (type::is_float(type)) {
set_float(current_state, RESULT_REGISTER);
} else {
set_integer(current_state, RESULT_REGISTER);
}
break;
}
case OPCODE_IGET_BOOLEAN:
case OPCODE_IGET_BYTE:
case OPCODE_IGET_CHAR:
case OPCODE_IGET_SHORT: {
refine_reference(current_state, insn->src(0));
set_integer(current_state, RESULT_REGISTER);
break;
}
case OPCODE_IGET_WIDE: {
refine_reference(current_state, insn->src(0));
const DexType* type = insn->get_field()->get_type();
if (type::is_double(type)) {
set_double(current_state, RESULT_REGISTER);
} else {
set_long(current_state, RESULT_REGISTER);
}
break;
}
case OPCODE_IGET_OBJECT: {
refine_reference(current_state, insn->src(0));
always_assert(insn->has_field());
const auto field = insn->get_field();
set_reference(current_state, RESULT_REGISTER, field->get_type());
break;
}
case OPCODE_IPUT: {
const DexType* type = insn->get_field()->get_type();
if (type::is_float(type)) {
refine_float(current_state, insn->src(0));
} else {
refine_integer(current_state, insn->src(0));
}
refine_reference(current_state, insn->src(1));
break;
}
case OPCODE_IPUT_BOOLEAN:
case OPCODE_IPUT_BYTE:
case OPCODE_IPUT_CHAR:
case OPCODE_IPUT_SHORT: {
refine_integer(current_state, insn->src(0));
refine_reference(current_state, insn->src(1));
break;
}
case OPCODE_IPUT_WIDE: {
refine_wide_scalar(current_state, insn->src(0));
refine_reference(current_state, insn->src(1));
break;
}
case OPCODE_IPUT_OBJECT: {
refine_reference(current_state, insn->src(0));
refine_reference(current_state, insn->src(1));
break;
}
case OPCODE_SGET: {
DexType* type = insn->get_field()->get_type();
if (type::is_float(type)) {
set_float(current_state, RESULT_REGISTER);
} else {
set_integer(current_state, RESULT_REGISTER);
}
break;
}
case OPCODE_SGET_BOOLEAN:
case OPCODE_SGET_BYTE:
case OPCODE_SGET_CHAR:
case OPCODE_SGET_SHORT: {
set_integer(current_state, RESULT_REGISTER);
break;
}
case OPCODE_SGET_WIDE: {
DexType* type = insn->get_field()->get_type();
if (type::is_double(type)) {
set_double(current_state, RESULT_REGISTER);
} else {
set_long(current_state, RESULT_REGISTER);
}
break;
}
case OPCODE_SGET_OBJECT: {
always_assert(insn->has_field());
const auto field = insn->get_field();
set_reference(current_state, RESULT_REGISTER, field->get_type());
break;
}
case OPCODE_SPUT: {
const DexType* type = insn->get_field()->get_type();
if (type::is_float(type)) {
refine_float(current_state, insn->src(0));
} else {
refine_integer(current_state, insn->src(0));
}
break;
}
case OPCODE_SPUT_BOOLEAN:
case OPCODE_SPUT_BYTE:
case OPCODE_SPUT_CHAR:
case OPCODE_SPUT_SHORT: {
refine_integer(current_state, insn->src(0));
break;
}
case OPCODE_SPUT_WIDE: {
refine_wide_scalar(current_state, insn->src(0));
break;
}
case OPCODE_SPUT_OBJECT: {
refine_reference(current_state, insn->src(0));
break;
}
case OPCODE_INVOKE_CUSTOM:
case OPCODE_INVOKE_POLYMORPHIC: {
// TODO(T59277083)
not_reached_log(
"TypeInference::analyze_instruction does not support "
"invoke-custom and invoke-polymorphic yet");
break;
}
case OPCODE_INVOKE_VIRTUAL:
case OPCODE_INVOKE_SUPER:
case OPCODE_INVOKE_DIRECT:
case OPCODE_INVOKE_STATIC:
case OPCODE_INVOKE_INTERFACE: {
DexMethodRef* dex_method = insn->get_method();
const auto* arg_types = dex_method->get_proto()->get_args();
size_t expected_args =
(insn->opcode() != OPCODE_INVOKE_STATIC ? 1 : 0) + arg_types->size();
always_assert_log(insn->srcs_size() == expected_args, "%s", SHOW(insn));
size_t src_idx{0};
if (insn->opcode() != OPCODE_INVOKE_STATIC) {
// The first argument is a reference to the object instance on which the
// method is invoked.
refine_reference(current_state, insn->src(src_idx++));
}
for (DexType* arg_type : *arg_types) {
if (type::is_object(arg_type)) {
refine_reference(current_state, insn->src(src_idx++));
continue;
}
if (type::is_integer(arg_type)) {
refine_integer(current_state, insn->src(src_idx++));
continue;
}
if (type::is_long(arg_type)) {
refine_long(current_state, insn->src(src_idx++));
continue;
}
if (type::is_float(arg_type)) {
refine_float(current_state, insn->src(src_idx++));
continue;
}
always_assert(type::is_double(arg_type));
refine_double(current_state, insn->src(src_idx++));
}
DexType* return_type = dex_method->get_proto()->get_rtype();
if (type::is_void(return_type)) {
break;
}
if (type::is_object(return_type)) {
set_reference(current_state, RESULT_REGISTER, return_type);
break;
}
if (type::is_integer(return_type)) {
set_integer(current_state, RESULT_REGISTER);
break;
}
if (type::is_long(return_type)) {
set_long(current_state, RESULT_REGISTER);
break;
}
if (type::is_float(return_type)) {
set_float(current_state, RESULT_REGISTER);
break;
}
always_assert(type::is_double(return_type));
set_double(current_state, RESULT_REGISTER);
break;
}
case OPCODE_NEG_INT:
case OPCODE_NOT_INT: {
refine_integer(current_state, insn->src(0));
set_integer(current_state, insn->dest());
break;
}
case OPCODE_NEG_LONG:
case OPCODE_NOT_LONG: {
refine_long(current_state, insn->src(0));
set_long(current_state, insn->dest());
break;
}
case OPCODE_NEG_FLOAT: {
refine_float(current_state, insn->src(0));
set_float(current_state, insn->dest());
break;
}
case OPCODE_NEG_DOUBLE: {
refine_double(current_state, insn->src(0));
set_double(current_state, insn->dest());
break;
}
case OPCODE_INT_TO_BYTE:
case OPCODE_INT_TO_CHAR:
case OPCODE_INT_TO_SHORT: {
refine_integer(current_state, insn->src(0));
set_integer(current_state, insn->dest());
break;
}
case OPCODE_LONG_TO_INT: {
refine_long(current_state, insn->src(0));
set_integer(current_state, insn->dest());
break;
}
case OPCODE_FLOAT_TO_INT: {
refine_float(current_state, insn->src(0));
set_integer(current_state, insn->dest());
break;
}
case OPCODE_DOUBLE_TO_INT: {
refine_double(current_state, insn->src(0));
set_integer(current_state, insn->dest());
break;
}
case OPCODE_INT_TO_LONG: {
refine_integer(current_state, insn->src(0));
set_long(current_state, insn->dest());
break;
}
case OPCODE_FLOAT_TO_LONG: {
refine_float(current_state, insn->src(0));
set_long(current_state, insn->dest());
break;
}
case OPCODE_DOUBLE_TO_LONG: {
refine_double(current_state, insn->src(0));
set_long(current_state, insn->dest());
break;
}
case OPCODE_INT_TO_FLOAT: {
refine_integer(current_state, insn->src(0));
set_float(current_state, insn->dest());
break;
}
case OPCODE_LONG_TO_FLOAT: {
refine_long(current_state, insn->src(0));
set_float(current_state, insn->dest());
break;
}
case OPCODE_DOUBLE_TO_FLOAT: {
refine_double(current_state, insn->src(0));
set_float(current_state, insn->dest());
break;
}
case OPCODE_INT_TO_DOUBLE: {
refine_integer(current_state, insn->src(0));
set_double(current_state, insn->dest());
break;
}
case OPCODE_LONG_TO_DOUBLE: {
refine_long(current_state, insn->src(0));
set_double(current_state, insn->dest());
break;
}
case OPCODE_FLOAT_TO_DOUBLE: {
refine_float(current_state, insn->src(0));
set_double(current_state, insn->dest());
break;
}
case OPCODE_ADD_INT:
case OPCODE_SUB_INT:
case OPCODE_MUL_INT:
case OPCODE_AND_INT:
case OPCODE_OR_INT:
case OPCODE_XOR_INT:
case OPCODE_SHL_INT:
case OPCODE_SHR_INT:
case OPCODE_USHR_INT: {
refine_integer(current_state, insn->src(0));
refine_integer(current_state, insn->src(1));
set_integer(current_state, insn->dest());
break;
}
case OPCODE_DIV_INT:
case OPCODE_REM_INT: {
refine_integer(current_state, insn->src(0));
refine_integer(current_state, insn->src(1));
set_integer(current_state, RESULT_REGISTER);
break;
}
case OPCODE_ADD_LONG:
case OPCODE_SUB_LONG:
case OPCODE_MUL_LONG:
case OPCODE_AND_LONG:
case OPCODE_OR_LONG:
case OPCODE_XOR_LONG: {
refine_long(current_state, insn->src(0));
refine_long(current_state, insn->src(1));
set_long(current_state, insn->dest());
break;
}
case OPCODE_DIV_LONG:
case OPCODE_REM_LONG: {
refine_long(current_state, insn->src(0));
refine_long(current_state, insn->src(1));
set_long(current_state, RESULT_REGISTER);
break;
}
case OPCODE_SHL_LONG:
case OPCODE_SHR_LONG:
case OPCODE_USHR_LONG: {
refine_long(current_state, insn->src(0));
refine_integer(current_state, insn->src(1));
set_long(current_state, insn->dest());
break;
}
case OPCODE_ADD_FLOAT:
case OPCODE_SUB_FLOAT:
case OPCODE_MUL_FLOAT:
case OPCODE_DIV_FLOAT:
case OPCODE_REM_FLOAT: {
refine_float(current_state, insn->src(0));
refine_float(current_state, insn->src(1));
set_float(current_state, insn->dest());
break;
}
case OPCODE_ADD_DOUBLE:
case OPCODE_SUB_DOUBLE:
case OPCODE_MUL_DOUBLE:
case OPCODE_DIV_DOUBLE:
case OPCODE_REM_DOUBLE: {
refine_double(current_state, insn->src(0));
refine_double(current_state, insn->src(1));
set_double(current_state, insn->dest());
break;
}
case OPCODE_ADD_INT_LIT16:
case OPCODE_RSUB_INT:
case OPCODE_MUL_INT_LIT16:
case OPCODE_AND_INT_LIT16:
case OPCODE_OR_INT_LIT16:
case OPCODE_XOR_INT_LIT16:
case OPCODE_ADD_INT_LIT8:
case OPCODE_RSUB_INT_LIT8:
case OPCODE_MUL_INT_LIT8:
case OPCODE_AND_INT_LIT8:
case OPCODE_OR_INT_LIT8:
case OPCODE_XOR_INT_LIT8:
case OPCODE_SHL_INT_LIT8:
case OPCODE_SHR_INT_LIT8:
case OPCODE_USHR_INT_LIT8: {
refine_integer(current_state, insn->src(0));
set_integer(current_state, insn->dest());
break;
}
case OPCODE_DIV_INT_LIT16:
case OPCODE_REM_INT_LIT16:
case OPCODE_DIV_INT_LIT8:
case OPCODE_REM_INT_LIT8: {
refine_integer(current_state, insn->src(0));
set_integer(current_state, RESULT_REGISTER);
break;
}
case IOPCODE_INIT_CLASS: {
break;
}
}
// If the opcode does not set the RESULT_REGISTER, clear it.
if (!insn->has_move_result_any()) {
set_type(current_state, RESULT_REGISTER, TypeDomain::top());
current_state->reset_dex_type(RESULT_REGISTER);
}
}