예제 #1
0
static bool validate_sget(DexMethod* context, DexOpcodeField* opfield) {
  auto opcode = opfield->opcode();
  switch (opcode) {
  case OPCODE_SGET_WIDE:
    unhandled_inline++;
    return false;
  case OPCODE_SGET:
  case OPCODE_SGET_BOOLEAN:
  case OPCODE_SGET_BYTE:
  case OPCODE_SGET_CHAR:
  case OPCODE_SGET_SHORT:
    return true;
  default:
    auto field = resolve_field(opfield->field(), FieldSearch::Static);
    always_assert_log(field->is_concrete(), "Must be a concrete field");
    auto value = field->get_static_value();
    always_assert_log(
        false,
        "Unexpected field type in inline_*sget %s for field %s value %s in "
        "method %s\n",
        SHOW(opfield),
        SHOW(field),
        value != nullptr ? value->show().c_str() : "('nullptr')",
        SHOW(context));
  }
  return false;
}
예제 #2
0
void inline_field_values(Scope& fullscope) {
  std::unordered_set<DexField*> inline_field;
  std::unordered_set<DexField*> cheap_inline_field;
  std::vector<DexClass*> scope;
  uint32_t aflags = ACC_STATIC | ACC_FINAL;

  for (auto clazz : fullscope) {
    std::unordered_map<DexField*, bool> blank_statics;
    get_sput_in_clinit(clazz, blank_statics);
    auto sfields = clazz->get_sfields();
    for (auto sfield : sfields) {
      if ((sfield->get_access() & aflags) != aflags) continue;
      if (blank_statics[sfield]) continue;
      auto value = sfield->get_static_value();
      if (value == nullptr && !is_primitive(sfield->get_type())) {
        continue;
      }
      if (value != nullptr && !value->is_evtype_primitive()) {
        continue;
      }
      uint64_t v = value != nullptr ? value->value() : 0;
      if ((v & 0xffff) == v || (v & 0xffff0000) == v) {
        cheap_inline_field.insert(sfield);
      }
      inline_field.insert(sfield);
      scope.push_back(clazz);
    }
  }
  std::vector<std::pair<DexMethod*, DexOpcodeField*>> cheap_rewrites;
  std::vector<std::pair<DexMethod*, DexOpcodeField*>> simple_rewrites;
  walk_opcodes(
      fullscope,
      [](DexMethod* method) { return true; },
      [&](DexMethod* method, DexInstruction* insn) {
        if (insn->has_fields() && is_sfield_op(insn->opcode())) {
          auto fieldop = static_cast<DexOpcodeField*>(insn);
          auto field = resolve_field(fieldop->field(), FieldSearch::Static);
          if (field == nullptr || !field->is_concrete()) return;
          if (inline_field.count(field) == 0) return;
          if (cheap_inline_field.count(field) > 0) {
            cheap_rewrites.push_back(std::make_pair(method, fieldop));
            return;
          }
          simple_rewrites.push_back(std::make_pair(method, fieldop));
        }
      });
  TRACE(FINALINLINE, 1,
          "Method Re-writes Cheap %lu  Simple %lu\n",
          cheap_rewrites.size(),
          simple_rewrites.size());
  for (auto cheapcase : cheap_rewrites) {
    inline_cheap_sget(cheapcase.first, cheapcase.second);
  }
  for (auto simplecase : simple_rewrites) {
    inline_sget(simplecase.first, simplecase.second);
  }
  MethodTransform::sync_all();
}
예제 #3
0
파일: DexUtil.cpp 프로젝트: facebook/redex
void change_visibility(DexMethod* method) {
  auto code = method->get_code();
  always_assert(code != nullptr);

  editable_cfg_adapter::iterate(code, [](MethodItemEntry& mie) {
    auto insn = mie.insn;

    if (insn->has_field()) {
      auto cls = type_class(insn->get_field()->get_class());
      if (cls != nullptr && !cls->is_external()) {
        set_public(cls);
      }
      auto field =
          resolve_field(insn->get_field(), is_sfield_op(insn->opcode())
              ? FieldSearch::Static : FieldSearch::Instance);
      if (field != nullptr && field->is_concrete()) {
        set_public(field);
        set_public(type_class(field->get_class()));
        // FIXME no point in rewriting opcodes in the method
        insn->set_field(field);
      }
    } else if (insn->has_method()) {
      auto cls = type_class(insn->get_method()->get_class());
      if (cls != nullptr && !cls->is_external()) {
        set_public(cls);
      }
      auto current_method = resolve_method(
          insn->get_method(), opcode_to_search(insn));
      if (current_method != nullptr && current_method->is_concrete()) {
        set_public(current_method);
        set_public(type_class(current_method->get_class()));
        // FIXME no point in rewriting opcodes in the method
        insn->set_method(current_method);
      }
    } else if (insn->has_type()) {
      auto type = insn->get_type();
      auto cls = type_class(type);
      if (cls != nullptr && !cls->is_external()) {
        set_public(cls);
      }
    }
    return editable_cfg_adapter::LOOP_CONTINUE;
  });

  std::vector<DexType*> types;
  if (code->editable_cfg_built()) {
    code->cfg().gather_catch_types(types);
  } else {
    code->gather_catch_types(types);
  }
  for (auto type : types) {
    auto cls = type_class(type);
    if (cls != nullptr && !cls->is_external()) {
      set_public(cls);
    }
  }
}
예제 #4
0
/**
 * Check collisions in field definition.
 */
