Esempio n. 1
0
void unmark_keep(const Scope& scope,
                 const std::vector<std::string>& package_list,
                 const std::vector<std::string>& supercls_list) {
  if (package_list.size() == 0 && supercls_list.size() == 0) {
    return;
  }
  std::unordered_set<const DexType*> superclasses;
  std::unordered_set<const DexType*> interface_list;
  for (auto& cls_name : supercls_list) {
    const DexType* supercls_type = DexType::get_type(cls_name);
    if (supercls_type) {
      DexClass* supercls = type_class(supercls_type);
      if (supercls && is_interface(supercls)) {
        interface_list.emplace(supercls_type);
      } else {
        superclasses.emplace(supercls_type);
      }
    }
  }
  TypeSystem ts(scope);
  // Unmark proguard keep rule for interface implementors like
  // "-keep class * extend xxx".
  for (const DexType* intf_type : interface_list) {
    for (const DexType* implementor : ts.get_implementors(intf_type)) {
      DexClass* implementor_cls = type_class(implementor);
      if (implementor_cls) {
        implementor_cls->rstate.force_unset_allowshrinking();
      }
    }
  }
  walk::parallel::classes(
      scope, [&ts, &superclasses, &package_list](DexClass* cls) {
        // Unmark proguard keep rule for classes under path from package list.
        for (const auto& package : package_list) {
          if (strstr(cls->get_name()->c_str(), package.c_str()) != nullptr) {
            cls->rstate.force_unset_allowshrinking();
            return;
          }
        }
        if (!is_interface(cls)) {
          // Unmark proguard keep for classes that extend class from superclass
          // list for proguard keep rule like "-keep class * extend xxx".
          const auto& parents_chain = ts.parent_chain(cls->get_type());
          if (parents_chain.size() <= 2) {
            // The class's direct super class is java.lang.Object, no need
            // to proceed.
            return;
          }
          // We only need to find class started at the second of parents_chain
          // because first is java.lang.Object, and end at second to last,
          // because last one is itself.
          for (uint32_t index = 1; index < parents_chain.size() - 1; ++index) {
            if (superclasses.find(parents_chain[index]) != superclasses.end()) {
              cls->rstate.force_unset_allowshrinking();
              return;
            }
          }
        }
      });
}
Esempio n. 2
0
bool Class::is_instanceof(Class* clss)
{
    assert(!is_interface());

#ifdef VM_STATS
    UNSAFE_REGION_START
    VM_Statistics::get_vm_stats().num_type_checks++;
    if(this == clss)
        VM_Statistics::get_vm_stats().num_type_checks_equal_type++;
    if(clss->m_is_suitable_for_fast_instanceof)
        VM_Statistics::get_vm_stats().num_type_checks_fast_decision++;
    else if(clss->is_array())
        VM_Statistics::get_vm_stats().num_type_checks_super_is_array++;
    else if(clss->is_interface())
        VM_Statistics::get_vm_stats().num_type_checks_super_is_interface ++;
    else if((unsigned)clss->m_depth >= vm_max_fast_instanceof_depth())
        VM_Statistics::get_vm_stats().num_type_checks_super_is_too_deep++;
    UNSAFE_REGION_END
#endif // VM_STATS

    if(this == clss) return true;

    Global_Env* env = VM_Global_State::loader_env;

    if(is_array()) {
        Class* object_class = env->JavaLangObject_Class;
        assert(object_class != NULL);
        if(clss == object_class) return true;
        if(clss == env->java_io_Serializable_Class) return true;
        if(clss == env->java_lang_Cloneable_Class) return true;
        if(!clss->is_array()) return false;

        return class_is_subtype(get_array_element_class(), clss->get_array_element_class());
    } else {
        if(clss->m_is_suitable_for_fast_instanceof)
        {
            return m_vtable->superclasses[clss->m_depth - 1] == clss;
        }

        if(!clss->is_interface()) {
            for(Class *c = this; c; c = c->get_super_class()) {
                if(c == clss) return true;
            }
        } else {
            for(Class *c = this; c; c = c->get_super_class()) {
                unsigned n_intf = c->get_number_of_superinterfaces();
                for(unsigned i = 0; i < n_intf; i++) {
                    Class* intf = c->get_superinterface(i);
                    assert(intf);
                    assert(intf->is_interface());
                    if(class_is_subtype(intf, clss)) return true;
                }
            }
        }
    }

    return false;
}
Esempio n. 3
0
struct usb_endpoint_descriptor *first_endpoint(const struct usb_interface_descriptor *d)
{
	char *ptr = (char*)d;

