void IRTypeChecker::check_instruction()

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);
  }
}