| // 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.internal.StringUtils; | |
| import com.android.tools.r8.utils.internal.collections.Pair; | |
| 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. | |
| } | |
| } | |
| } |