	if (!is_interface(d)) {
		return 0;
	}

	ptr += d->bLength;
	return (struct usb_endpoint_descriptor*)ptr;
}
Esempio n. 4
0
bool is_assignable_to(const DexType* child, const DexType* parent) {
  // Check class hierarchy
  auto super = child;
  while (super != nullptr) {
    if (parent == super) return true;
    const auto cls = type_class(super);
    if (cls == nullptr) break;
    super = cls->get_super_class();
  }
  // Check interface hierarchy
  DexClass* parent_cls = type_class(parent);
  return parent_cls &&
    is_interface(parent_cls) &&
    is_assignable_to_interface(child, parent);
}
Esempio n. 5
0
void AccessFlags::print_on(outputStream* st) const {
  if (is_public      ()) st->print("public "      );
  if (is_private     ()) st->print("private "     );
  if (is_protected   ()) st->print("protected "   );
  if (is_static      ()) st->print("static "      );
  if (is_final       ()) st->print("final "       );
  if (is_synchronized()) st->print("synchronized ");
  if (is_volatile    ()) st->print("volatile "    );
  if (is_transient   ()) st->print("transient "   );
  if (is_native      ()) st->print("native "      );
  if (is_interface   ()) st->print("interface "   );
  if (is_abstract    ()) st->print("abstract "    );
  if (is_strict      ()) st->print("strict "      );
  if (is_synthetic   ()) st->print("synthetic "   );
  if (is_old         ()) st->print("{old} "       );
  if (is_obsolete    ()) st->print("{obsolete} "  );
}
Esempio n. 6
0
void MethodBlock::invoke(DexMethod* meth, const std::vector<Location>& args) {
  always_assert(meth->is_concrete());
  IROpcode opcode;
  if (meth->is_virtual()) {
    if (is_interface(type_class(meth->get_class()))) {
      opcode = OPCODE_INVOKE_INTERFACE;
    } else {
      opcode = OPCODE_INVOKE_VIRTUAL;
    }
  } else {
    if (is_static(meth)) {
      opcode = OPCODE_INVOKE_STATIC;
    } else {
      opcode = OPCODE_INVOKE_DIRECT;
    }
  }
  invoke(opcode, meth, args);
}
Esempio n. 7
0
/**
 * @return 1 or 0.
 */
STACKWORD instance_of (Object *obj, byte classIndex)
{
  byte rtType;

  if (obj == null)
    return 0;
  rtType = get_class_index(obj);
  // TBD: support for interfaces
  if (is_interface (get_class_record(classIndex)))
    return 1;
 LABEL_INSTANCE:
  if (rtType == classIndex)
    return 1;
  if (rtType == JAVA_LANG_OBJECT)
    return 0;
  rtType = get_class_record(rtType)->parentClass;
  goto LABEL_INSTANCE;
}
Esempio n. 8
0
/*
	The interface can be followed not only by endpoint descriptors,
	but by other types of descriptors too.
*/
int get_interface_size(const struct usb_interface_descriptor *d)
{
	struct usb_endpoint_descriptor *ed = 0;
	unsigned int i = 0;
	int len = 0;

	if (!is_interface(d)) {
		return 0;
	}

	len = d->bLength;
	ed = first_endpoint(d);

	for (i = 0; ed && i < d->bNumEndpoints; i += is_endpoint(ed), ed = next_endpoint(ed)) {
		len += ed->bLength;
	}

	return len;
}
Esempio n. 9
0
/**
 * Check to see if it is allowed to assign the an object of type srcCls
 * to an object of type dstCls.
 */
