in libredex/IRTypeChecker.cpp [894:1452]
void IRTypeChecker::check_instruction(IRInstruction* insn,
TypeEnvironment* current_state) 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: {
assume_scalar(current_state, insn->src(0), /* in_move */ true);
break;
}
case OPCODE_MOVE_OBJECT: {
assume_reference(current_state, insn->src(0), /* in_move */ true);
break;
}
case OPCODE_MOVE_WIDE: {
assume_wide_scalar(current_state, insn->src(0));
break;
}
case IOPCODE_MOVE_RESULT_PSEUDO:
case OPCODE_MOVE_RESULT: {
assume_scalar(current_state, RESULT_REGISTER);
break;
}
case IOPCODE_MOVE_RESULT_PSEUDO_OBJECT:
case OPCODE_MOVE_RESULT_OBJECT: {
assume_reference(current_state, RESULT_REGISTER);
break;
}
case IOPCODE_MOVE_RESULT_PSEUDO_WIDE:
case OPCODE_MOVE_RESULT_WIDE: {
assume_wide_scalar(current_state, RESULT_REGISTER);
break;
}
case OPCODE_MOVE_EXCEPTION: {
// We don't know where to grab the type of the just-caught exception.
// Simply set to j.l.Throwable here.
break;
}
case OPCODE_RETURN_VOID: {
break;
}
case OPCODE_RETURN: {
assume_scalar(current_state, insn->src(0));
break;
}
case OPCODE_RETURN_WIDE: {
assume_wide_scalar(current_state, insn->src(0));
break;
}
case OPCODE_RETURN_OBJECT: {
assume_reference(current_state, insn->src(0));
auto dtype = current_state->get_dex_type(insn->src(0));
auto rtype = m_dex_method->get_proto()->get_rtype();
// If the inferred type is a fallback, there's no point performing the
// accurate type assignment checking.
if (dtype && !is_inference_fallback_type(*dtype)) {
// Return type checking is non-strict: it is allowed to return any
// reference type when `rtype` is an interface.
if (!check_is_assignable_from(*dtype, rtype, /*strict=*/false)) {
std::ostringstream out;
out << "Returning " << dtype << ", but expected from declaration "
<< rtype << std::endl;
throw TypeCheckingException(out.str());
}
}
break;
}
case OPCODE_CONST: {
break;
}
case OPCODE_CONST_WIDE: {
break;
}
case OPCODE_CONST_STRING: {
break;
}
case OPCODE_CONST_CLASS: {
break;
}
case OPCODE_MONITOR_ENTER:
case OPCODE_MONITOR_EXIT: {
assume_reference(current_state, insn->src(0));
break;
}
case OPCODE_CHECK_CAST: {
assume_reference(current_state, insn->src(0));
break;
}
case OPCODE_INSTANCE_OF:
case OPCODE_ARRAY_LENGTH: {
assume_reference(current_state, insn->src(0));
break;
}
case OPCODE_NEW_INSTANCE: {
break;
}
case OPCODE_NEW_ARRAY: {
assume_integer(current_state, insn->src(0));
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) {
assume_reference(current_state, insn->src(i));
} else {
assume_scalar(current_state, insn->src(i));
}
}
break;
}
case OPCODE_FILL_ARRAY_DATA: {
break;
}
case OPCODE_THROW: {
assume_reference(current_state, insn->src(0));
break;
}
case OPCODE_GOTO: {
break;
}
case OPCODE_SWITCH: {
assume_integer(current_state, insn->src(0));
break;
}
case OPCODE_CMPL_FLOAT:
case OPCODE_CMPG_FLOAT: {
assume_float(current_state, insn->src(0));
assume_float(current_state, insn->src(1));
break;
}
case OPCODE_CMPL_DOUBLE:
case OPCODE_CMPG_DOUBLE: {
assume_double(current_state, insn->src(0));
assume_double(current_state, insn->src(1));
break;
}
case OPCODE_CMP_LONG: {
assume_long(current_state, insn->src(0));
assume_long(current_state, insn->src(1));
break;
}
case OPCODE_IF_EQ:
case OPCODE_IF_NE: {
assume_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: {
assume_integer(current_state, insn->src(0));
assume_integer(current_state, insn->src(1));
break;
}
case OPCODE_IF_EQZ:
case OPCODE_IF_NEZ: {
assume_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: {
assume_integer(current_state, insn->src(0));
break;
}
case OPCODE_AGET: {
assume_reference(current_state, insn->src(0));
assume_integer(current_state, insn->src(1));
break;
}
case OPCODE_AGET_BOOLEAN:
case OPCODE_AGET_BYTE:
case OPCODE_AGET_CHAR:
case OPCODE_AGET_SHORT: {
assume_reference(current_state, insn->src(0));
assume_integer(current_state, insn->src(1));
break;
}
case OPCODE_AGET_WIDE: {
assume_reference(current_state, insn->src(0));
assume_integer(current_state, insn->src(1));
break;
}
case OPCODE_AGET_OBJECT: {
assume_reference(current_state, insn->src(0));
assume_integer(current_state, insn->src(1));
break;
}
case OPCODE_APUT: {
assume_scalar(current_state, insn->src(0));
assume_reference(current_state, insn->src(1));
assume_integer(current_state, insn->src(2));
break;
}
case OPCODE_APUT_BOOLEAN:
case OPCODE_APUT_BYTE:
case OPCODE_APUT_CHAR:
case OPCODE_APUT_SHORT: {
assume_integer(current_state, insn->src(0));
assume_reference(current_state, insn->src(1));
assume_integer(current_state, insn->src(2));
break;
}
case OPCODE_APUT_WIDE: {
assume_wide_scalar(current_state, insn->src(0));
assume_reference(current_state, insn->src(1));
assume_integer(current_state, insn->src(2));
break;
}
case OPCODE_APUT_OBJECT: {
assume_reference(current_state, insn->src(0));
assume_reference(current_state, insn->src(1));
assume_integer(current_state, insn->src(2));
break;
}
case OPCODE_IGET: {
assume_reference(current_state, insn->src(0));
const auto f_cls = insn->get_field()->get_class();
assume_assignable(current_state->get_dex_type(insn->src(0)), f_cls);
break;
}
case OPCODE_IGET_BOOLEAN:
case OPCODE_IGET_BYTE:
case OPCODE_IGET_CHAR:
case OPCODE_IGET_SHORT:
case OPCODE_IGET_WIDE: {
assume_reference(current_state, insn->src(0));
const auto f_cls = insn->get_field()->get_class();
assume_assignable(current_state->get_dex_type(insn->src(0)), f_cls);
break;
}
case OPCODE_IGET_OBJECT: {
assume_reference(current_state, insn->src(0));
always_assert(insn->has_field());
const auto f_cls = insn->get_field()->get_class();
assume_assignable(current_state->get_dex_type(insn->src(0)), f_cls);
break;
}
case OPCODE_IPUT: {
const DexType* type = insn->get_field()->get_type();
if (type::is_float(type)) {
assume_float(current_state, insn->src(0));
} else {
assume_integer(current_state, insn->src(0));
}
assume_reference(current_state, insn->src(1));
const auto f_cls = insn->get_field()->get_class();
assume_assignable(current_state->get_dex_type(insn->src(1)), f_cls);
break;
}
case OPCODE_IPUT_BOOLEAN:
case OPCODE_IPUT_BYTE:
case OPCODE_IPUT_CHAR:
case OPCODE_IPUT_SHORT: {
assume_integer(current_state, insn->src(0));
assume_reference(current_state, insn->src(1));
const auto f_cls = insn->get_field()->get_class();
assume_assignable(current_state->get_dex_type(insn->src(1)), f_cls);
break;
}
case OPCODE_IPUT_WIDE: {
assume_wide_scalar(current_state, insn->src(0));
assume_reference(current_state, insn->src(1));
const auto f_cls = insn->get_field()->get_class();
assume_assignable(current_state->get_dex_type(insn->src(1)), f_cls);
break;
}
case OPCODE_IPUT_OBJECT: {
assume_reference(current_state, insn->src(0));
assume_reference(current_state, insn->src(1));
always_assert(insn->has_field());
const auto f_type = insn->get_field()->get_type();
assume_assignable(current_state->get_dex_type(insn->src(0)), f_type);
const auto f_cls = insn->get_field()->get_class();
assume_assignable(current_state->get_dex_type(insn->src(1)), f_cls);
break;
}
case OPCODE_SGET: {
break;
}
case OPCODE_SGET_BOOLEAN:
case OPCODE_SGET_BYTE:
case OPCODE_SGET_CHAR:
case OPCODE_SGET_SHORT: {
break;
}
case OPCODE_SGET_WIDE: {
break;
}
case OPCODE_SGET_OBJECT: {
break;
}
case OPCODE_SPUT: {
const DexType* type = insn->get_field()->get_type();
if (type::is_float(type)) {
assume_float(current_state, insn->src(0));
} else {
assume_integer(current_state, insn->src(0));
}
break;
}
case OPCODE_SPUT_BOOLEAN:
case OPCODE_SPUT_BYTE:
case OPCODE_SPUT_CHAR:
case OPCODE_SPUT_SHORT: {
assume_integer(current_state, insn->src(0));
break;
}
case OPCODE_SPUT_WIDE: {
assume_wide_scalar(current_state, insn->src(0));
break;
}
case OPCODE_SPUT_OBJECT: {
assume_reference(current_state, insn->src(0));
break;
}
case OPCODE_INVOKE_CUSTOM:
case OPCODE_INVOKE_POLYMORPHIC:
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();
if (insn->srcs_size() != expected_args) {
std::ostringstream out;
out << SHOW(insn) << ": argument count mismatch; "
<< "expected " << expected_args << ", "
<< "but found " << insn->srcs_size() << " instead";
throw TypeCheckingException(out.str());
}
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.
auto src = insn->src(src_idx++);
assume_reference(current_state, src);
assume_assignable(current_state->get_dex_type(src),
dex_method->get_class());
}
for (DexType* arg_type : *arg_types) {
if (type::is_object(arg_type)) {
auto src = insn->src(src_idx++);
assume_reference(current_state, src);
assume_assignable(current_state->get_dex_type(src), arg_type);
continue;
}
if (type::is_integer(arg_type)) {
assume_integer(current_state, insn->src(src_idx++));
continue;
}
if (type::is_long(arg_type)) {
assume_long(current_state, insn->src(src_idx++));
continue;
}
if (type::is_float(arg_type)) {
assume_float(current_state, insn->src(src_idx++));
continue;
}
always_assert(type::is_double(arg_type));
assume_double(current_state, insn->src(src_idx++));
}
if (m_validate_access) {
auto resolved =
resolve_method(dex_method, opcode_to_search(insn), m_dex_method);
::validate_access(m_dex_method, resolved);
}
if (m_validate_invoke_super && insn->opcode() == OPCODE_INVOKE_SUPER) {
validate_invoke_super(dex_method);
}
break;
}
case OPCODE_NEG_INT:
case OPCODE_NOT_INT: {
assume_integer(current_state, insn->src(0));
break;
}
case OPCODE_NEG_LONG:
case OPCODE_NOT_LONG: {
assume_long(current_state, insn->src(0));
break;
}
case OPCODE_NEG_FLOAT: {
assume_float(current_state, insn->src(0));
break;
}
case OPCODE_NEG_DOUBLE: {
assume_double(current_state, insn->src(0));
break;
}
case OPCODE_INT_TO_BYTE:
case OPCODE_INT_TO_CHAR:
case OPCODE_INT_TO_SHORT: {
assume_integer(current_state, insn->src(0));
break;
}
case OPCODE_LONG_TO_INT: {
assume_long(current_state, insn->src(0));
break;
}
case OPCODE_FLOAT_TO_INT: {
assume_float(current_state, insn->src(0));
break;
}
case OPCODE_DOUBLE_TO_INT: {
assume_double(current_state, insn->src(0));
break;
}
case OPCODE_INT_TO_LONG: {
assume_integer(current_state, insn->src(0));
break;
}
case OPCODE_FLOAT_TO_LONG: {
assume_float(current_state, insn->src(0));
break;
}
case OPCODE_DOUBLE_TO_LONG: {
assume_double(current_state, insn->src(0));
break;
}
case OPCODE_INT_TO_FLOAT: {
assume_integer(current_state, insn->src(0));
break;
}
case OPCODE_LONG_TO_FLOAT: {
assume_long(current_state, insn->src(0));
break;
}
case OPCODE_DOUBLE_TO_FLOAT: {
assume_double(current_state, insn->src(0));
break;
}
case OPCODE_INT_TO_DOUBLE: {
assume_integer(current_state, insn->src(0));
break;
}
case OPCODE_LONG_TO_DOUBLE: {
assume_long(current_state, insn->src(0));
break;
}
case OPCODE_FLOAT_TO_DOUBLE: {
assume_float(current_state, insn->src(0));
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: {
assume_integer(current_state, insn->src(0));
assume_integer(current_state, insn->src(1));
break;
}
case OPCODE_DIV_INT:
case OPCODE_REM_INT: {
assume_integer(current_state, insn->src(0));
assume_integer(current_state, insn->src(1));
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: {
assume_long(current_state, insn->src(0));
assume_long(current_state, insn->src(1));
break;
}
case OPCODE_DIV_LONG:
case OPCODE_REM_LONG: {
assume_long(current_state, insn->src(0));
assume_long(current_state, insn->src(1));
break;
}
case OPCODE_SHL_LONG:
case OPCODE_SHR_LONG:
case OPCODE_USHR_LONG: {
assume_long(current_state, insn->src(0));
assume_integer(current_state, insn->src(1));
break;
}
case OPCODE_ADD_FLOAT:
case OPCODE_SUB_FLOAT:
case OPCODE_MUL_FLOAT:
case OPCODE_DIV_FLOAT:
case OPCODE_REM_FLOAT: {
assume_float(current_state, insn->src(0));
assume_float(current_state, insn->src(1));
break;
}
case OPCODE_ADD_DOUBLE:
case OPCODE_SUB_DOUBLE:
case OPCODE_MUL_DOUBLE:
case OPCODE_DIV_DOUBLE:
case OPCODE_REM_DOUBLE: {
assume_double(current_state, insn->src(0));
assume_double(current_state, insn->src(1));
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: {
assume_integer(current_state, insn->src(0));
break;
}
case OPCODE_DIV_INT_LIT16:
case OPCODE_REM_INT_LIT16:
case OPCODE_DIV_INT_LIT8:
case OPCODE_REM_INT_LIT8: {
assume_integer(current_state, insn->src(0));
break;
}
case IOPCODE_INIT_CLASS: {
break;
}
}
if (insn->has_field() && m_validate_access) {
auto search = opcode::is_an_sfield_op(insn->opcode())
? FieldSearch::Static
: FieldSearch::Instance;
auto resolved = resolve_field(insn->get_field(), search);
::validate_access(m_dex_method, resolved);
}
}