EscapeReason OptimizationImpl::check_field_collision(DexType* intf,
                                                     SingleImplData& data) {
  for (const auto field : data.fielddefs) {
    assert(!single_impls->is_escaped(field->get_class()));
    auto collision =
        resolve_field(field->get_class(), field->get_name(), data.cls);
    if (collision) return FIELD_COLLISION;
  }
  return NO_ESCAPE;
}
예제 #5
0
void inline_sget(DexMethod* method, DexOpcodeField* opfield) {
  if (!validate_sget(method, opfield)) return;
  auto opcode = OPCODE_CONST;
  auto dest = opfield->dest();
  auto field = resolve_field(opfield->field(), FieldSearch::Static);
  always_assert_log(field->is_concrete(), "Must be a concrete field");
  auto value = field->get_static_value();
  /* FIXME for sget_wide case */
  uint32_t v = value != nullptr ? (uint32_t)value->value() : 0;

  auto newopcode = (new DexInstruction(opcode))->set_dest(dest)->set_literal(v);
  replace_opcode(method, opfield, newopcode);
}
예제 #6
0
void analyze_sget(cfg::InstructionIterator it, Environment* env) {
  auto insn = it->insn;
  auto op = insn->opcode();
  if (op == OPCODE_SGET_OBJECT) {
    auto field = resolve_field(insn->get_field(), FieldSearch::Static);
    if (field != nullptr) {
      Info info;
      info.array_field = field;
      env->set(RESULT_REGISTER, Domain(info));
      return;
    }
  }
  analyze_default(it, env);
}
예제 #7
0
std::unordered_set<DexField*> get_called_field_defs(Scope& scope) {
  std::vector<DexField*> field_refs;
  walk_methods(scope,
               [&](DexMethod* method) { method->gather_fields(field_refs); });
  sort_unique(field_refs);
  /* Okay, now we have a complete list of field refs
   * for this particular dex.  Map to the def actually invoked.
   */
  std::unordered_set<DexField*> field_defs;
  for (auto field_ref : field_refs) {
    auto field_def = resolve_field(field_ref);
    if (field_def == nullptr || !field_def->is_concrete()) continue;
    field_defs.insert(field_def);
  }
  return field_defs;
}
예제 #8
0
/**
 * The callee contains a *get/put instruction to an unknown field.
 * Given the caller may not be in the same hierarchy/package we cannot inline
 * it unless we make the field public.
 * But we need to make all fields public across the hierarchy and for fields
 * we don't know we have no idea whether the field was public or not anyway.
 */
bool MultiMethodInliner::unknown_field(DexInstruction* insn, DexMethod* context) {
  if (is_ifield_op(insn->opcode()) || is_sfield_op(insn->opcode())) {
    auto fop = static_cast<DexOpcodeField*>(insn);
    auto field = fop->field();
    field = resolve_field(field, is_sfield_op(insn->opcode())
        ? FieldSearch::Static : FieldSearch::Instance);
    if (field == nullptr) {
      info.escaped_field++;
      return true;
    }
    if (!field->is_concrete() && !is_public(field)) {
      info.non_pub_field++;
      return true;
    }
  }
  return false;
}
예제 #9
0
/**
 * Change the visibility of members accessed in a callee as they are moved
 * to the caller context.
 * We make everything public but we could be more precise and only
 * relax visibility as needed.
 */