boolean is_assignable(const byte srcCls, const byte dstCls)
{
  ClassRecord *dstRec;
  // Check common cases
  if (srcCls == dstCls || dstCls == JAVA_LANG_OBJECT) return true;
  dstRec = get_class_record(dstCls);
  if (is_interface(dstRec))
  {
    // we are testing against an interface. So we use the associated interface
    // map to test if the src implements it...
    int base = get_interface_map_base(dstRec);
    // Special case all arrays implement cloneable
    if (dstCls == JAVA_LANG_CLONEABLE && is_array_class(get_class_record(srcCls))) return true;
    if (srcCls < base) return false;
    if (srcCls - base >= get_interface_map_len(dstRec)) return false;
    base = srcCls - base;
    return ((get_interface_map(dstRec)[base/8]) & (1 << (base%8))) != 0;
  }
  return sub_type_of(srcCls, dstCls);
}
Esempio n. 10
0
// ------------------------------------------------------------------
// ciFlags::print_klass_flags
void ciFlags::print_klass_flags(outputStream* st) {
  if (is_public()) {
    st->print("public");
  } else {
    st->print("DEFAULT_ACCESS");
  }

  if (is_final()) {
    st->print(",final");
  }
  if (is_super()) {
    st->print(",super");
  }
  if (is_interface()) {
    st->print(",interface");
  }
  if (is_abstract()) {
    st->print(",abstract");
  }
}
Esempio n. 11
0
void TypeErasurePass::erase_model(const ModelSpec& spec,
                                  Scope& scope,
                                  PassManager& mgr,
                                  DexStoresVector& stores,
                                  ConfigFiles& cfg) {
  TRACE(TERA, 2, "[TERA] erasing %s model\n", spec.name.c_str());
  Timer t("erase_model");
  for (const auto root : spec.roots) {
    always_assert(!is_interface(type_class(root)));
  }
  auto model = Model::build_model(scope, stores, spec, cfg);
  model.update_redex_stats(mgr);

  auto mm = get_model_merger();
  auto merger_classes =
      mm->merge_model(scope, stores, model, m_max_num_dispatch_target);
  mm->update_redex_stats(spec.class_name_prefix, mgr);

  delete mm;
}
Esempio n. 12
0
struct usb_interface_descriptor *find_interface(
        const struct usb_config_descriptor *d, int num, int altnum)
{
	int cnt = get_total_interfaces(d);
	struct usb_interface_descriptor *iface = cnt > 0 ? first_interface(d, 0) : 0;
	int i = 0;

	for (i = 0; i < cnt && iface;) {

		if (!is_interface(iface)) {
			iface = next_descriptor(iface);
		} else if (iface->bInterfaceNumber == num && iface->bAlternateSetting == altnum) {
			return iface;
		} else {
			iface = next_interface(iface);
			++i;
		}
	}

	return 0;
}
Esempio n. 13
0
/**
 * In some limited cases we can do type erasure on an interface when
 * implementors of the interface only implement that interface and have no
 * parent class other than java.lang.Object. We create a base class for those
 * implementors and use the new base class as root, and proceed with type
 * erasure as usual.
 */
