void TypeInference::analyze_instruction()

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