Add a structured consumer for tracereferences
Bug: 169127026
Change-Id: Ie43ac267c91d648ec5535f7b983165201c08df41
diff --git a/src/main/java/com/android/tools/r8/tracereferences/Formatter.java b/src/main/java/com/android/tools/r8/tracereferences/Formatter.java
new file mode 100644
index 0000000..b545e65
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/tracereferences/Formatter.java
@@ -0,0 +1,176 @@
+// Copyright (c) 2020, 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.tracereferences;
+
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.PackageReference;
+import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedClass;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedField;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedMethod;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.StringUtils.BraceType;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+abstract class Formatter {
+
+ private final StringBuilder output;
+
+ Formatter() {
+ output = new StringBuilder();
+ }
+
+ String get() {
+ return output.toString();
+ }
+
+ protected void append(String string) {
+ output.append(string);
+ }
+
+ protected void appendLine(String string) {
+ output.append(StringUtils.lines(string));
+ }
+
+ protected void appendLine() {
+ appendLine("");
+ }
+
+ protected void printArguments(MethodReference method) {
+ StringUtils.append(
+ output,
+ ListUtils.map(method.getFormalTypes(), TypeReference::getTypeName),
+ ",",
+ BraceType.PARENS);
+ }
+
+ protected abstract void printConstructorName(MethodReference method);
+
+ private void printError(String message) {
+ append("# Error: " + message);
+ }
+
+ protected abstract void printField(TracedField field);
+
+ protected abstract void printMethod(TracedMethod method);
+
+ private void printFieldError(FieldReference field) {
+ appendLine(
+ field.getFieldType().getTypeName()
+ + " "
+ + field.getHolderClass().getTypeName()
+ + "."
+ + field.getFieldName());
+ }
+
+ private void printMethodError(MethodReference method) {
+ printReturn(method);
+ append(" ");
+ append(method.getHolderClass().getTypeName());
+ append(".");
+ append(method.getMethodName());
+ printArguments(method);
+ appendLine();
+ }
+
+ protected abstract void printPackageNames(List<String> packageNames);
+
+ protected void printReturn(MethodReference method) {
+ append(method.getReturnType() != null ? method.getReturnType().getTypeName() : "void");
+ }
+
+ protected void printNameAndReturn(MethodReference method) {
+ if (method.getMethodName().equals("<init>")) {
+ printConstructorName(method);
+ } else {
+ printReturn(method);
+ append(" ");
+ append(method.getMethodName());
+ }
+ }
+
+ protected abstract void printTypeHeader(TracedClass clazz);
+
+ protected abstract void printTypeFooter();
+
+ void format(TraceReferencesResult result) {
+ int errors =
+ print(
+ result.types,
+ result.keepPackageNames,
+ result.fields,
+ result.methods,
+ result.missingDefinition);
+ assert errors == result.missingDefinition.size();
+ }
+
+ private int print(
+ Set<TracedClass> types,
+ Set<PackageReference> keepPackageNames,
+ Map<ClassReference, Set<TracedField>> fields,
+ Map<ClassReference, Set<TracedMethod>> methods,
+ Set<Object> missingDefinition) {
+ int errors = 0;
+ List<TracedClass> sortedTypes = new ArrayList<>(types);
+ sortedTypes.sort(Comparator.comparing(tracedClass -> tracedClass.getReference().getTypeName()));
+ for (TracedClass type : sortedTypes) {
+ if (missingDefinition.contains(type)) {
+ printError("Could not find definition for type " + type.getReference().getTypeName());
+ errors++;
+ continue;
+ }
+ printTypeHeader(type);
+ Set<TracedMethod> methodsForClass = methods.get(type.getReference());
+ if (methodsForClass != null) {
+ List<TracedMethod> sortedMethods = new ArrayList<>(methods.get(type.getReference()).size());
+ for (TracedMethod method : methods.get(type.getReference())) {
+ if (method.isMissingDefinition()) {
+ printError("Could not find definition for method ");
+ printMethodError(method.getReference());
+ errors++;
+ continue;
+ }
+ assert method.getAccessFlags() != null;
+ sortedMethods.add(method);
+ }
+ sortedMethods.sort(
+ Comparator.comparing(tracedMethod -> tracedMethod.getReference().toString()));
+ for (TracedMethod method : sortedMethods) {
+ printMethod(method);
+ }
+ }
+ Set<TracedField> fieldsForClass = fields.get(type.getReference());
+ if (fieldsForClass != null) {
+ List<TracedField> sortedFields = new ArrayList<>(fieldsForClass);
+ sortedFields.sort(
+ Comparator.comparing(tracedField -> tracedField.getReference().toString()));
+ for (TracedField field : sortedFields) {
+ if (field.isMissingDefinition()) {
+ printError("Could not find definition for field ");
+ printFieldError(field.getReference());
+ errors++;
+ continue;
+ }
+ printField(field);
+ }
+ }
+ printTypeFooter();
+ }
+ List<String> packageNamesToKeep =
+ keepPackageNames.stream()
+ .map(PackageReference::getPackageName)
+ .sorted()
+ .collect(Collectors.toList());
+ printPackageNames(packageNamesToKeep);
+ return errors;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/KeepRuleFormatter.java b/src/main/java/com/android/tools/r8/tracereferences/KeepRuleFormatter.java
index f259b01..85352f2 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/KeepRuleFormatter.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/KeepRuleFormatter.java
@@ -3,70 +3,66 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.tracereferences;
-import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.StringConsumer;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedClass;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedField;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedMethod;
import com.android.tools.r8.utils.StringUtils;
import java.util.List;
-class KeepRuleFormatter extends ResultFormatter {
+class KeepRuleFormatter extends Formatter {
final boolean allowObfuscation;
- KeepRuleFormatter(
- StringConsumer output, DiagnosticsHandler diagnosticsHandler, boolean allowObfuscation) {
- super(output, diagnosticsHandler);
+ KeepRuleFormatter(boolean allowObfuscation) {
this.allowObfuscation = allowObfuscation;
}
@Override
- protected void printTypeHeader(DexClass dexClass) {
+ protected void printTypeHeader(TracedClass tracedClass) {
append(allowObfuscation ? "-keep,allowobfuscation" : "-keep");
- if (dexClass.isInterface()) {
- append(" interface " + dexClass.type.toSourceString() + " {" + System.lineSeparator());
- } else if (dexClass.accessFlags.isEnum()) {
- append(" enum " + dexClass.type.toSourceString() + " {" + System.lineSeparator());
+ if (tracedClass.getAccessFlags().isInterface()) {
+ appendLine(" interface " + tracedClass.getReference().getTypeName() + " {");
+ } else if (tracedClass.getAccessFlags().isEnum()) {
+ appendLine(" enum " + tracedClass.getReference().getTypeName() + " {");
} else {
- append(" class " + dexClass.type.toSourceString() + " {" + System.lineSeparator());
+ appendLine(" class " + tracedClass.getReference().getTypeName() + " {");
}
}
@Override
- protected void printConstructorName(DexEncodedMethod encodedMethod) {
+ protected void printConstructorName(MethodReference method) {
append("<init>");
}
@Override
- protected void printField(DexClass dexClass, DexField field) {
+ protected void printField(TracedField field) {
append(
" "
- + field.type.toSourceString()
+ + field.getReference().getFieldType().getTypeName()
+ " "
- + field.name.toString()
+ + field.getReference().getFieldName()
+ ";"
+ System.lineSeparator());
}
@Override
- protected void printMethod(DexEncodedMethod encodedMethod, String typeName) {
- // Static initializers do not require keep rules - it is kept by keeping the class.
- if (encodedMethod.accessFlags.isConstructor() && encodedMethod.accessFlags.isStatic()) {
+ protected void printMethod(TracedMethod tracedMethod) {
+ if (tracedMethod.getReference().getMethodName().equals("<clinit>")) {
return;
}
append(" ");
- if (encodedMethod.isPublicMethod()) {
+ if (tracedMethod.getAccessFlags().isPublic()) {
append("public ");
- } else if (encodedMethod.isPrivateMethod()) {
+ } else if (tracedMethod.getAccessFlags().isPrivate()) {
append("private ");
- } else if (encodedMethod.isProtectedMethod()) {
+ } else if (tracedMethod.getAccessFlags().isProtected()) {
append("protected ");
}
- if (encodedMethod.isStatic()) {
+ if (tracedMethod.getAccessFlags().isStatic()) {
append("static ");
}
- printNameAndReturn(encodedMethod);
- printArguments(encodedMethod.method);
+ printNameAndReturn(tracedMethod.getReference());
+ printArguments(tracedMethod.getReference());
appendLine(";");
}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/PrintUsesFormatter.java b/src/main/java/com/android/tools/r8/tracereferences/PrintUsesFormatter.java
index 12b2e6b..a29fdb5 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/PrintUsesFormatter.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/PrintUsesFormatter.java
@@ -3,35 +3,30 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.tracereferences;
-import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.StringConsumer;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedClass;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedField;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedMethod;
import java.util.List;
-class PrintUsesFormatter extends ResultFormatter {
-
- PrintUsesFormatter(StringConsumer output, DiagnosticsHandler diagnosticsHandler) {
- super(output, diagnosticsHandler);
- }
+class PrintUsesFormatter extends Formatter {
@Override
- protected void printConstructorName(DexEncodedMethod encodedMethod) {
- if (encodedMethod.accessFlags.isStatic()) {
+ protected void printConstructorName(MethodReference method) {
+ if (method.getMethodName().equals("<clinit>")) {
append("<clinit>");
} else {
- String holderName = encodedMethod.holder().toSourceString();
+ String holderName = method.getHolderClass().getTypeName();
String constructorName = holderName.substring(holderName.lastIndexOf('.') + 1);
append(constructorName);
}
}
@Override
- protected void printMethod(DexEncodedMethod encodedMethod, String typeName) {
- append(typeName + ": ");
- printNameAndReturn(encodedMethod);
- printArguments(encodedMethod.method);
+ protected void printMethod(TracedMethod method) {
+ append(method.getReference().getHolderClass().getTypeName() + ": ");
+ printNameAndReturn(method.getReference());
+ printArguments(method.getReference());
appendLine("");
}
@@ -41,20 +36,20 @@
}
@Override
- protected void printTypeHeader(DexClass dexClass) {
- appendLine(dexClass.type.toSourceString());
+ protected void printTypeHeader(TracedClass type) {
+ appendLine(type.getReference().getTypeName());
}
@Override
protected void printTypeFooter() {}
@Override
- protected void printField(DexClass dexClass, DexField field) {
+ protected void printField(TracedField field) {
appendLine(
- dexClass.type.toSourceString()
+ field.getReference().getHolderClass().getTypeName()
+ ": "
- + field.type.toSourceString()
+ + field.getReference().getFieldType().getTypeName()
+ " "
- + field.name.toString());
+ + field.getReference().getFieldName());
}
}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/Result.java b/src/main/java/com/android/tools/r8/tracereferences/Result.java
deleted file mode 100644
index 6889f0c..0000000
--- a/src/main/java/com/android/tools/r8/tracereferences/Result.java
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) 2020, 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.tracereferences;
-
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexReference;
-import com.android.tools.r8.graph.DexType;
-import java.util.Map;
-import java.util.Set;
-
-class Result {
- final DexApplication application;
- final Set<DexType> types;
- final Set<String> keepPackageNames;
- final Map<DexType, Set<DexField>> fields;
- final Map<DexType, Set<DexMethod>> methods;
- final Set<DexReference> missingDefinition;
-
- Result(
- DexApplication application,
- Set<DexType> types,
- Set<String> keepPackageNames,
- Map<DexType, Set<DexField>> fields,
- Map<DexType, Set<DexMethod>> methods,
- Set<DexReference> missingDefinition) {
- this.application = application;
- this.types = types;
- this.keepPackageNames = keepPackageNames;
- this.fields = fields;
- this.methods = methods;
- this.missingDefinition = missingDefinition;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/ResultFormatter.java b/src/main/java/com/android/tools/r8/tracereferences/ResultFormatter.java
deleted file mode 100644
index 2fd8934..0000000
--- a/src/main/java/com/android/tools/r8/tracereferences/ResultFormatter.java
+++ /dev/null
@@ -1,142 +0,0 @@
-// Copyright (c) 2020, 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.tracereferences;
-
-import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.StringConsumer;
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexReference;
-import com.android.tools.r8.graph.DexType;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-abstract class ResultFormatter {
-
- private final StringConsumer output;
- private final DiagnosticsHandler diagnosticsHandler;
-
- ResultFormatter(StringConsumer output, DiagnosticsHandler diagnosticsHandler) {
- this.output = output;
- this.diagnosticsHandler = diagnosticsHandler;
- }
-
- protected void append(String string) {
- output.accept(string, diagnosticsHandler);
- }
-
- protected void appendLine(String string) {
- output.accept(string + System.lineSeparator(), diagnosticsHandler);
- }
-
- protected void printArguments(DexMethod method) {
- append("(");
- for (int i = 0; i < method.getArity(); i++) {
- if (i != 0) {
- append(",");
- }
- append(method.proto.parameters.values[i].toSourceString());
- }
- append(")");
- }
-
- protected abstract void printConstructorName(DexEncodedMethod encodedMethod);
-
- private void printError(String message) {
- appendLine("# Error: " + message);
- }
-
- protected abstract void printField(DexClass dexClass, DexField field);
-
- protected abstract void printMethod(DexEncodedMethod encodedMethod, String typeName);
-
- protected abstract void printPackageNames(List<String> packageNames);
-
- protected void printNameAndReturn(DexEncodedMethod encodedMethod) {
- if (encodedMethod.accessFlags.isConstructor()) {
- printConstructorName(encodedMethod);
- } else {
- DexMethod method = encodedMethod.method;
- append(method.proto.returnType.toSourceString());
- append(" ");
- append(method.name.toSourceString());
- }
- }
-
- protected abstract void printTypeHeader(DexClass dexClass);
-
- protected abstract void printTypeFooter();
-
- void format(Result result) {
- int errors =
- print(
- result.application,
- result.types,
- result.keepPackageNames,
- result.fields,
- result.methods,
- result.missingDefinition);
- output.finished(diagnosticsHandler);
- assert errors == result.missingDefinition.size();
- }
-
- private int print(
- DexApplication application,
- Set<DexType> types,
- Set<String> keepPackageNames,
- Map<DexType, Set<DexField>> fields,
- Map<DexType, Set<DexMethod>> methods,
- Set<DexReference> missingDefinition) {
- int errors = 0;
- List<DexType> sortedTypes = new ArrayList<>(types);
- sortedTypes.sort(Comparator.comparing(DexType::toSourceString));
- for (DexType type : sortedTypes) {
- DexClass dexClass = application.definitionFor(type);
- if (missingDefinition.contains(type)) {
- assert dexClass == null;
- printError("Could not find definition for type " + type.toSourceString());
- errors++;
- continue;
- }
- printTypeHeader(dexClass);
- List<DexEncodedMethod> methodDefinitions = new ArrayList<>(methods.size());
- for (DexMethod method : methods.get(type)) {
- DexEncodedMethod encodedMethod = dexClass.lookupMethod(method);
- if (missingDefinition.contains(method)) {
- assert encodedMethod == null;
- printError("Could not find definition for method " + method.toSourceString());
- errors++;
- continue;
- }
- methodDefinitions.add(encodedMethod);
- }
- methodDefinitions.sort(Comparator.comparing(x -> x.method.name.toSourceString()));
- for (DexEncodedMethod encodedMethod : methodDefinitions) {
- printMethod(encodedMethod, dexClass.type.toSourceString());
- }
- List<DexField> sortedFields = new ArrayList<>(fields.get(type));
- sortedFields.sort(Comparator.comparing(DexField::toSourceString));
- for (DexField field : sortedFields) {
- if (missingDefinition.contains(field)) {
- printError("Could not find definition for field " + field.toSourceString());
- errors++;
- continue;
- }
- printField(dexClass, field);
- }
- printTypeFooter();
- }
- ArrayList<String> packageNamesToKeep = new ArrayList<>(keepPackageNames);
- Collections.sort(packageNamesToKeep);
- printPackageNames(packageNamesToKeep);
- return errors;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
index d0ec93e..acc2cb6 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
@@ -32,7 +32,7 @@
throw new CompilationFailedException();
} catch (Exception e) {
command.getDiagnosticsHandler().error(new ExceptionDiagnostic(e));
- throw new CompilationFailedException();
+ throw new CompilationFailedException(e);
}
}
@@ -46,8 +46,8 @@
if (command.getSource().isEmpty()) {
throw new TraceReferencesException("No source specified");
}
- if (command.getOutput() == null) {
- throw new TraceReferencesException("No output specified");
+ if (command.getConsumer() == null) {
+ throw new TraceReferencesException("No consumer specified");
}
AndroidApp.Builder builder = AndroidApp.builder();
command.getLibrary().forEach(builder::addLibraryResourceProvider);
@@ -80,25 +80,8 @@
}
}
}
- Tracer tracer = new Tracer(tagetDescriptors, builder.build());
- Result result = tracer.run();
- ResultFormatter formatter;
- switch (command.getOutputFormat()) {
- case PRINTUSAGE:
- formatter = new PrintUsesFormatter(command.getOutput(), command.getDiagnosticsHandler());
- break;
- case KEEP_RULES:
- formatter =
- new KeepRuleFormatter(command.getOutput(), command.getDiagnosticsHandler(), false);
- break;
- case KEEP_RULES_WITH_ALLOWOBFUSCATION:
- formatter =
- new KeepRuleFormatter(command.getOutput(), command.getDiagnosticsHandler(), true);
- break;
- default:
- throw new TraceReferencesException("Unexpected format " + command.getOutputFormat().name());
- }
- formatter.format(result);
+ Tracer tracer = new Tracer(tagetDescriptors, builder.build(), command.getDiagnosticsHandler());
+ tracer.run(command.getConsumer());
}
public static void run(String... args) throws CompilationFailedException {
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java
index f6fcb69..ae0ee57 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java
@@ -11,10 +11,8 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.Keep;
import com.android.tools.r8.ProgramResourceProvider;
-import com.android.tools.r8.StringConsumer;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
-import com.android.tools.r8.tracereferences.TraceReferencesCommandParser.OutputFormat;
import com.android.tools.r8.utils.ArchiveResourceProvider;
import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.StringDiagnostic;
@@ -35,8 +33,7 @@
private final ImmutableList<ClassFileResourceProvider> library;
private final ImmutableList<ClassFileResourceProvider> traceTarget;
private final ImmutableList<ProgramResourceProvider> traceSource;
- private final StringConsumer output;
- private final OutputFormat outputFormat;
+ private final TraceReferencesConsumer consumer;
TraceReferencesCommand(
boolean printHelp,
@@ -45,16 +42,14 @@
ImmutableList<ClassFileResourceProvider> library,
ImmutableList<ClassFileResourceProvider> traceTarget,
ImmutableList<ProgramResourceProvider> traceSource,
- StringConsumer output,
- OutputFormat outputFormat) {
+ TraceReferencesConsumer consumer) {
this.printHelp = printHelp;
this.printVersion = printVersion;
this.diagnosticsHandler = diagnosticsHandler;
this.library = library;
this.traceTarget = traceTarget;
this.traceSource = traceSource;
- this.output = output;
- this.outputFormat = outputFormat;
+ this.consumer = consumer;
}
/**
@@ -101,8 +96,7 @@
ImmutableList.builder();
private final ImmutableList.Builder<ProgramResourceProvider> traceSourceBuilder =
ImmutableList.builder();
- private StringConsumer output;
- private OutputFormat outputFormat = TraceReferencesCommandParser.OutputFormat.PRINTUSAGE;
+ private TraceReferencesConsumer consumer;
private Builder(DiagnosticsHandler diagnosticsHandler) {
this.diagnosticsHandler = diagnosticsHandler;
@@ -197,13 +191,8 @@
return this;
}
- Builder setOutputPath(Path output) {
- this.output = new StringConsumer.FileConsumer(output);
- return this;
- }
-
- Builder setOutputFormat(OutputFormat outputFormat) {
- this.outputFormat = outputFormat;
+ Builder setConsumer(TraceReferencesConsumer consumer) {
+ this.consumer = consumer;
return this;
}
@@ -217,8 +206,7 @@
libraryBuilder.build(),
traceTarget,
traceSource,
- output,
- outputFormat);
+ consumer);
}
void error(Diagnostic diagnostic) {
@@ -244,11 +232,7 @@
return traceSource;
}
- StringConsumer getOutput() {
- return output;
- }
-
- OutputFormat getOutputFormat() {
- return outputFormat;
+ TraceReferencesConsumer getConsumer() {
+ return consumer;
}
}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommandParser.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommandParser.java
index 79de8e2..476d0d4 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommandParser.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommandParser.java
@@ -6,11 +6,13 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.JdkClassFileProvider;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.tracereferences.TraceReferencesFormattingConsumer.OutputFormat;
import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.FlagFile;
import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -19,17 +21,6 @@
class TraceReferencesCommandParser {
- enum OutputFormat {
- /** Format used with the -printusage flag */
- PRINTUSAGE,
- /** Keep rules keeping each of the traced references */
- KEEP_RULES,
- /**
- * Keep rules with <code>allowobfuscation</code> modifier keeping each of the traced references
- */
- KEEP_RULES_WITH_ALLOWOBFUSCATION
- }
-
private static final Set<String> OPTIONS_WITH_PARAMETER =
ImmutableSet.of("--lib", "--target", "--source", "--format", "--output");
@@ -78,6 +69,8 @@
private TraceReferencesCommand.Builder parse(
String[] args, Origin origin, TraceReferencesCommand.Builder builder) {
String[] expandedArgs = FlagFile.expandFlagFiles(args, builder::error);
+ Path output = null;
+ OutputFormat format = TraceReferencesFormattingConsumer.OutputFormat.PRINTUSAGE;
for (int i = 0; i < expandedArgs.length; i++) {
String arg = expandedArgs[i].trim();
String nextArg = null;
@@ -103,28 +96,42 @@
} else if (arg.equals("--source")) {
builder.addSourceFiles(Paths.get(nextArg));
} else if (arg.equals("--format")) {
- OutputFormat format = null;
if (nextArg.equals("printuses")) {
- format = OutputFormat.PRINTUSAGE;
+ format = TraceReferencesFormattingConsumer.OutputFormat.PRINTUSAGE;
}
if (nextArg.equals("keep")) {
- format = OutputFormat.KEEP_RULES;
+ format = TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES;
}
if (nextArg.equals("keepallowobfuscation")) {
- format = OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION;
+ format = TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION;
}
if (format == null) {
builder.error(new StringDiagnostic("Unsupported format '" + nextArg + "'"));
}
- builder.setOutputFormat(format);
} else if (arg.equals("--output")) {
- builder.setOutputPath(Paths.get(nextArg));
+ output = Paths.get(nextArg);
} else if (arg.startsWith("@")) {
builder.error(new StringDiagnostic("Recursive @argfiles are not supported: ", origin));
} else {
builder.error(new StringDiagnostic("Unsupported argument '" + arg + "'"));
}
}
+ final Path finalOutput = output;
+ builder.setConsumer(
+ new TraceReferencesFormattingConsumer(format) {
+ @Override
+ public void finished() {
+ PrintStream out = System.out;
+ if (finalOutput != null) {
+ try {
+ out = new PrintStream(Files.newOutputStream(finalOutput));
+ } catch (IOException e) {
+ builder.error(new ExceptionDiagnostic(e));
+ }
+ }
+ out.print(get());
+ }
+ });
return builder;
}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesConsumer.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesConsumer.java
new file mode 100644
index 0000000..38b8086
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesConsumer.java
@@ -0,0 +1,90 @@
+// Copyright (c) 2020, 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.tracereferences;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.KeepForSubclassing;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.PackageReference;
+
+/** Consumer interface for recording references */
+@KeepForSubclassing
+public interface TraceReferencesConsumer {
+
+ /**
+ * Interface for asking for the access flags for a traced reference when the definition is present
+ */
+ @Keep
+ interface AccessFlags {
+ boolean isStatic();
+
+ boolean isPublic();
+
+ boolean isProtected();
+
+ boolean isPrivate();
+ }
+
+ /**
+ * Interface for asking for additional class information for a traced class when the definition is
+ * found.
+ */
+ @Keep
+ interface ClassAccessFlags extends AccessFlags {
+ boolean isInterface();
+
+ boolean isEnum();
+ }
+
+ @Keep
+ interface FieldAccessFlags extends AccessFlags {}
+
+ @Keep
+ interface MethodAccessFlags extends AccessFlags {}
+
+ /** Interface implemented by all references reported */
+ @Keep
+ interface TracedReference<T, F> {
+ /** Returns if the reference does not have a definition in the program traced. */
+ boolean isMissingDefinition();
+
+ /** Returns the reference traced. */
+ T getReference();
+
+ /**
+ * Returns the access flags for the reference traced. If the definition is not found (<code>
+ * isMissingDefinition()</code> returns <code>true</code>) the access flags are not known and
+ * this returns <code>null</code>.
+ */
+ F getAccessFlags();
+ }
+
+ @Keep
+ interface TracedClass extends TracedReference<ClassReference, ClassAccessFlags> {}
+
+ @Keep
+ interface TracedField extends TracedReference<FieldReference, FieldAccessFlags> {}
+
+ @Keep
+ interface TracedMethod extends TracedReference<MethodReference, MethodAccessFlags> {}
+
+ /** Class has been traced. */
+ void acceptType(TracedClass tracedClazz);
+
+ /** Field has been traced. */
+ void acceptField(TracedField tracedField);
+
+ /** Method has been traced. */
+ void acceptMethod(TracedMethod tracedMethod);
+
+ /** Package which is required for package privatge access has been traced. */
+ void acceptPackage(PackageReference pkg);
+
+ /**
+ * Tracing has finished. There will be no more calls to any of the <code>acceptXXX</code> methods.
+ */
+ void finished();
+}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesFormattingConsumer.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesFormattingConsumer.java
new file mode 100644
index 0000000..67167c4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesFormattingConsumer.java
@@ -0,0 +1,79 @@
+// Copyright (c) 2020, 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.tracereferences;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.references.PackageReference;
+
+class TraceReferencesFormattingConsumer implements TraceReferencesConsumer {
+
+ public enum OutputFormat {
+ /** Format used with the -printusage flag */
+ PRINTUSAGE,
+ /** Keep rules keeping each of the traced references */
+ KEEP_RULES,
+ /**
+ * Keep rules with <code>allowobfuscation</code> modifier keeping each of the traced references
+ */
+ KEEP_RULES_WITH_ALLOWOBFUSCATION
+ }
+
+ private final OutputFormat format;
+ private final TraceReferencesResult.Builder builder = TraceReferencesResult.builder();
+ private boolean finishedCalled = false;
+
+ public TraceReferencesFormattingConsumer(OutputFormat format) {
+ this.format = format;
+ }
+
+ @Override
+ public void acceptType(TracedClass type) {
+ assert !finishedCalled;
+ builder.acceptType(type);
+ }
+
+ @Override
+ public void acceptField(TracedField field) {
+ assert !finishedCalled;
+ builder.acceptField(field);
+ }
+
+ @Override
+ public void acceptMethod(TracedMethod method) {
+ assert !finishedCalled;
+ builder.acceptMethod(method);
+ }
+
+ @Override
+ public void acceptPackage(PackageReference pkg) {
+ assert !finishedCalled;
+ builder.acceptPackage(pkg);
+ }
+
+ @Override
+ public void finished() {
+ assert !finishedCalled;
+ finishedCalled = true;
+ }
+
+ public String get() {
+ TraceReferencesResult result = builder.build();
+ Formatter formatter;
+ switch (format) {
+ case PRINTUSAGE:
+ formatter = new PrintUsesFormatter();
+ break;
+ case KEEP_RULES:
+ formatter = new KeepRuleFormatter(false);
+ break;
+ case KEEP_RULES_WITH_ALLOWOBFUSCATION:
+ formatter = new KeepRuleFormatter(true);
+ break;
+ default:
+ throw new Unreachable();
+ }
+ formatter.format(result);
+ return formatter.get();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesResult.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesResult.java
new file mode 100644
index 0000000..f8dab44
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesResult.java
@@ -0,0 +1,94 @@
+// Copyright (c) 2020, 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.tracereferences;
+
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.PackageReference;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedClass;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedField;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedMethod;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+class TraceReferencesResult {
+
+ final Set<TracedClass> types;
+ final Map<ClassReference, Set<TracedField>> fields;
+ final Map<ClassReference, Set<TracedMethod>> methods;
+ final Set<PackageReference> keepPackageNames;
+ final Set<Object> missingDefinition;
+
+ TraceReferencesResult(
+ Set<TracedClass> types,
+ Map<ClassReference, Set<TracedField>> fields,
+ Map<ClassReference, Set<TracedMethod>> methods,
+ Set<PackageReference> keepPackageNames,
+ Set<Object> missingDefinition) {
+ this.types = types;
+ this.fields = fields;
+ this.methods = methods;
+ this.keepPackageNames = keepPackageNames;
+ this.missingDefinition = missingDefinition;
+ }
+
+ static Builder builder() {
+ return new Builder();
+ }
+
+ static class Builder implements TraceReferencesConsumer {
+ private final Set<TracedClass> types = new HashSet<>();
+ private final Map<ClassReference, Set<TracedField>> fields = new HashMap<>();
+ private final Map<ClassReference, Set<TracedMethod>> methods = new HashMap<>();
+ private final Set<Object> missingDefinition = new HashSet<>();
+ private final Set<PackageReference> keepPackageNames = new HashSet<>();
+
+ @Override
+ public void acceptType(TracedClass tracedClass) {
+ types.add(tracedClass);
+ if (tracedClass.isMissingDefinition()) {
+ this.missingDefinition.add(tracedClass.getReference());
+ }
+ }
+
+ @Override
+ public void acceptField(TracedField tracedField) {
+ FieldReference field = tracedField.getReference();
+ fields.computeIfAbsent(field.getHolderClass(), k -> new HashSet<>()).add(tracedField);
+ if (tracedField.isMissingDefinition()) {
+ this.missingDefinition.add(field);
+ }
+ }
+
+ @Override
+ public void acceptMethod(TracedMethod tracedMethod) {
+ MethodReference method = tracedMethod.getReference();
+ methods.computeIfAbsent(method.getHolderClass(), k -> new HashSet<>()).add(tracedMethod);
+ if (tracedMethod.isMissingDefinition()) {
+ this.missingDefinition.add(method);
+ }
+ }
+
+ @Override
+ public void acceptPackage(PackageReference pkg) {
+ keepPackageNames.add(pkg);
+ }
+
+ @Override
+ public void finished() {}
+
+ TraceReferencesResult build() {
+ missingDefinition.forEach(
+ missingDefinition -> {
+ assert missingDefinition instanceof ClassReference
+ || missingDefinition instanceof FieldReference
+ || missingDefinition instanceof MethodReference;
+ });
+ return new TraceReferencesResult(types, fields, methods, keepPackageNames, missingDefinition);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
index 5ad1cfa..d30158b 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
@@ -5,6 +5,7 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -17,7 +18,6 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueArray;
@@ -26,29 +26,209 @@
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
import com.android.tools.r8.shaking.MainDexClasses;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.AccessFlags;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.ClassAccessFlags;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.FieldAccessFlags;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.MethodAccessFlags;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedClass;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedField;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedMethod;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedReference;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.Timing;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
+import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Set;
class Tracer {
+ static class AccessFlagsImpl<T extends com.android.tools.r8.graph.AccessFlags<T>>
+ implements AccessFlags {
+ T accessFlags;
+
+ AccessFlagsImpl(T accessFlags) {
+ this.accessFlags = accessFlags;
+ }
+
+ @Override
+ public boolean isStatic() {
+ return accessFlags.isStatic();
+ }
+
+ @Override
+ public boolean isPublic() {
+ return accessFlags.isPublic();
+ }
+
+ @Override
+ public boolean isProtected() {
+ return accessFlags.isProtected();
+ }
+
+ @Override
+ public boolean isPrivate() {
+ return accessFlags.isPrivate();
+ }
+ }
+
+ static class ClassAccessFlagsImpl
+ extends AccessFlagsImpl<com.android.tools.r8.graph.ClassAccessFlags>
+ implements ClassAccessFlags {
+ ClassAccessFlagsImpl(com.android.tools.r8.graph.ClassAccessFlags accessFlags) {
+ super(accessFlags);
+ }
+
+ @Override
+ public boolean isInterface() {
+ return accessFlags.isInterface();
+ }
+
+ @Override
+ public boolean isEnum() {
+ return accessFlags.isEnum();
+ }
+ }
+
+ static class FieldAccessFlagsImpl
+ extends AccessFlagsImpl<com.android.tools.r8.graph.FieldAccessFlags>
+ implements FieldAccessFlags {
+ FieldAccessFlagsImpl(com.android.tools.r8.graph.FieldAccessFlags accessFlags) {
+ super(accessFlags);
+ }
+ }
+
+ static class MethodAccessFlagsImpl
+ extends AccessFlagsImpl<com.android.tools.r8.graph.MethodAccessFlags>
+ implements MethodAccessFlags {
+ MethodAccessFlagsImpl(com.android.tools.r8.graph.MethodAccessFlags accessFlags) {
+ super(accessFlags);
+ }
+ }
+
+ abstract static class TracedReferenceBase<T, F> implements TracedReference<T, F> {
+ private final T reference;
+ private final F accessFlags;
+ private final boolean missingDefinition;
+
+ private TracedReferenceBase(T reference, F accessFlags, boolean missingDefinition) {
+ assert accessFlags != null || missingDefinition;
+ this.reference = reference;
+ this.accessFlags = accessFlags;
+ this.missingDefinition = missingDefinition;
+ }
+
+ @Override
+ public T getReference() {
+ return reference;
+ }
+
+ @Override
+ public boolean isMissingDefinition() {
+ return missingDefinition;
+ }
+
+ @Override
+ public F getAccessFlags() {
+ return accessFlags;
+ }
+
+ @Override
+ public int hashCode() {
+ // Equality is only based on the reference.
+ return reference.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ // Equality is only based on the reference.
+ if (!(other instanceof TracedReferenceBase)) {
+ return false;
+ }
+ return reference.equals(((TracedReferenceBase<?, ?>) other).reference);
+ }
+
+ public abstract String getKindName();
+ }
+
+ static class TracedClassImpl extends TracedReferenceBase<ClassReference, ClassAccessFlags>
+ implements TracedClass {
+ private TracedClassImpl(DexType reference, DexClass definition) {
+ super(
+ Reference.classFromDescriptor(reference.toDescriptorString()),
+ definition != null ? new ClassAccessFlagsImpl(definition.getAccessFlags()) : null,
+ definition == null);
+ }
+
+ @Override
+ public String getKindName() {
+ return "type";
+ }
+
+ @Override
+ public String toString() {
+ return getReference().getTypeName();
+ }
+ }
+
+ static class TracedFieldImpl extends TracedReferenceBase<FieldReference, FieldAccessFlags>
+ implements TracedField {
+ private TracedFieldImpl(DexField reference, DexEncodedField definition) {
+ super(
+ Reference.field(
+ Reference.classFromDescriptor(reference.holder.toDescriptorString()),
+ reference.name.toString(),
+ Reference.typeFromDescriptor(reference.type.toDescriptorString())),
+ definition != null ? new FieldAccessFlagsImpl(definition.getAccessFlags()) : null,
+ definition == null);
+ }
+
+ @Override
+ public String getKindName() {
+ return "field";
+ }
+
+ @Override
+ public String toString() {
+ return getReference().toString();
+ }
+ }
+
+ static class TracedMethodImpl extends TracedReferenceBase<MethodReference, MethodAccessFlags>
+ implements TracedMethod {
+ private TracedMethodImpl(DexMethod reference, DexEncodedMethod definition) {
+ super(
+ reference.asMethodReference(),
+ definition != null ? new MethodAccessFlagsImpl(definition.getAccessFlags()) : null,
+ definition == null);
+ }
+
+ @Override
+ public String getKindName() {
+ return "method";
+ }
+
+ @Override
+ public String toString() {
+ return getReference().toString();
+ }
+ }
+
private final Set<String> descriptors;
- private Set<DexType> types = Sets.newIdentityHashSet();
- private Map<DexType, Set<DexMethod>> methods = Maps.newIdentityHashMap();
- private Map<DexType, Set<DexField>> fields = Maps.newIdentityHashMap();
- private Set<String> keepPackageNames = Sets.newHashSet();
- private Set<DexReference> missingDefinitions = Sets.newHashSet();
+ private final DiagnosticsHandler diagnostics;
private final DirectMappedDexApplication application;
private final AppInfoWithClassHierarchy appInfo;
- Tracer(Set<String> descriptors, AndroidApp inputApp) throws Exception {
+ Tracer(Set<String> descriptors, AndroidApp inputApp, DiagnosticsHandler diagnostics)
+ throws Exception {
this.descriptors = descriptors;
+ this.diagnostics = diagnostics;
InternalOptions options = new InternalOptions();
application =
new ApplicationReader(inputApp, options, new Timing("ReferenceTrace")).read().toDirect();
@@ -59,8 +239,8 @@
MainDexClasses.createEmptyMainDexClasses());
}
- Result run() {
- UseCollector useCollector = new UseCollector(appInfo.dexItemFactory());
+ void run(TraceReferencesConsumer consumer) {
+ UseCollector useCollector = new UseCollector(appInfo.dexItemFactory(), consumer, diagnostics);
for (DexProgramClass clazz : application.classes()) {
useCollector.setContext(clazz);
useCollector.registerSuperType(clazz, clazz.superType);
@@ -70,74 +250,80 @@
clazz.forEachProgramMethod(useCollector::registerMethod);
clazz.forEachField(useCollector::registerField);
}
-
- return new Result(application, types, keepPackageNames, fields, methods, missingDefinitions);
- }
-
- private boolean isTargetType(DexType type) {
- return descriptors.contains(type.toDescriptorString());
- }
-
- private void addType(DexType type) {
- if (isTargetType(type) && types.add(type)) {
- DexClass clazz = appInfo.definitionFor(type);
- if (clazz != null && clazz.accessFlags.isVisibilityDependingOnPackage()) {
- keepPackageNames.add(clazz.type.getPackageName());
- }
- methods.put(type, Sets.newIdentityHashSet());
- fields.put(type, Sets.newIdentityHashSet());
- }
- }
-
- private void addField(DexField field) {
- addType(field.type);
- DexEncodedField baseField = appInfo.resolveField(field).getResolvedField();
- if (baseField != null && baseField.holder() != field.holder) {
- field = baseField.field;
- }
- addType(field.holder);
- if (isTargetType(field.holder)) {
- Set<DexField> typeFields = fields.get(field.holder);
- assert typeFields != null;
- if (baseField != null) {
- if (baseField.accessFlags.isVisibilityDependingOnPackage()) {
- keepPackageNames.add(baseField.holder().getPackageName());
- }
- } else {
- missingDefinitions.add(field);
- }
- typeFields.add(field);
- }
- }
-
- private void addMethod(DexMethod method) {
- addType(method.holder);
- for (DexType parameterType : method.proto.parameters.values) {
- addType(parameterType);
- }
- addType(method.proto.returnType);
- if (isTargetType(method.holder)) {
- Set<DexMethod> typeMethods = methods.get(method.holder);
- assert typeMethods != null;
- DexClass holder = appInfo.definitionForHolder(method);
- DexEncodedMethod definition = method.lookupOnClass(holder);
- if (definition != null) {
- if (definition.accessFlags.isVisibilityDependingOnPackage()) {
- keepPackageNames.add(definition.holder().getPackageName());
- }
- } else {
- missingDefinitions.add(method);
- }
- typeMethods.add(method);
- }
+ consumer.finished();
}
class UseCollector extends UseRegistry {
+ private final TraceReferencesConsumer consumer;
private DexProgramClass context;
+ private final DiagnosticsHandler diagnostics;
+ private Set<TracedReference<?, ?>> missingDefinitionReported = new HashSet<>();
- UseCollector(DexItemFactory factory) {
+ UseCollector(
+ DexItemFactory factory, TraceReferencesConsumer consumer, DiagnosticsHandler diagnostics) {
super(factory);
+ this.consumer = consumer;
+ this.diagnostics = diagnostics;
+ }
+
+ private boolean isTargetType(DexType type) {
+ return descriptors.contains(type.toDescriptorString());
+ }
+
+ private void addType(DexType type) {
+ if (isTargetType(type)) {
+ DexClass clazz = appInfo.definitionFor(type);
+ TracedClassImpl tracedClass = new TracedClassImpl(type, clazz);
+ consumer.acceptType(tracedClass);
+ checkDiagnostics(tracedClass);
+ if (clazz != null && clazz.accessFlags.isVisibilityDependingOnPackage()) {
+ consumer.acceptPackage(Reference.packageFromString(clazz.type.getPackageName()));
+ }
+ }
+ }
+
+ private void addField(DexField field) {
+ addType(field.type);
+ DexEncodedField baseField = appInfo.resolveField(field).getResolvedField();
+ if (baseField != null && baseField.holder() != field.holder) {
+ field = baseField.field;
+ }
+ addType(field.holder);
+ if (isTargetType(field.holder)) {
+ TracedFieldImpl tracedField = new TracedFieldImpl(field, baseField);
+ consumer.acceptField(tracedField);
+ checkDiagnostics(tracedField);
+ if (baseField != null && baseField.accessFlags.isVisibilityDependingOnPackage()) {
+ consumer.acceptPackage(Reference.packageFromString(baseField.holder().getPackageName()));
+ }
+ }
+ }
+
+ private void addMethod(DexMethod method) {
+ addType(method.holder);
+ for (DexType parameterType : method.proto.parameters.values) {
+ addType(parameterType);
+ }
+ addType(method.proto.returnType);
+ if (isTargetType(method.holder)) {
+ DexClass holder = appInfo.definitionForHolder(method);
+ DexEncodedMethod definition = method.lookupOnClass(holder);
+ TracedMethodImpl tracedMethod = new TracedMethodImpl(method, definition);
+ consumer.acceptMethod(tracedMethod);
+ checkDiagnostics(tracedMethod);
+ if (definition != null && definition.accessFlags.isVisibilityDependingOnPackage()) {
+ consumer.acceptPackage(Reference.packageFromString(definition.holder().getPackageName()));
+ }
+ }
+ }
+
+ private void checkDiagnostics(TracedReferenceBase<?, ?> tracedReference) {
+ if (tracedReference.isMissingDefinition() && missingDefinitionReported.add(tracedReference)) {
+ diagnostics.warning(
+ new StringDiagnostic(
+ "Missing definition of " + tracedReference.getKindName() + " " + tracedReference));
+ }
}
public void setContext(DexProgramClass context) {
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java
index 489d8b2..88ec578 100644
--- a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java
@@ -14,7 +14,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.tracereferences.TraceReferencesCommandParser.OutputFormat;
+import com.android.tools.r8.tracereferences.TraceReferencesFormattingConsumer.OutputFormat;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.FileUtils;
@@ -26,6 +26,7 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
+import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -53,8 +54,7 @@
assertEquals(0, command.getLibrary().size());
assertEquals(0, command.getTarget().size());
assertEquals(0, command.getSource().size());
- assertEquals(TraceReferencesCommandParser.OutputFormat.PRINTUSAGE, command.getOutputFormat());
- assertNull(command.getOutput());
+ assertNull(command.getConsumer());
}
@Test(expected = CompilationFailedException.class)
@@ -105,29 +105,48 @@
}
private String formatName(OutputFormat format) {
- if (format == TraceReferencesCommandParser.OutputFormat.PRINTUSAGE) {
+ if (format == TraceReferencesFormattingConsumer.OutputFormat.PRINTUSAGE) {
return "printuses";
}
- if (format == TraceReferencesCommandParser.OutputFormat.KEEP_RULES) {
+ if (format == TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES) {
return "keep";
}
- assertSame(format, TraceReferencesCommandParser.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION);
+ assertSame(
+ format, TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION);
return "keepallowobfuscation";
}
public void runAndCheckOutput(
Path targetJar, Path sourceJar, OutputFormat format, String expected) throws Throwable {
+ runAndCheckOutput(targetJar, sourceJar, format, expected, null);
+ }
+
+ public void runAndCheckOutput(
+ Path targetJar,
+ Path sourceJar,
+ OutputFormat format,
+ String expected,
+ Consumer<DiagnosticsChecker> diagnosticsCheckerConsumer)
+ throws Throwable {
Path dir = temp.newFolder().toPath();
Path output = dir.resolve("output.txt");
+ DiagnosticsChecker diagnosticsChecker = new DiagnosticsChecker();
+ TraceReferencesFormattingConsumer consumer = new TraceReferencesFormattingConsumer(format);
TraceReferences.run(
- TraceReferencesCommand.builder()
+ TraceReferencesCommand.builder(diagnosticsChecker)
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
.addTargetFiles(targetJar)
.addSourceFiles(sourceJar)
- .setOutputPath(output)
- .setOutputFormat(format)
+ .setConsumer(consumer)
.build());
- assertEquals(expected, FileUtils.readTextFile(output, Charsets.UTF_8));
+ assertEquals(expected, consumer.get());
+ if (diagnosticsCheckerConsumer != null) {
+ diagnosticsCheckerConsumer.accept(diagnosticsChecker);
+ } else {
+ assertEquals(0, diagnosticsChecker.errors.size());
+ assertEquals(0, diagnosticsChecker.warnings.size());
+ assertEquals(0, diagnosticsChecker.infos.size());
+ }
TraceReferences.run(
TraceReferencesCommand.parse(
@@ -172,7 +191,7 @@
runAndCheckOutput(
ImmutableList.of(Target.class),
ImmutableList.of(Source.class),
- TraceReferencesCommandParser.OutputFormat.PRINTUSAGE,
+ TraceReferencesFormattingConsumer.OutputFormat.PRINTUSAGE,
StringUtils.lines(
ImmutableList.of(
"com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target",
@@ -187,7 +206,7 @@
runAndCheckOutput(
ImmutableList.of(Target.class),
ImmutableList.of(Source.class),
- TraceReferencesCommandParser.OutputFormat.KEEP_RULES,
+ TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES,
StringUtils.lines(
ImmutableList.of(
"-keep class com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target"
@@ -203,7 +222,7 @@
runAndCheckOutput(
ImmutableList.of(Target.class),
ImmutableList.of(Source.class),
- TraceReferencesCommandParser.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION,
+ TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION,
StringUtils.lines(
ImmutableList.of(
"-keep,allowobfuscation class"
@@ -219,7 +238,7 @@
runAndCheckOutput(
ImmutableList.of(OtherTarget.class),
ImmutableList.of(Source.class),
- TraceReferencesCommandParser.OutputFormat.PRINTUSAGE,
+ TraceReferencesFormattingConsumer.OutputFormat.PRINTUSAGE,
StringUtils.lines(ImmutableList.of()));
}
@@ -228,7 +247,7 @@
runAndCheckOutput(
ImmutableList.of(OtherTarget.class),
ImmutableList.of(Source.class),
- TraceReferencesCommandParser.OutputFormat.KEEP_RULES,
+ TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES,
StringUtils.lines(ImmutableList.of()));
}
@@ -237,7 +256,7 @@
runAndCheckOutput(
ImmutableList.of(OtherTarget.class),
ImmutableList.of(Source.class),
- TraceReferencesCommandParser.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION,
+ TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION,
StringUtils.lines(ImmutableList.of()));
}
@@ -264,7 +283,7 @@
runAndCheckOutput(
targetJar,
sourceJar,
- TraceReferencesCommandParser.OutputFormat.PRINTUSAGE,
+ TraceReferencesFormattingConsumer.OutputFormat.PRINTUSAGE,
StringUtils.lines(
ImmutableList.of(
"com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target",
@@ -273,7 +292,12 @@
+ ".target(int)",
"# Error: Could not find definition for field int"
+ " com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target"
- + ".field")));
+ + ".field")),
+ diagnosticsChecker -> {
+ assertEquals(0, diagnosticsChecker.errors.size());
+ assertEquals(2, diagnosticsChecker.warnings.size());
+ assertEquals(0, diagnosticsChecker.infos.size());
+ });
}
@Test
@@ -289,7 +313,7 @@
runAndCheckOutput(
targetJar,
sourceJar,
- TraceReferencesCommandParser.OutputFormat.KEEP_RULES,
+ TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES,
StringUtils.lines(
ImmutableList.of(
"-keep class com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target"
@@ -301,7 +325,12 @@
+ " com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target"
+ ".field",
"}",
- "-keeppackagenames com.android.tools.r8.tracereferences")));
+ "-keeppackagenames com.android.tools.r8.tracereferences")),
+ diagnosticsChecker -> {
+ assertEquals(0, diagnosticsChecker.errors.size());
+ assertEquals(2, diagnosticsChecker.warnings.size());
+ assertEquals(0, diagnosticsChecker.infos.size());
+ });
}
@Test
@@ -317,7 +346,7 @@
runAndCheckOutput(
targetJar,
sourceJar,
- TraceReferencesCommandParser.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION,
+ TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION,
StringUtils.lines(
ImmutableList.of(
"-keep,allowobfuscation class"
@@ -327,7 +356,12 @@
"# Error: Could not find definition for field int"
+ " com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target.field",
"}",
- "-keeppackagenames com.android.tools.r8.tracereferences")));
+ "-keeppackagenames com.android.tools.r8.tracereferences")),
+ diagnosticsChecker -> {
+ assertEquals(0, diagnosticsChecker.errors.size());
+ assertEquals(2, diagnosticsChecker.warnings.size());
+ assertEquals(0, diagnosticsChecker.infos.size());
+ });
}
private byte[] getClassWithTargetRemoved() throws IOException {