void handle_interface_as_root(ModelSpec& spec,
                              Scope& scope,
                              DexStoresVector& stores) {
  TypeSet interface_roots;
  for (const auto root : spec.roots) {
    if (is_interface(type_class(root))) {
      interface_roots.insert(root);
    }
  }

  for (const auto interface_root : interface_roots) {
    auto empty_base =
        create_empty_base_type(spec, interface_root, scope, stores);
    if (empty_base != nullptr) {
      TRACE(TERA, 3, "Changing the root from %s to %s.\n", SHOW(interface_root),
            SHOW(empty_base));
      spec.roots.insert(empty_base);
      add_class(type_class(empty_base), scope, stores);
    }
    // Remove interface roots regardless of whether an empty base was added.
    spec.roots.erase(interface_root);
  }
}
Esempio n. 14
0
bool Class::load_ancestors(Global_Env* env)
{
    m_state = ST_LoadingAncestors;

    const String* superName = get_super_class_name();

    if(superName == NULL) {
        if(env->InBootstrap() || get_name() != env->JavaLangClass_String) {
            // This class better be java.lang.Object
            if(get_name() != env->JavaLangObject_String) {
                // ClassFormatError
                std::stringstream ss;
                ss << get_name()->bytes
                    << ": class does not have superclass "
                    << "but the class is not java.lang.Object";
                REPORT_FAILED_CLASS_CLASS(m_class_loader, this,
                    "java/lang/ClassFormatError", ss.str().c_str());
                return false;
            }
        }
    } else {
        // Load super class
        Class* superClass;
        m_super_class.name = NULL;
        superClass = m_class_loader->LoadVerifyAndPrepareClass(env, superName);
        if(superClass == NULL) {
            assert(exn_raised());
            return false;
        }

        if(superClass->is_interface()) {
            REPORT_FAILED_CLASS_CLASS(m_class_loader, this,
                "java/lang/IncompatibleClassChangeError",
                "class " << m_name->bytes << " has interface "
                << superClass->get_name()->bytes << " as super class");
            return false;
        }
        if(superClass->is_final()) {
            REPORT_FAILED_CLASS_CLASS(m_class_loader, this,
                "java/lang/VerifyError",
                m_name->bytes << " cannot inherit from final class "
                << superClass->get_name()->bytes);
            return false;
        }

        // super class was successfully loaded
        m_super_class.clss = superClass;
        if(m_super_class.cp_index) {
            m_const_pool.resolve_entry(m_super_class.cp_index, superClass);
        }

        // if it's an interface, its superclass must be java/lang/Object
        if(is_interface()) {
            if((env->JavaLangObject_Class != NULL) && (superClass != env->JavaLangObject_Class)) {
                std::stringstream ss;
                ss << get_name()->bytes << ": interface superclass is not java.lang.Object";
                REPORT_FAILED_CLASS_CLASS(m_class_loader, this,
                    "java/lang/ClassFormatError", ss.str().c_str());
                return false;
            }
        }

        // Update the cha_first_child and cha_next_sibling fields.
        m_cha_first_child = NULL;
        if(has_super_class())
        {
            m_cha_next_sibling = get_super_class()->m_cha_first_child;
            get_super_class()->m_cha_first_child = this;
        }
    }

    //
    // load in super interfaces
    //
    for(unsigned i = 0; i < m_num_superinterfaces; i++ ) {
        const String* intfc_name = m_superinterfaces[i].name;
        Class* intfc = m_class_loader->LoadVerifyAndPrepareClass(env, intfc_name);
        if(intfc == NULL) {
            assert(exn_raised());
            return false;
        }
        if(!intfc->is_interface()) {
            REPORT_FAILED_CLASS_CLASS(m_class_loader, this,
                "java/lang/IncompatibleClassChangeError",
                get_name()->bytes << ": " << intfc->get_name()->bytes
                << " is not an interface");
            return false;
        }

        // superinterface was successfully loaded
        m_superinterfaces[i].clss = intfc;
        if(m_superinterfaces[i].cp_index != 0) {
            // there are no constant pool entries for array classes
            m_const_pool.resolve_entry(m_superinterfaces[i].cp_index, intfc);
        }
    }
    // class, superclass, and superinterfaces successfully loaded

    m_state = ST_Loaded;
    if(!is_array())
        m_package = m_class_loader->ProvidePackage(env, m_name, NULL);

    return true;
}
Esempio n. 15
0
bool Type::is_ref() const {
    return is_interface() || is_object() || is_alt() || is_generic() || is_nil();
}   
Esempio n. 16
0
int32_t main(int32_t argc, char *argv[])
{
    int res =0;
    uint8_t *eventbuf = NULL;
    uint8_t *dbgbuf = NULL;
    int32_t c;
    struct dbglog_slot *slot;
    int cnss_intf_len = strlen(CNSS_INTF) +  1;
    pthread_t thd_id;

    progname = argv[0];
    uint16_t diag_type = 0;
    int32_t option_index = 0;
    static struct option long_options[] = {
        {"logfile", 1, NULL, 'f'},
        {"reclimit", 1, NULL, 'r'},
        {"console", 0, NULL, 'c'},
        {"qxdm", 0, NULL, 'q'},
        {"silent", 0, NULL, 's'},
        {"debug", 0, NULL, 'd'},
        { 0, 0, 0, 0}
    };

    while (1) {
        c = getopt_long (argc, argv, "f:scqdr:", long_options, &option_index);
        if (c == -1) break;

        switch (c) {
            case 'f':
                memset(dbglogoutfile, 0, PATH_MAX);
                memcpy(dbglogoutfile, optarg, strlen(optarg));
                optionflag |= LOGFILE_FLAG;
            break;

            case 'c':
                optionflag |= CONSOLE_FLAG;
            break;

            case 'q':
                optionflag |= QXDM_FLAG;
            break;

            case 'r':
                rec_limit = strtoul(optarg, NULL, 0);
            break;

            case 's':
                optionflag |= SILENT_FLAG;
            break;

            case 'd':
                optionflag |= DEBUG_FLAG;
            break;
            default:
                usage();
        }
    }

    if (!(optionflag & (LOGFILE_FLAG | CONSOLE_FLAG | QXDM_FLAG | SILENT_FLAG
           | DEBUG_FLAG))) {
        usage();
        return -1;
    }

    if (optionflag & QXDM_FLAG) {
        /* Intialize the fd required for diag APIs */
        if (TRUE != Diag_LSM_Init(NULL))
        {
             perror("Failed on Diag_LSM_Init\n");
             return -1;
        }
         /* Register CALLABACK for QXDM input data */
        DIAGPKT_DISPATCH_TABLE_REGISTER(DIAG_SUBSYS_WLAN, cnss_wlan_tbl);
#ifdef ANDROID
        if(cnssdiagservice_cap_handle()) {
            printf("Cap bouncing failed EXIT!!!");
            exit(1);
        }
#endif
    }

    pthread_create(&thd_id, NULL, &cnss_intf_wait_receive, NULL);

    sock_fd = create_nl_socket();
    if (sock_fd < 0) {
        fprintf(stderr, "Socket creation failed sock_fd 0x%x \n", sock_fd);
        return -1;
    }

    if (is_interface(CNSS_INTF, cnss_intf_len)) {
        initialize(sock_fd);
        cnssdiag_register_kernel_logging(sock_fd, nlh);
    }

    signal(SIGINT, stop);
    signal(SIGTERM, stop);

    if (optionflag & LOGFILE_FLAG) {

        if (rec_limit < RECLEN) {
            fprintf(stderr, "Too small maximum length (has to be >= %d)\n",
                    RECLEN);
            close(sock_fd);
            free(nlh);
            return -1;
        }
        max_records = rec_limit;
        printf("Storing last %d records\n", max_records);

        log_out = fopen(dbglogoutfile, "w");
        if (log_out == NULL) {
            perror("Failed to create output file");
            close(sock_fd);
            free(nlh);
            return -1;
        }

        fwlog_res_file = "./reorder";
    }


    parser_init();

    while ( 1 )  {
        if ((res = recvmsg(sock_fd, &msg, 0)) < 0)
                  continue;
        if ((res >= (int)sizeof(struct dbglog_slot)) ||
                  (nlh->nlmsg_type == WLAN_NL_MSG_CNSS_HOST_EVENT_LOG)) {
            process_cnss_diag_msg((tAniNlHdr *)nlh);
            memset(nlh,0,NLMSG_SPACE(MAX_MSG_SIZE));
        } else {
            /* Ignore other messages that might be broadcast */
            continue;
        }
    }
    /* Release the handle to Diag*/
    Diag_LSM_DeInit();
    if (optionflag & LOGFILE_FLAG)
        cleanup();
    close(sock_fd);
    free(nlh);
    return 0;
}
Esempio n. 17
0
int main(int argc, char *argv[])
{
	struct lxc_container *c;
	char *cmd, *dev_name, *dst_name;
	int ret = 1;

	if (geteuid() != 0) {
		ERROR("%s must be run as root", argv[0]);
		exit(1);
	}

	if (lxc_arguments_parse(&my_args, argc, argv))
		goto err;

	if (!my_args.log_file)
		my_args.log_file = "none";

	if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
			 my_args.progname, my_args.quiet, my_args.lxcpath[0]))
		goto err;
	lxc_log_options_no_override();

	c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
	if (!c) {
		ERROR("%s doesn't exist", my_args.name);
		goto err;
	}

	if (!c->is_running(c)) {
		ERROR("Container %s is not running.", c->name);
		goto err1;
	}

	if (my_args.argc < 2) {
		ERROR("Error: no command given (Please see --help output)");
		goto err1;
	}

	cmd = my_args.argv[0];
	dev_name = my_args.argv[1];
	if (my_args.argc < 3)
		dst_name = dev_name;
	else
		dst_name = my_args.argv[2];

	if (strcmp(cmd, "add") == 0) {
		if (is_interface(dev_name, 1)) {
			ret = c->attach_interface(c, dev_name, dst_name);
		} else {
			ret = c->add_device_node(c, dev_name, dst_name);
		}
		if (ret != true) {
			ERROR("Failed to add %s to %s.", dev_name, c->name);
			ret = 1;
			goto err1;
		}
		INFO("Add %s to %s.", dev_name, c->name);
	} else if (strcmp(cmd, "del") == 0) {
		if (is_interface(dev_name, c->init_pid(c))) {
			ret = c->detach_interface(c, dev_name, dst_name);
		} else {
			ret = c->remove_device_node(c, dev_name, dst_name);
		}
		if (ret != true) {
			ERROR("Failed to del %s from %s.", dev_name, c->name);
			ret = 1;
			goto err1;
		}
		INFO("Delete %s from %s.", dev_name, c->name);
	} else {
		ERROR("Error: Please use add or del (Please see --help output)");
		goto err1;
	}
	exit(0);
