Add GenericSignatureEnqueuerAnalysis to visit generic types
This will basically just ensure that we can lookup definitions for
types only mentioned in generic signatures.
Bug: 129925954
Change-Id: I4afc63432969a5ec4b5944f9f6c319ad4dc4a7d0
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 34b73a8..0f43472 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -429,6 +429,8 @@
annotationRemover.ensureValid().run();
classesToRetainInnerClassAttributeFor =
annotationRemover.getClassesToRetainInnerClassAttributeFor();
+ new GenericSignatureRewriter(appView, NamingLens.getIdentityLens())
+ .run(appView.appInfo().classes(), executorService);
}
} finally {
timing.end();
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java
new file mode 100644
index 0000000..b01445d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java
@@ -0,0 +1,151 @@
+// 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.graph;
+
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.ReturnType;
+import com.android.tools.r8.graph.GenericSignature.TypeSignature;
+import com.android.tools.r8.graph.analysis.EnqueuerAnalysis;
+import com.android.tools.r8.shaking.EnqueuerWorklist;
+import java.util.List;
+
+public class GenericSignatureEnqueuerAnalysis extends EnqueuerAnalysis {
+
+ private final GenericSignatureVisitor visitor;
+
+ public GenericSignatureEnqueuerAnalysis(DexDefinitionSupplier definitionSupplier) {
+ visitor = new GenericSignatureTypeVisitor(definitionSupplier);
+ }
+
+ @Override
+ public void processNewlyLiveClass(DexProgramClass clazz, EnqueuerWorklist worklist) {
+ visitor.visitClassSignature(clazz.getClassSignature());
+ }
+
+ @Override
+ public void processNewlyLiveField(ProgramField field) {
+ visitor.visitFieldTypeSignature(field.getDefinition().getGenericSignature());
+ }
+
+ @Override
+ public void processNewlyLiveMethod(ProgramMethod method) {
+ visitor.visitMethodSignature(method.getDefinition().getGenericSignature());
+ }
+
+ private static class GenericSignatureTypeVisitor implements GenericSignatureVisitor {
+
+ private final DexDefinitionSupplier definitionSupplier;
+
+ private GenericSignatureTypeVisitor(DexDefinitionSupplier definitionSupplier) {
+ this.definitionSupplier = definitionSupplier;
+ }
+
+ @Override
+ public void visitClassSignature(ClassSignature classSignature) {
+ if (classSignature.hasNoSignature()) {
+ return;
+ }
+ classSignature.visit(this);
+ }
+
+ @Override
+ public void visitMethodSignature(MethodTypeSignature methodSignature) {
+ if (methodSignature.hasNoSignature()) {
+ return;
+ }
+ methodSignature.visit(this);
+ }
+
+ @Override
+ public void visitFieldTypeSignature(FieldTypeSignature fieldSignature) {
+ if (fieldSignature.hasNoSignature()) {
+ return;
+ }
+ if (fieldSignature.isStar()) {
+ return;
+ }
+ if (fieldSignature.isTypeVariableSignature()) {
+ return;
+ }
+ if (fieldSignature.isArrayTypeSignature()) {
+ fieldSignature.asArrayTypeSignature().visit(this);
+ return;
+ }
+ assert fieldSignature.isClassTypeSignature();
+ visitClassTypeSignature(fieldSignature.asClassTypeSignature());
+ }
+
+ private void visitClassTypeSignature(ClassTypeSignature classTypeSignature) {
+ definitionSupplier.definitionFor(classTypeSignature.type);
+ classTypeSignature.visit(this);
+ }
+
+ @Override
+ public void visitFormalTypeParameters(List<FormalTypeParameter> formalTypeParameters) {
+ formalTypeParameters.forEach(formalTypeParameter -> formalTypeParameter.visit(this));
+ }
+
+ @Override
+ public void visitClassBound(FieldTypeSignature fieldSignature) {
+ visitFieldTypeSignature(fieldSignature);
+ }
+
+ @Override
+ public void visitInterfaceBound(FieldTypeSignature fieldSignature) {
+ visitFieldTypeSignature(fieldSignature);
+ }
+
+ @Override
+ public void visitSuperClass(ClassTypeSignature classTypeSignature) {
+ visitClassTypeSignature(classTypeSignature);
+ }
+
+ @Override
+ public void visitSuperInterface(ClassTypeSignature classTypeSignature) {
+ visitClassTypeSignature(classTypeSignature);
+ }
+
+ @Override
+ public void visitTypeSignature(TypeSignature typeSignature) {
+ if (typeSignature.isBaseTypeSignature()) {
+ return;
+ }
+ assert typeSignature.isFieldTypeSignature();
+ visitFieldTypeSignature(typeSignature.asFieldTypeSignature());
+ }
+
+ @Override
+ public void visitSimpleClass(ClassTypeSignature classTypeSignature) {
+ visitClassTypeSignature(classTypeSignature);
+ }
+
+ @Override
+ public void visitReturnType(ReturnType returnType) {
+ if (returnType.isVoidDescriptor()) {
+ return;
+ }
+ visitTypeSignature(returnType.typeSignature);
+ }
+
+ @Override
+ public void visitMethodTypeSignatures(List<TypeSignature> typeSignatures) {
+ typeSignatures.forEach(this::visitTypeSignature);
+ }
+
+ @Override
+ public void visitThrowsSignatures(List<TypeSignature> typeSignatures) {
+ typeSignatures.forEach(this::visitTypeSignature);
+ }
+
+ @Override
+ public void visitTypeArguments(List<FieldTypeSignature> typeArguments) {
+ typeArguments.forEach(this::visitFieldTypeSignature);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
index 7804c5f..e9b45f1 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
@@ -38,21 +38,21 @@
}
public ClassSignature rewrite(ClassSignature classSignature) {
- if (classSignature.hasNoSignature() || appView.graphLens().isIdentityLens()) {
+ if (classSignature.hasNoSignature()) {
return classSignature;
}
return new ClassSignatureRewriter().run(classSignature);
}
public FieldTypeSignature rewrite(FieldTypeSignature fieldTypeSignature) {
- if (fieldTypeSignature.hasNoSignature() || appView.graphLens().isIdentityLens()) {
+ if (fieldTypeSignature.hasNoSignature()) {
return fieldTypeSignature;
}
return new TypeSignatureRewriter().run(fieldTypeSignature);
}
public MethodTypeSignature rewrite(MethodTypeSignature methodTypeSignature) {
- if (methodTypeSignature.hasNoSignature() || appView.graphLens().isIdentityLens()) {
+ if (methodTypeSignature.hasNoSignature()) {
return methodTypeSignature;
}
return new MethodTypeSignatureRewriter().run(methodTypeSignature);
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 629506c..a7fdc85 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -47,6 +47,7 @@
import com.android.tools.r8.graph.FieldAccessInfoCollectionImpl;
import com.android.tools.r8.graph.FieldAccessInfoImpl;
import com.android.tools.r8.graph.FieldResolutionResult;
+import com.android.tools.r8.graph.GenericSignatureEnqueuerAnalysis;
import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.LookupLambdaTarget;
@@ -2730,6 +2731,10 @@
new KotlinMetadataEnqueuerExtension(
appView, enqueuerDefinitionSupplier, initialPrunedTypes));
}
+ if (appView.options().getProguardConfiguration() != null
+ && appView.options().getProguardConfiguration().getKeepAttributes().signature) {
+ registerAnalysis(new GenericSignatureEnqueuerAnalysis(enqueuerDefinitionSupplier));
+ }
if (mode.isInitialTreeShaking()) {
// This is simulating the effect of the "root set" applied rules.
// This is done only in the initial pass, in subsequent passes the "rules" are reapplied
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/UnknownClassInSignatureTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/UnknownClassInSignatureTest.java
new file mode 100644
index 0000000..4c2ac5e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/UnknownClassInSignatureTest.java
@@ -0,0 +1,83 @@
+// 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.graph.genericsignature;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.transformers.ClassFileTransformer.FieldPredicate;
+import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class UnknownClassInSignatureTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final String NEW_CLASS_SIGNATURE = "<T:LUnknownClass1;>LUnknownClass2<LUnknownClass3;>;";
+ private final String NEW_FIELD_SIGNATURE = "LUnknownClass4<LUnknownClass4;>;";
+ private final String NEW_METHOD_SIGNATURE = "()LUnkownClass5<LunknownPackage/UnknownClass6;>;";
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public UnknownClassInSignatureTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(
+ transformer(Main.class)
+ .removeInnerClasses()
+ .setGenericSignature(NEW_CLASS_SIGNATURE)
+ .setGenericSignature(FieldPredicate.onName("field"), NEW_FIELD_SIGNATURE)
+ .setGenericSignature(MethodPredicate.onName("main"), NEW_METHOD_SIGNATURE)
+ .transform())
+ .addKeepAllClassesRule()
+ .addKeepAttributes(
+ ProguardKeepAttributes.SIGNATURE,
+ ProguardKeepAttributes.ENCLOSING_METHOD,
+ ProguardKeepAttributes.INNER_CLASSES)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello World!")
+ .inspect(
+ inspector -> {
+ ClassSubject clazz = inspector.clazz(Main.class);
+ assertThat(clazz, isPresent());
+ assertEquals(NEW_CLASS_SIGNATURE, clazz.getFinalSignatureAttribute());
+ FieldSubject field = clazz.uniqueFieldWithFinalName("field");
+ assertThat(field, isPresent());
+ assertEquals(NEW_FIELD_SIGNATURE, field.getFinalSignatureAttribute());
+ MethodSubject method = clazz.uniqueMethodWithFinalName("main");
+ assertThat(method, isPresent());
+ assertEquals(NEW_METHOD_SIGNATURE, method.getFinalSignatureAttribute());
+ });
+ }
+
+ public static class Main {
+
+ private List<Main> field;
+
+ public static void main(String[] args) {
+ System.out.println("Hello World!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index 86e4cb7..89ddee8 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -607,6 +607,20 @@
return renameField(FieldPredicate.onName(oldName), newName);
}
+ public ClassFileTransformer setGenericSignature(FieldPredicate predicate, String newSignature) {
+ return addClassTransformer(
+ new ClassTransformer() {
+ @Override
+ public FieldVisitor visitField(
+ int access, String name, String descriptor, String signature, Object value) {
+ if (predicate.test(access, name, descriptor, signature, value)) {
+ return super.visitField(access, name, descriptor, newSignature, value);
+ }
+ return super.visitField(access, name, descriptor, signature, value);
+ }
+ });
+ }
+
/** Abstraction of the MethodVisitor.visitMethodInsn method with its sub visitor. */
@FunctionalInterface
public interface MethodInsnTransform {