bool parse_class()

in libredex/JarLoader.cpp [364:537]


bool parse_class(uint8_t* buffer,
                 Scope* classes,
                 attribute_hook_t attr_hook,
                 const DexLocation* jar_location) {
  uint32_t magic = read32(buffer);
  uint16_t vminor DEBUG_ONLY = read16(buffer);
  uint16_t vmajor DEBUG_ONLY = read16(buffer);
  uint16_t cp_count = read16(buffer);
  if (magic != kClassMagic) {
    fprintf(stderr, "Bad class magic %08x, Bailing\n", magic);
    return false;
  }
  std::vector<cp_entry> cpool;
  cpool.resize(cp_count);
  /* The zero'th entry is always empty.  Java is annoying. */
  for (int i = 1; i < cp_count; i++) {
    if (!parse_cp_entry(buffer, cpool[i])) return false;
    if (cpool[i].tag == CP_CONST_LONG || cpool[i].tag == CP_CONST_DOUBLE) {
      cpool[i + 1] = cpool[i];
      i++;
    }
  }
  uint16_t aflags = read16(buffer);
  uint16_t clazz = read16(buffer);
  uint16_t super = read16(buffer);
  uint16_t ifcount = read16(buffer);

  if (is_module((DexAccessFlags)aflags)) {
    // Classes with the ACC_MODULE access flag are special.  They contain
    // metadata for the module/package system and don't have a superclass.
    // Ignore them for now.
    TRACE(MAIN, 5, "Warning: ignoring module-info class in jar '%s'",
          jar_location->get_file_name().c_str());
    return true;
  }

  DexType* self = make_dextype_from_cref(cpool, clazz);
  DexClass* cls = type_class(self);
  if (cls) {
    // We are seeing duplicate classes when parsing jar file
    if (cls->is_external()) {
      // Two external classes in .jar file has the same name
      // Just issue an warning for now
      TRACE(MAIN, 1,
            "Warning: Found a duplicate class '%s' in two .jar files:\n "
            "  Current: '%s'\n"
            "  Previous: '%s'",
            SHOW(self), jar_location->get_file_name().c_str(),
            cls->get_location()->get_file_name().c_str());
    } else if (!dup_classes::is_known_dup(cls)) {
      TRACE(MAIN, 1,
            "Warning: Found a duplicate class '%s' in .dex and .jar file."
            "  Current: '%s'\n"
            "  Previous: '%s'\n",
            SHOW(self), jar_location->get_file_name().c_str(),
            cls->get_location()->get_file_name().c_str());

      // TODO: There are still blocking issues in instrumentation test that are
      // blocking. We currently only fail for duplicate `android*` classes,
      // we can make this throw for all the classes once they are fixed.

      if (boost::starts_with(cls->str(), "Landroid")) {
        throw RedexException(RedexError::DUPLICATE_CLASSES,
                             "Found duplicate class in two different files.",
                             {{"class", SHOW(self)},
                              {"jar", jar_location->get_file_name()},
                              {"dex", cls->get_location()->get_file_name()}});
      }
    }
    return true;
  }

  ClassCreator cc(self, jar_location);
  cc.set_external();
  if (super != 0) {
    DexType* sclazz = make_dextype_from_cref(cpool, super);
    cc.set_super(sclazz);
  }
  cc.set_access((DexAccessFlags)aflags);
  if (ifcount) {
    for (int i = 0; i < ifcount; i++) {
      uint16_t iface = read16(buffer);
      DexType* iftype = make_dextype_from_cref(cpool, iface);
      cc.add_interface(iftype);
    }
  }
  uint16_t fcount = read16(buffer);

  auto invoke_attr_hook =
      [&](const boost::variant<DexField*, DexMethod*>& field_or_method,
          uint8_t* attrPtr) {
        if (attr_hook == nullptr) {
          return;
        }
        uint16_t attributes_count = read16(attrPtr);
        for (uint16_t j = 0; j < attributes_count; j++) {
          uint16_t attribute_name_index = read16(attrPtr);
          uint32_t attribute_length = read32(attrPtr);
          char attribute_name[MAX_CLASS_NAMELEN];
          auto extract_res = extract_utf8(cpool, attribute_name_index,
                                          attribute_name, MAX_CLASS_NAMELEN);
          always_assert_log(
              extract_res,
              "attribute hook was specified, but failed to load the attribute "
              "name due to insufficient name buffer");
          attr_hook(field_or_method, attribute_name, attrPtr);
          attrPtr += attribute_length;
        }
      };

  for (int i = 0; i < fcount; i++) {
    cp_field_info cpfield;
    cpfield.aflags = read16(buffer);
    cpfield.nameNdx = read16(buffer);
    cpfield.descNdx = read16(buffer);
    uint8_t* attrPtr = buffer;
    skip_attributes(buffer);
    DexField* field = make_dexfield(cpool, self, cpfield);
    if (field == nullptr) return false;
    cc.add_field(field);
    invoke_attr_hook({field}, attrPtr);
  }

  uint16_t mcount = read16(buffer);
  if (mcount) {
    for (int i = 0; i < mcount; i++) {
      cp_method_info cpmethod;
      cpmethod.aflags = read16(buffer);
      cpmethod.nameNdx = read16(buffer);
      cpmethod.descNdx = read16(buffer);

      uint8_t* attrPtr = buffer;
      skip_attributes(buffer);
      DexMethod* method = make_dexmethod(cpool, self, cpmethod);
      if (method == nullptr) return false;
      cc.add_method(method);
      invoke_attr_hook({method}, attrPtr);
    }
  }
  DexClass* dc = cc.create();
  if (classes != nullptr) {
    classes->emplace_back(dc);
  }
  //#define DEBUG_PRINT
#ifdef DEBUG_PRINT
  fprintf(stderr, "DexClass constructed from jar:\n%s\n", SHOW(dc));
  if (dc->get_sfields().size()) {
    fprintf(stderr, "Static Fields:\n");
    for (auto const& field : dc->get_sfields()) {
      fprintf(stderr, "\t%s\n", SHOW(field));
    }
  }
  if (dc->get_ifields().size()) {
    fprintf(stderr, "Instance Fields:\n");
    for (auto const& field : dc->get_ifields()) {
      fprintf(stderr, "\t%s\n", SHOW(field));
    }
  }
  if (dc->get_dmethods().size()) {
    fprintf(stderr, "Direct Methods:\n");
    for (auto const& method : dc->get_dmethods()) {
      fprintf(stderr, "\t%s\n", SHOW(method));
    }
  }
  if (dc->get_vmethods().size()) {
    fprintf(stderr, "Virtual Methods:\n");
    for (auto const& method : dc->get_vmethods()) {
      fprintf(stderr, "\t%s\n", SHOW(method));
    }
  }

#endif
  return true;
}