| // 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. |
| } |
| } |
| } |