Exemple #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;
}
Exemple #2
0
DexInstruction* DexInstruction::set_offset(int32_t offset) {
  auto format = opcode_format(opcode());
  switch (format) {
  case FMT_f10t:
    always_assert_log((int32_t)(int8_t)(offset & 0xff) == offset,
                      "offset %d too large for %s",
                      offset,
                      SHOW(this));
    m_opcode = (m_opcode & 0xff) | ((offset & 0xff) << 8);
    return this;
  case FMT_f20t:
  case FMT_f21t:
  case FMT_f22t:
    always_assert_log((int32_t)(int16_t)(offset & 0xffff) == offset,
                      "offset %d too large for %s",
                      offset,
                      SHOW(this));
    m_arg[0] = offset;
    return this;
  case FMT_f30t:
  case FMT_f31t:
    m_arg[0] = offset;
    m_arg[1] = offset >> 16;
    return this;
  default:
    assert(false);
  }
  not_reached();
}
Exemple #3
0
static void validate_dex_header(const dex_header* dh, size_t dexsize) {
  if (memcmp(dh->magic, DEX_HEADER_DEXMAGIC, sizeof(dh->magic))) {
    always_assert_log(false, "Bad dex magic %s\n", dh->magic);
  }
  always_assert_log(
    dh->file_size == dexsize,
    "Reported size in header (%z) does not match file size (%u)\n",
    dexsize,
    dh->file_size);
}
Exemple #4
0
ProguardMap::ProguardMap(const std::string& filename) {
  if (!filename.empty()) {
    std::ifstream fp(filename);
    always_assert_log(fp, "Can't open proguard map: %s\n", filename.c_str());
    parse_proguard_map(fp);
  }
}
Exemple #5
0
uint16_t DexInstruction::dest() const {
  auto format = opcode_format(opcode());
  switch (format) {
  case FMT_f12x:
  case FMT_f12x_2:
  case FMT_f11n:
  case FMT_f22s:
  case FMT_f22c_d:
  case FMT_f22cs:
    return (m_opcode >> 8) & 0xf;
  case FMT_f11x_d:
  case FMT_f22x:
  case FMT_f21s:
  case FMT_f21h:
  case FMT_f21c_d:
  case FMT_f23x_d:
  case FMT_f22b:
  case FMT_f31i:
  case FMT_f31c:
  case FMT_f51l:
    return (m_opcode >> 8) & 0xff;
  case FMT_f32x:
    return m_arg[0];
  case FMT_f41c_d:
  case FMT_f52c_d:
    return m_arg[0];
  default:
    // All other formats do not define a destination register.
    always_assert_log(false, "Unhandled opcode: %s", SHOW(this));
  }
  not_reached();
}
Exemple #6
0
/**
 * Read a map of {list_name : class_list} from json
 */
