blob: 6bd61cd53fdd6a223182926cc77ad7ad1d604cdd [file] [log] [blame]
// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
class UnusedItemsPrinter {
private static class Members {
final List<DexEncodedField> fields = new ArrayList<>();
final List<DexEncodedMethod> methods = new ArrayList<>();
boolean hasMembers() {
return !fields.isEmpty() || !methods.isEmpty();
}
void sort() {
fields.sort((a, b) -> a.getReference().compareTo(b.getReference()));
methods.sort((a, b) -> a.getReference().compareTo(b.getReference()));
}
}
private static final String INDENT = " ";
static final UnusedItemsPrinter DONT_PRINT = new NopPrinter();
private final Consumer<String> consumer;
private DexType currentType = null;
private Members currentMembers = new Members();
private List<Pair<DexType, Members>> classes = new ArrayList<>();
UnusedItemsPrinter(Consumer<String> consumer) {
this.consumer = consumer;
}
void registerUnusedClass(DexProgramClass clazz) {
assert currentType == null;
classes.add(new Pair<>(clazz.type, null));
}
// Visiting methods and fields of the given clazz.
void visiting(DexProgramClass clazz) {
assert currentType == null;
currentType = clazz.type;
}
// Visited methods and fields of the top at the clazz stack.
void visited() {
if (currentMembers.hasMembers()) {
classes.add(new Pair<>(currentType, currentMembers));
currentMembers = new Members();
}
currentType = null;
}
void registerUnusedMethod(DexEncodedMethod method) {
currentMembers.methods.add(method);
}
void registerUnusedField(DexEncodedField field) {
currentMembers.fields.add(field);
}
public void finished() {
classes.sort((a, b) -> a.getFirst().compareTo(b.getFirst()));
for (Pair<DexType, Members> entry : classes) {
DexType type = entry.getFirst();
Members members = entry.getSecond();
consumer.accept(type.toSourceString());
if (members == null) {
consumer.accept(StringUtils.LINE_SEPARATOR);
} else {
consumer.accept(":" + StringUtils.LINE_SEPARATOR);
members.sort();
members.fields.forEach(this::printUnusedField);
members.methods.forEach(this::printUnusedMethod);
}
}
classes = null;
}
private void append(String string) {
consumer.accept(string);
}
private void newline() {
append(StringUtils.LINE_SEPARATOR);
}
private void printUnusedMethod(DexEncodedMethod method) {
append(INDENT);
String accessFlags = method.accessFlags.toString();
if (!accessFlags.isEmpty()) {
append(accessFlags);
append(" ");
}
append(method.getReference().proto.returnType.toSourceString());
append(" ");
append(method.getReference().name.toSourceString());
append("(");
for (int i = 0; i < method.getReference().proto.parameters.values.length; i++) {
if (i != 0) {
append(",");
}
append(method.getReference().proto.parameters.values[i].toSourceString());
}
append(")");
newline();
}
private void printUnusedField(DexEncodedField field) {
append(INDENT);
String accessFlags = field.accessFlags.toString();
if (!accessFlags.isEmpty()) {
append(accessFlags);
append(" ");
}
append(field.getReference().type.toSourceString());
append(" ");
append(field.getReference().name.toSourceString());
newline();
}
// Empty implementation to silently ignore printing dead code.
private static class NopPrinter extends UnusedItemsPrinter {
public NopPrinter() {
super(null);
}
@Override
void registerUnusedClass(DexProgramClass clazz) {
// Intentionally left empty.
}
@Override
void visiting(DexProgramClass clazz) {
// Intentionally left empty.
}
@Override
void visited() {
// Intentionally left empty.
}
@Override
void registerUnusedMethod(DexEncodedMethod method) {
// Intentionally left empty.
}
@Override
void registerUnusedField(DexEncodedField field) {
// Intentionally left empty.
}
@Override
public void finished() {
// Intentionally left empty.
}
}
}