err1:
	lxc_container_put(c);
err:
	exit(ret);
}
Esempio n. 18
0
void RemoveBuildersPass::run_pass(DexStoresVector& stores,
                                  ConfigFiles&,
                                  PassManager& mgr) {
  if (mgr.no_proguard_rules()) {
    TRACE(BUILDERS,
          1,
          "RemoveBuildersPass did not run because no Proguard configuration "
          "was provided.");
    return;
  }

  // Initialize couters.
  b_counter = {0, 0, 0, 0};

  auto obj_type = get_object_type();
  auto scope = build_class_scope(stores);
  for (DexClass* cls : scope) {
    if (is_annotation(cls) || is_interface(cls) ||
        cls->get_super_class() != obj_type) {
      continue;
    }

    if (has_builder_name(cls->get_type())) {
      m_builders.emplace(cls->get_type());
    }
  }

  std::unordered_set<DexType*> escaped_builders;
  walk::methods(scope, [&](DexMethod* m) {
    auto builders = created_builders(m);
    for (DexType* builder : builders) {
      if (escapes_stack(builder, m)) {
        TRACE(BUILDERS,
              3,
              "%s escapes in %s\n",
              SHOW(builder),
              m->get_deobfuscated_name().c_str());
        escaped_builders.emplace(builder);
      }
    }
  });

  std::unordered_set<DexType*> stack_only_builders;
  for (DexType* builder : m_builders) {
    if (escaped_builders.find(builder) == escaped_builders.end()) {
      stack_only_builders.emplace(builder);
    }
  }

  std::unordered_set<DexType*> builders_and_supers;
  for (DexType* builder : stack_only_builders) {
    DexType* cls = builder;
    while (cls != nullptr && cls != obj_type) {
      builders_and_supers.emplace(cls);
      cls = type_class(cls)->get_super_class();
    }
  }

  std::unordered_set<DexType*> this_escapes;
  for (DexType* cls_ty : builders_and_supers) {
    DexClass* cls = type_class(cls_ty);
    if (cls->is_external() ||
        this_arg_escapes(cls, m_enable_buildee_constr_change)) {
      this_escapes.emplace(cls_ty);
    }
  }

  // set of builders that neither escape the stack nor pass their 'this' arg
  // to another function
  std::unordered_set<DexType*> no_escapes;
  for (DexType* builder : stack_only_builders) {
    DexType* cls = builder;
    bool hierarchy_has_escape = false;
    while (cls != nullptr) {
      if (this_escapes.find(cls) != this_escapes.end()) {
        hierarchy_has_escape = true;
        break;
      }
      cls = type_class(cls)->get_super_class();
    }
    if (!hierarchy_has_escape) {
      no_escapes.emplace(builder);
    }
  }

  size_t dmethod_count = 0;
  size_t vmethod_count = 0;
  size_t build_count = 0;
  for (DexType* builder : no_escapes) {
    auto cls = type_class(builder);
    auto buildee = get_buildee(builder);
    dmethod_count += cls->get_dmethods().size();
    vmethod_count += cls->get_vmethods().size();
    for (DexMethod* m : cls->get_vmethods()) {
      if (m->get_proto()->get_rtype() == buildee) {
        build_count++;
      }
    }
  }

  std::unordered_set<DexClass*> trivial_builders =
      get_trivial_builders(m_builders, no_escapes);

  std::unordered_set<DexClass*> kept_builders =
      get_builders_with_subclasses(scope);

  PassConfig pc(mgr.get_config());
  BuilderTransform b_transform(pc, scope, stores, false);

  // Inline non init methods.
  std::unordered_set<DexClass*> removed_builders;
  walk::methods(scope, [&](DexMethod* method) {
    auto builders = created_builders(method);

    for (DexType* builder : builders) {
      if (method->get_class() == builder) {
        continue;
      }

      DexClass* builder_cls = type_class(builder);

      // Filter out builders that we cannot remove.
      if (kept_builders.find(builder_cls) != kept_builders.end()) {
        continue;
      }

      // Check it is a trivial one.
      if (trivial_builders.find(builder_cls) != trivial_builders.end()) {

        DexMethod* method_copy = DexMethod::make_method_from(
            method,
            method->get_class(),
            DexString::make_string(method->get_name()->str() +
                                   "$redex_builders"));
        bool was_not_removed =
            !b_transform.inline_methods(
                method, builder, &get_non_init_methods) ||
            !remove_builder_from(method, builder_cls, b_transform);

        if (was_not_removed) {
          kept_builders.emplace(builder_cls);
          method->set_code(method_copy->release_code());
        } else {
          b_counter.methods_cleared++;
          removed_builders.emplace(builder_cls);
        }

        DexMethod::erase_method(method_copy);
      }
    }
  });

  // No need to remove the builders here, since `RemoveUnreachable` will
  // take care of it.
  gather_removal_builder_stats(removed_builders, kept_builders);

  mgr.set_metric("total_builders", m_builders.size());
  mgr.set_metric("stack_only_builders", stack_only_builders.size());
  mgr.set_metric("no_escapes", no_escapes.size());
  mgr.incr_metric(METRIC_CLASSES_REMOVED, b_counter.classes_removed);
  mgr.incr_metric(METRIC_METHODS_REMOVED, b_counter.methods_removed);
  mgr.incr_metric(METRIC_FIELDS_REMOVED, b_counter.fields_removed);
  mgr.incr_metric(METRIC_METHODS_CLEARED, b_counter.methods_cleared);

  TRACE(BUILDERS, 1, "Total builders: %d\n", m_builders.size());
  TRACE(BUILDERS, 1, "Stack-only builders: %d\n", stack_only_builders.size());
  TRACE(BUILDERS,
        1,
        "Stack-only builders that don't let `this` escape: %d\n",
        no_escapes.size());
  TRACE(BUILDERS, 1, "Stats for unescaping builders:\n");
  TRACE(BUILDERS, 1, "\tdmethods: %d\n", dmethod_count);
  TRACE(BUILDERS, 1, "\tvmethods: %d\n", vmethod_count);
  TRACE(BUILDERS, 1, "\tbuild methods: %d\n", build_count);
  TRACE(BUILDERS, 1, "Trivial builders: %d\n", trivial_builders.size());
  TRACE(BUILDERS, 1, "Classes removed: %d\n", b_counter.classes_removed);
  TRACE(BUILDERS, 1, "Methods removed: %d\n", b_counter.methods_removed);
  TRACE(BUILDERS, 1, "Fields removed: %d\n", b_counter.fields_removed);
  TRACE(BUILDERS, 1, "Methods cleared: %d\n", b_counter.methods_cleared);
}