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