std::unordered_map<std::string, std::vector<std::string> > ConfigFiles::load_class_lists() {
  std::unordered_map<std::string, std::vector<std::string> > lists;
  std::string class_lists_filename;
  this->m_json.get("class_lists", "", class_lists_filename);

  if (class_lists_filename.empty()) {
    return lists;
  }

  std::ifstream input(class_lists_filename);
  Json::Reader reader;
  Json::Value root;
  bool parsing_succeeded = reader.parse(input, root);
  always_assert_log(parsing_succeeded, "Failed to parse class list json from file: %s\n%s",
                    class_lists_filename.c_str(),
                    reader.getFormattedErrorMessages().c_str());

  for (Json::ValueIterator it = root.begin(); it != root.end(); ++it) {
    std::vector<std::string> class_list;
    Json::Value current_list = *it;
    for (Json::ValueIterator list_it = current_list.begin(); list_it != current_list.end(); ++list_it) {
      lists[it.key().asString()].push_back((*list_it).asString());
    }
  }

  lists["secondary_dex_head.list"] = get_coldstart_classes();

  return lists;
}
Exemple #7
0
size_t delete_methods(
    std::vector<DexClass*>& scope, std::unordered_set<DexMethod*>& removable,
    std::function<DexMethod*(DexMethod*, MethodSearch search)> resolver) {

  // if a removable candidate is invoked do not delete
  walk_opcodes(scope, [](DexMethod* meth) { return true; },
      [&](DexMethod* meth, DexInstruction* insn) {
        if (is_invoke(insn->opcode())) {
          const auto mop = static_cast<DexOpcodeMethod*>(insn);
          auto callee = resolver(mop->get_method(), opcode_to_search(insn));
          if (callee != nullptr) {
            removable.erase(callee);
          }
        }
      });

  size_t deleted = 0;
  for (auto callee : removable) {
    if (!callee->is_concrete()) continue;
    if (do_not_strip(callee)) continue;
    auto cls = type_class(callee->get_class());
    always_assert_log(cls != nullptr,
        "%s is concrete but does not have a DexClass\n",
        SHOW(callee));
    if (callee->is_virtual()) {
      cls->get_vmethods().remove(callee);
    } else {
      cls->get_dmethods().remove(callee);
    }
    deleted++;
    TRACE(DELMET, 4, "removing %s\n", SHOW(callee));
  }
  return deleted;
}
std::vector<DexType*> find(const Scope& scope, const GetNewSpec& get_new_spec) {
  // Compute what the new prototypes will be after we convert a method. Check
  // the prototypes against existing methods and other prototypes created by
  // this method.
  std::vector<DexType*> result;
  for (const DexClass* cls : scope) {
    std::unordered_set<DexMethodSpec> new_specs;
    for (const DexMethod* m : cls->get_dmethods()) {
      if (is_init(m)) {
        std::vector<DexType*> unsafe_refs;
        const auto& new_spec = get_new_spec(m, &unsafe_refs);
        if (new_spec) {
          const auto& pair = new_specs.emplace(*new_spec);
          bool already_there = !pair.second;
          if (already_there || DexMethod::get_method(*new_spec)) {
            always_assert_log(
                !unsafe_refs.empty(),
                "unsafe_refs should be filled with the types that will be "
                "replaced on this <init> method's prototype");
            result.insert(result.end(), unsafe_refs.begin(), unsafe_refs.end());
          }
        }
      }
    }
  }
  return result;
}
Exemple #9
0
void IRList::insert_after(IRInstruction* position,
                          const std::vector<IRInstruction*>& opcodes) {
  /* The nullptr case handling is strange-ish..., this will not work as expected
   * if a method has a branch target as it's first instruction.
   *
   * To handle this case sanely, we'd need to export a interface based on
   * MEI's probably.
   */
  for (auto const& mei : m_list) {
    if (mei.type == MFLOW_OPCODE &&
        (position == nullptr || mei.insn == position)) {
      auto insert_at = m_list.iterator_to(mei);
      if (position != nullptr) {
        insert_at++;
        if (position->has_move_result_pseudo()) {
          insert_at++;
        }
      }
      for (auto* opcode : opcodes) {
        MethodItemEntry* mentry = new MethodItemEntry(opcode);
        m_list.insert(insert_at, *mentry);
      }
      return;
    }
  }
  always_assert_log(false, "No match found");
}
Exemple #10
0
DexOpcode invert_conditional_branch(DexOpcode op) {
  switch (op) {
  case DOPCODE_IF_EQ:
    return DOPCODE_IF_NE;
  case DOPCODE_IF_NE:
    return DOPCODE_IF_EQ;
  case DOPCODE_IF_LT:
    return DOPCODE_IF_GE;
  case DOPCODE_IF_GE:
    return DOPCODE_IF_LT;
  case DOPCODE_IF_GT:
    return DOPCODE_IF_LE;
  case DOPCODE_IF_LE:
    return DOPCODE_IF_GT;
  case DOPCODE_IF_EQZ:
    return DOPCODE_IF_NEZ;
  case DOPCODE_IF_NEZ:
    return DOPCODE_IF_EQZ;
  case DOPCODE_IF_LTZ:
    return DOPCODE_IF_GEZ;
  case DOPCODE_IF_GEZ:
    return DOPCODE_IF_LTZ;
  case DOPCODE_IF_GTZ:
    return DOPCODE_IF_LEZ;
  case DOPCODE_IF_LEZ:
    return DOPCODE_IF_GTZ;
  default:
    always_assert_log(false, "Invalid conditional opcode %s", SHOW(op));
  }
}
Exemple #11
0
DexOpcode* DexOpcode::set_dest(uint16_t vreg) {
  auto format = opcode_format(opcode());
  switch (format) {
  case FMT_f12x:
  case FMT_f12x_2:
  case FMT_f11n:
  case FMT_f22s:
  case FMT_f22c_d:
  case FMT_f22cs:
    assert((vreg & 0xf) == vreg);
    m_opcode = (m_opcode & 0xf0ff) | (vreg << 8);
    return this;
  case FMT_f11x_d:
  case FMT_f22x:
  case FMT_f21s:
  case FMT_f21h:
  case FMT_f21c_d:
  case FMT_f23x_d:
  case FMT_f22b:
  case FMT_f31i:
  case FMT_f31c:
  case FMT_f51l:
    assert((vreg & 0xff) == vreg);
    m_opcode = (m_opcode & 0x00ff) | (vreg << 8);
    return this;
  case FMT_f32x:
    m_arg[0] = vreg;
    return this;
  default:
    // All other formats do not define a destination register.
    always_assert_log(false, "Unhandled opcode: %s", SHOW(this));
  }
  not_reached();
}
Exemple #12
0
// We can't output regions with more than 2^16 code units.
// But the IR has no such restrictions. This function splits up a large try
// region into many small try regions that have the exact same catch
// information.
//
// Also, try region boundaries must lie on instruction boundaries.
void IRCode::split_and_insert_try_regions(
    uint32_t start,
    uint32_t end,
    const DexCatches& catches,
    std::vector<std::unique_ptr<DexTryItem>>* tries) {

  const auto& get_last_addr_before = [this](uint32_t requested_addr) {
    uint32_t valid_addr = 0;
    for (const auto& mie : *m_ir_list) {
      if (mie.type == MFLOW_DEX_OPCODE) {
        auto insn_size = mie.dex_insn->size();
        if (valid_addr == requested_addr ||
            valid_addr + insn_size > requested_addr) {
          return valid_addr;
        }
        valid_addr += insn_size;
      }
    }
    always_assert_log(false, "no valid address for %d", requested_addr);
  };

  constexpr uint32_t max = std::numeric_limits<uint16_t>::max();
  while (start < end) {
    auto size = (end - start <= max)
                    ? end - start
                    : get_last_addr_before(start + max) - start;
    auto tri = std::make_unique<DexTryItem>(start, size);
    tri->m_catches = catches;
    tries->push_back(std::move(tri));
    start += size;
  }
}
Exemple #13
0
MethodCreator::MethodCreator(DexMethod* meth)
    : method(meth)
    , meth_code(meth->get_code()) {
  always_assert_log(meth->is_concrete(),
      "Method must be concrete or use the other ctor");
  load_locals(meth);
  main_block = new MethodBlock(meth_code->main_block(), this);
}
Exemple #14
0
IRInstruction* primary_instruction_of_move_result_pseudo(
    IRList::iterator it) {
  --it;
  always_assert_log(it->type == MFLOW_OPCODE &&
                        it->insn->has_move_result_pseudo(),
                    "%s does not have a move result pseudo", SHOW(*it));
  return it->insn;
}
Exemple #15
0
DexClasses DexLoader::load_dex(const char* location, dex_stats_t* stats) {
  m_file.open(location, boost::iostreams::mapped_file::readonly);
  if (!m_file.is_open()) {
    fprintf(stderr, "error: cannot create memory-mapped file: %s\n", location);
    exit(EXIT_FAILURE);
  }
  auto dh = reinterpret_cast<const dex_header*>(m_file.const_data());
  validate_dex_header(dh, m_file.size());
  if (dh->class_defs_size == 0) {
    return DexClasses(0);
  }
  m_idx = new DexIdx(dh);
  auto off = (uint64_t)dh->class_defs_off;
  auto limit = off + dh->class_defs_size * sizeof(dex_class_def);
  always_assert_log(off < m_file.size(), "class_defs_off out of range");
  always_assert_log(limit <= m_file.size(), "invalid class_defs_size");
  m_class_defs =
      reinterpret_cast<const dex_class_def*>(m_file.const_data() + off);
  DexClasses classes(dh->class_defs_size);
  m_classes = &classes;

  auto lwork = new class_load_work[dh->class_defs_size];
  auto wq =
      workqueue_mapreduce<class_load_work*, std::vector<std::exception_ptr>>(
        class_work, exc_reducer);
  for (uint32_t i = 0; i < dh->class_defs_size; i++) {
    lwork[i].dl = this;
    lwork[i].num = i;
    wq.add_item(&lwork[i]);
  }
  const auto exceptions = wq.run_all();
  delete[] lwork;

  if (!exceptions.empty()) {
    // At least one of the workers raised an exception
    aggregate_exception ae(exceptions);
    throw ae;
  }

  gather_input_stats(stats, dh);

  return classes;
}
Exemple #16
0
const std::vector<DexMethod*>& get_vmethods(const DexType* type) {
  const DexClass* cls = type_class(type);
  if (cls == nullptr) {
    always_assert_log(
        type == get_object_type(), "Unknown type %s\n", SHOW(type));
    create_object_class();
    cls = type_class(type);
  }
  return cls->get_vmethods();
}
Exemple #17
0
MethodCreator::MethodCreator(DexMethod* meth)
    : method(meth),
      meth_code(MethodTransform::get_new_method(method)),
      out_count(0),
      top_reg(0) {
  always_assert_log(meth->is_concrete(),
                    "Method must be concrete or use the other ctor");
  load_locals(meth);
  main_block = new MethodBlock(meth_code->main_block(), this);
}
Exemple #18
0
unsigned min_srcs_size(DexOpcode op) {
  switch (dex_opcode::format(op)) {
  case FMT_f00x:
  case FMT_f10x:
  case FMT_f11n:
  case FMT_f11x_d:
  case FMT_f10t:
  case FMT_f20t:
  case FMT_f21s:
  case FMT_f21h:
  case FMT_f21c_d:
  case FMT_f30t:
  case FMT_f31i:
  case FMT_f31c:
  case FMT_f3rc:
  case FMT_f51l:
  case FMT_f5rc:
  case FMT_f41c_d:
  case FMT_fopcode:
  case FMT_iopcode:
    return 0;
  case FMT_f12x:
  case FMT_f11x_s:
  case FMT_f22x:
  case FMT_f21t:
  case FMT_f21c_s:
  case FMT_f22b:
  case FMT_f22s:
  case FMT_f22c_d:
  case FMT_f32x:
  case FMT_f31t:
  case FMT_f41c_s:
  case FMT_f52c_d:
    return 1;
  case FMT_f12x_2:
  case FMT_f23x_d:
  case FMT_f22t:
  case FMT_f22c_s:
  case FMT_f52c_s:
    return 2;
  case FMT_f23x_s:
    return 3;
  case FMT_f35c:
  case FMT_f57c:
    return 0;
  case FMT_f20bc:
  case FMT_f22cs:
  case FMT_f35ms:
  case FMT_f35mi:
  case FMT_f3rms:
  case FMT_f3rmi:
    always_assert_log(false, "Unimplemented opcode `%s'", SHOW(op));
  }
  not_reached();
}
Exemple #19
0
unsigned DexInstruction::srcs_size() const {
  auto format = opcode_format(opcode());
  switch (format) {
  case FMT_f00x:
  case FMT_f10x:
  case FMT_f11n:
  case FMT_f11x_d:
  case FMT_f10t:
  case FMT_f20t:
  case FMT_f21s:
  case FMT_f21h:
  case FMT_f21c_d:
  case FMT_f30t:
  case FMT_f31i:
  case FMT_f31c:
  case FMT_f51l:
  case FMT_f41c_d:
  case FMT_fopcode:
    return 0;
  case FMT_f12x:
  case FMT_f11x_s:
  case FMT_f22x:
  case FMT_f21t:
  case FMT_f21c_s:
  case FMT_f22b:
  case FMT_f22s:
  case FMT_f22c_d:
  case FMT_f32x:
  case FMT_f31t:
  case FMT_f3rc:
  case FMT_f41c_s:
  case FMT_f52c_d:
  case FMT_f5rc:
    return 1;
  case FMT_f12x_2:
  case FMT_f23x_d:
  case FMT_f22t:
  case FMT_f22c_s:
  case FMT_f52c_s:
    return 2;
  case FMT_f23x_s:
    return 3;
  case FMT_f35c:
  case FMT_f57c:
    return arg_word_count();
  case FMT_f20bc:
  case FMT_f22cs:
  case FMT_f35ms:
  case FMT_f35mi:
  case FMT_f3rms:
  case FMT_f3rmi:
    always_assert_log(false, "Unimplemented opcode `%s'", SHOW(this));
  }
  not_reached();
}
Exemple #20
0
opcode::Branchingness MethodItemEntry::branchingness() const {
  switch (type) {
  case MFLOW_OPCODE:
    return opcode::branchingness(insn->opcode());
  case MFLOW_DEX_OPCODE:
    always_assert_log(false, "Not expecting dex instructions here");
    not_reached();
  default:
    return opcode::BRANCH_NONE;
  }
}
Exemple #21
0
DexString* DexIdx::get_stringidx_fromdex(uint32_t stridx) {
  redex_assert(stridx < m_string_ids_size);
  uint32_t stroff = m_string_ids[stridx].offset;
  always_assert_log(
    stroff < ((dex_header*)m_dexbase)->file_size,
    "String data offset out of range");
  const uint8_t* dstr = m_dexbase + stroff;
  /* Strip off uleb128 size encoding */
  int utfsize = read_uleb128(&dstr);
  return DexString::make_string((const char*)dstr, utfsize);
}
Exemple #22
0
void IRList::remove_opcode(IRInstruction* insn) {
  for (auto& mei : m_list) {
    if (mei.type == MFLOW_OPCODE && mei.insn == insn) {
      auto it = m_list.iterator_to(mei);
      remove_opcode(it);
      return;
    }
  }
  always_assert_log(false,
                    "No match found while removing '%s' from method",
                    SHOW(insn));
}
SwitchMethodPartitioning::SwitchMethodPartitioning(IRCode* code,
                                                   bool verify_default_case)
    : m_code(code) {
  m_code->build_cfg(/* editable */ true);
  auto& cfg = m_code->cfg();
  // Note that a single-case switch can be compiled as either a switch opcode or
  // a series of if-* opcodes. We can use constant propagation to handle these
  // cases uniformly: to determine the case key, we use the inferred value of
  // the operand to the branching opcode in the successor blocks.
  cp::intraprocedural::FixpointIterator fixpoint(
      cfg, cp::ConstantPrimitiveAnalyzer());
  fixpoint.run(ConstantEnvironment());

  auto determining_reg =
      compute_prologue_blocks(&cfg, fixpoint, verify_default_case);

  // Find all the outgoing edges from the prologue blocks
  std::vector<cfg::Edge*> cases;
  for (const cfg::Block* prologue : m_prologue_blocks) {
    for (cfg::Edge* e : prologue->succs()) {
      if (std::find(m_prologue_blocks.begin(), m_prologue_blocks.end(),
                    e->target()) == m_prologue_blocks.end()) {
        cases.push_back(e);
      }
    }
  }

  for (auto edge : cases) {
    auto case_block = edge->target();
    auto env = fixpoint.get_entry_state_at(case_block);
    auto case_key = env.get<SignedConstantDomain>(*determining_reg);
    if (case_key.is_top() && verify_default_case) {
      auto last_insn_it = case_block->get_last_insn();
      always_assert_log(last_insn_it != case_block->end() &&
                            last_insn_it->insn->opcode() == OPCODE_THROW,
                        "Could not determine key for block that does not look "
                        "like it throws an IllegalArgumentException: %d in %s",
                        case_block->id(), SHOW(cfg));
    } else if (!case_key.is_top()) {
      const auto& c = case_key.get_constant();
      if (c != boost::none) {
        m_key_to_block[*c] = case_block;
      } else {
        // handle multiple case keys that map to a single block
        always_assert(edge->type() == cfg::EDGE_BRANCH);
        const auto& edge_case_key = edge->case_key();
        always_assert(edge_case_key != boost::none);
        m_key_to_block[*edge_case_key] = case_block;
      }
    }
  }
}
Exemple #24
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);
}
Exemple #25
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);
}
Exemple #26
0
void MethodTransform::remove_opcode(DexInstruction* insn) {
  for (auto const& mei : *m_fmethod) {
    if (mei.type == MFLOW_OPCODE && mei.insn == insn) {
      m_fmethod->erase(m_fmethod->iterator_to(mei));
      delete insn;
      return;
    }
  }
  always_assert_log(false,
                    "No match found while removing '%s' from method %s",
                    SHOW(insn),
                    show_short(m_method).c_str());
}
Exemple #27
0
void DexesStructure::add_class_no_checks(const MethodRefs& clazz_mrefs,
                                         const FieldRefs& clazz_frefs,
                                         const TypeRefs& clazz_trefs,
                                         DexClass* clazz) {
  always_assert_log(m_classes.count(clazz) == 0,
                    "Can't emit the same class twice: %s!\n", SHOW(clazz));

  auto laclazz = estimate_linear_alloc(clazz);
  m_current_dex.add_class_no_checks(clazz_mrefs, clazz_frefs, clazz_trefs,
                                    laclazz, clazz);
  m_classes.emplace(clazz);
  update_stats(clazz_mrefs, clazz_frefs, clazz);
}
Exemple #28
0
MethodCreator::MethodCreator(DexType* cls,
                             DexString* name,
                             DexProto* proto,
                             DexAccessFlags access)
    : method(DexMethod::make_method(cls, name, proto)),
      meth_code(MethodTransform::get_new_method(method)),
      out_count(0),
      top_reg(0) {
  always_assert_log(!method->is_concrete(), "Method already defined");
  method->set_access(access);
  load_locals(method);
  main_block = new MethodBlock(meth_code->main_block(), this);
}
Exemple #29
0
const VirtualScope& find_virtual_scope(const SignatureMap& sig_map,
                                       const DexMethod* meth) {
  const auto& protos = sig_map.find(meth->get_name());
  always_assert(protos != sig_map.end());
  const auto& scopes = protos->second.find(meth->get_proto());
  always_assert(scopes != protos->second.end());
  const auto meth_type = meth->get_class();
  for (const auto& scope : scopes->second) {
    if (scope.type == get_object_type()) return scope;
    if (is_subclass(scope.type, meth_type)) return scope;
  }
  always_assert_log(false, "unreachable. Scope not found for %s\n", SHOW(meth));
}
Exemple #30
0
void ProguardMap::parse_line(const std::string& line) {
  if (parse_class(line)) {
    return;
  }
  if (parse_field(line)) {
    return;
  }
  if (parse_method(line)) {
    return;
  }
  always_assert_log(false,
                    "Bogus line encountered in proguard map: %s\n",
                    line.c_str());
}