void MultiMethodInliner::change_visibility(DexMethod* callee) {
  TRACE(MMINL, 6, "checking visibility usage of members in %s\n",
      SHOW(callee));
  for (auto insn : callee->get_code()->get_instructions()) {
    if (insn->has_fields()) {
      auto fop = static_cast<DexOpcodeField*>(insn);
      auto field = fop->field();
      field = resolve_field(field, is_sfield_op(insn->opcode())
          ? FieldSearch::Static : FieldSearch::Instance);
      if (field != nullptr && field->is_concrete()) {
        TRACE(MMINL, 6, "changing visibility of %s.%s %s\n",
            SHOW(field->get_class()), SHOW(field->get_name()),
            SHOW(field->get_type()));
        set_public(field);
        set_public(type_class(field->get_class()));
        fop->rewrite_field(field);
      }
      continue;
    }
    if (insn->has_methods()) {
      auto mop = static_cast<DexOpcodeMethod*>(insn);
      auto method = mop->get_method();
      method = resolver(method, opcode_to_search(insn));
      if (method != nullptr && method->is_concrete()) {
        TRACE(MMINL, 6, "changing visibility of %s.%s: %s\n",
            SHOW(method->get_class()), SHOW(method->get_name()),
            SHOW(method->get_proto()));
        set_public(method);
        set_public(type_class(method->get_class()));
        mop->rewrite_method(method);
      }
      continue;
    }
    if (insn->has_types()) {
      auto type = static_cast<DexOpcodeType*>(insn)->get_type();
      auto cls = type_class(type);
      if (cls != nullptr && !cls->is_external()) {
        TRACE(MMINL, 6, "changing visibility of %s\n", SHOW(type));
        set_public(cls);
      }
      continue;
    }
  }
}
예제 #10
0
/*
 * There's no "good way" to differentiate blank vs. non-blank
 * finals.  So, we just scan the code in the CL-init.  If
 * it's sput there, then it's a blank.  Lame, agreed, but functional.
 *
 */
void get_sput_in_clinit(DexClass* clazz,
                        std::unordered_map<DexField*, bool>& blank_statics) {
  auto methods = clazz->get_dmethods();
  for (auto method : methods) {
    if (is_clinit(method)) {
      always_assert_log(is_static(method) && is_constructor(method),
          "static constructor doesn't have the proper access bits set\n");
      auto& code = method->get_code();
      auto opcodes = code->get_instructions();
      for (auto opcode : opcodes) {
        if (opcode->has_fields() && is_sput(opcode->opcode())) {
          auto fieldop = static_cast<DexOpcodeField*>(opcode);
          auto field = resolve_field(fieldop->field(), FieldSearch::Static);
          if (field == nullptr || !field->is_concrete()) continue;
          if (field->get_class() != clazz->get_type()) continue;
          blank_statics[field] = true;
        }
      }
    }
  }
}
예제 #11
0
void inline_cheap_sget(DexMethod* method, DexOpcodeField* opfield) {
  if (!validate_sget(method, opfield)) return;
  auto dest = opfield->dest();
  auto field = resolve_field(opfield->field(), FieldSearch::Static);
  always_assert_log(field->is_concrete(), "Must be a concrete field");
  auto value = field->get_static_value();
  /* FIXME for sget_wide case */
  uint32_t v = value != nullptr ? (uint32_t)value->value() : 0;
  auto opcode = [&] {
    if ((v & 0xffff) == v) {
      return OPCODE_CONST_16;
    } else if ((v & 0xffff0000) == v) {
      return OPCODE_CONST_HIGH16;
    }
    always_assert_log(false,
                      "Bad inline_cheap_sget queued up, can't fit to"
                      " CONST_16 or CONST_HIGH16, bailing\n");
  }();

  auto newopcode = (new DexInstruction(opcode, 0))->set_dest(dest)->set_literal(v);
  replace_opcode(method, opfield, newopcode);
}
예제 #12
0
TEST(ResolveField, empty) {
  g_redex = new RedexContext();
  create_scope();

  // different cases for int A.f1
  DexField* fdef = DexField::get_field(DexType::get_type("A"),
      DexString::get_string("f1"), DexType::get_type("I"));
  EXPECT_TRUE(fdef != nullptr && fdef->is_def());
  DexField* fref = make_field_ref(
      DexType::get_type("A"), "f1", DexType::get_type("I"));
  EXPECT_TRUE(fdef->is_def());
  EXPECT_TRUE(resolve_field(fref) == fdef);
  EXPECT_TRUE(resolve_field(fref, FieldSearch::Instance) == fdef);
  EXPECT_TRUE(resolve_field(DexType::get_type("A"),
      DexString::get_string("f1"),
      DexType::get_type("I"),
      FieldSearch::Static) == nullptr);
  EXPECT_TRUE(resolve_field(DexType::get_type("D"),
      DexString::get_string("f1"),
      DexType::get_type("I"),
      FieldSearch::Static) == nullptr);
  EXPECT_TRUE(resolve_field(DexType::get_type("B"),
      DexString::get_string("f1"),
      DexType::get_type("I"),
      FieldSearch::Instance) == fdef);
  EXPECT_TRUE(resolve_field(DexType::get_type("B"),
      DexString::get_string("f1"),
      DexType::get_type("I"),
      FieldSearch::Static) == nullptr);
  EXPECT_TRUE(resolve_field(DexType::get_type("C"),
      DexString::get_string("f1"),
      DexType::get_type("I")) == fdef);
  EXPECT_TRUE(resolve_field(DexType::get_type("C"),
      DexString::get_string("f1"),
      DexType::get_type("I"),
      FieldSearch::Static) == nullptr);
  fref = make_field_ref(
      DexType::get_type("B"), "f1", DexType::get_type("I"));
  EXPECT_FALSE(fref->is_def());
  EXPECT_TRUE(resolve_field(fref) == fdef);
  EXPECT_TRUE(resolve_field(fref, FieldSearch::Instance) == fdef);
  EXPECT_TRUE(resolve_field(fref, FieldSearch::Static) == nullptr);
  fref = make_field_ref(
      DexType::get_type("C"), "f1", DexType::get_type("I"));
  EXPECT_FALSE(fref->is_def());
  EXPECT_TRUE(resolve_field(fref) == fdef);
  EXPECT_TRUE(resolve_field(fref, FieldSearch::Instance) == fdef);
  EXPECT_TRUE(resolve_field(fref, FieldSearch::Static) == nullptr);

  // different cases for static String B.f2
  fdef = DexField::get_field(DexType::get_type("B"),
      DexString::get_string("f2"), DexType::get_type("Ljava/lang/String;"));
  EXPECT_TRUE(fdef != nullptr && fdef->is_def());
  fref = make_field_ref(
      DexType::get_type("A"), "f2", DexType::get_type("Ljava/lang/String;"));
  EXPECT_FALSE(fref->is_def());
  EXPECT_TRUE(resolve_field(fref) == nullptr);
  EXPECT_TRUE(resolve_field(fref, FieldSearch::Instance) == nullptr);
  EXPECT_TRUE(resolve_field(fref, FieldSearch::Static) == nullptr);
  fref = make_field_ref(
      DexType::get_type("B"), "f2", DexType::get_type("Ljava/lang/String;"));
  EXPECT_TRUE(fref->is_def());
  EXPECT_TRUE(resolve_field(fref) == fdef);
  EXPECT_TRUE(resolve_field(fref, FieldSearch::Instance) == fdef);
  fref = make_field_ref(
      DexType::get_type("C"), "f2", DexType::get_type("Ljava/lang/String;"));
  EXPECT_FALSE(fref->is_def());
  EXPECT_TRUE(resolve_field(fref) == fdef);
  EXPECT_TRUE(resolve_field(fref, FieldSearch::Static) == fdef);
  EXPECT_TRUE(resolve_field(fref, FieldSearch::Instance) == nullptr);

  // different cases for D.f
  fdef = DexField::get_field(DexType::get_type("D"),
      DexString::get_string("f"), DexType::get_type("A"));
  EXPECT_TRUE(fdef != nullptr && fdef->is_def());
  EXPECT_TRUE(resolve_field(fdef) == fdef);
  EXPECT_TRUE(resolve_field(fdef, FieldSearch::Instance) == fdef);

  // random non existent field
  fdef = DexField::make_field(DexType::get_type("U"),
      DexString::get_string("f"), DexType::get_type("I"));
  EXPECT_FALSE(fdef->is_def());
  EXPECT_TRUE(resolve_field(fdef) == nullptr);
  EXPECT_TRUE(resolve_field(fdef, FieldSearch::Instance) == nullptr);
  EXPECT_TRUE(resolve_field(fdef, FieldSearch::Static) == nullptr);
  EXPECT_TRUE(resolve_field(DexType::get_type("E"),
      DexString::get_string("f1"),
      DexType::get_type("I"),
      FieldSearch::Static) == nullptr);
  EXPECT_TRUE(resolve_field(DexType::get_type("E"),
      DexString::get_string("f1"),
      DexType::get_type("Ljava/lang/String;"),
      FieldSearch::Instance) == nullptr);
  EXPECT_TRUE(resolve_field(DexType::get_type("E"),
      DexString::get_string("f1"),
      DexType::get_type("I")) == nullptr);

  delete g_redex;
}