Merge "Special handling of referenced classes in Proguard compatibility mode"
diff --git a/src/main/java/com/android/tools/r8/code/ConstClass.java b/src/main/java/com/android/tools/r8/code/ConstClass.java
index ef8b257..f140022 100644
--- a/src/main/java/com/android/tools/r8/code/ConstClass.java
+++ b/src/main/java/com/android/tools/r8/code/ConstClass.java
@@ -39,7 +39,7 @@
@Override
public void registerUse(UseRegistry registry) {
- registry.registerConstClass(getType());
+ registry.registerTypeReference(getType());
}
public DexType getType() {
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
index f6736fa..78b2f56 100644
--- a/src/main/java/com/android/tools/r8/graph/UseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -65,8 +65,4 @@
throw new AssertionError();
}
}
-
- public boolean registerConstClass(DexType type) {
- return registerTypeReference(type);
- }
}
diff --git a/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java b/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
index 46bcbd5..3321cb7 100644
--- a/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
+++ b/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
@@ -51,7 +51,7 @@
// Nothing to register for method type, it represents only a prototype not associated with a
// method name.
if (((Type) cst).getSort() != Type.METHOD) {
- registry.registerConstClass(application.getType((Type) cst));
+ registry.registerTypeReference(application.getType((Type) cst));
}
} else if (cst instanceof Handle) {
registerMethodHandleType((Handle) cst);
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 41d06fc..215ea88 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -286,18 +286,7 @@
@Override
public boolean registerNewInstance(DexType type) {
- if (instantiatedTypes.contains(type)) {
- return false;
- }
- DexClass clazz = appInfo.definitionFor(type);
- if (clazz == null) {
- reportMissingClass(type);
- return false;
- }
- if (Log.ENABLED) {
- Log.verbose(getClass(), "Register new instatiation of `%s`.", clazz);
- }
- workList.add(Action.markInstantiated(clazz, KeepReason.instantiatedIn(currentMethod)));
+ markInstantiated(type, currentMethod);
return true;
}
@@ -335,30 +324,6 @@
}
return false;
}
-
- @Override
- public boolean registerConstClass(DexType type) {
- boolean result = registerTypeReference(type);
- // For Proguard compatibility mark default initializer for live type as live.
- if (options.forceProguardCompatibility) {
- if (type.isArrayType()) {
- return result;
- }
- assert type.isClassType();
- DexClass holder = appInfo.definitionFor(type);
- if (holder == null) {
- // Don't call reportMissingClass(type) here. That already happened in the call to
- // registerTypeReference(type).
- return result;
- }
- if (holder.hasDefaultInitializer()) {
- registerNewInstance(type);
- DexEncodedMethod init = holder.getDefaultInitializer();
- markDirectStaticOrConstructorMethodAsLive(init, KeepReason.reachableFromLiveType(type));
- }
- }
- return result;
- }
}
//
@@ -402,6 +367,9 @@
if (options.forceProguardCompatibility) {
if (holder.hasDefaultInitializer()) {
DexEncodedMethod init = holder.getDefaultInitializer();
+ // TODO(68246915): For now just register the default constructor as the source of
+ // instantiation.
+ markInstantiated(type, init);
markDirectStaticOrConstructorMethodAsLive(init, KeepReason.reachableFromLiveType(type));
}
}
@@ -618,6 +586,21 @@
enqueueRootItems(rootSet.getDependentItems(field));
}
+ private void markInstantiated(DexType type, DexEncodedMethod method) {
+ if (instantiatedTypes.contains(type)) {
+ return;
+ }
+ DexClass clazz = appInfo.definitionFor(type);
+ if (clazz == null) {
+ reportMissingClass(type);
+ return;
+ }
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Register new instatiation of `%s`.", clazz);
+ }
+ workList.add(Action.markInstantiated(clazz, KeepReason.instantiatedIn(method)));
+ }
+
private void markDirectStaticOrConstructorMethodAsLive(
DexEncodedMethod encodedMethod, KeepReason reason) {
assert encodedMethod != null;
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index ea51c65..2128663 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -26,11 +26,15 @@
import java.io.IOException;
import java.nio.file.Path;
import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
import org.junit.Rule;
import org.junit.rules.TemporaryFolder;
@@ -93,6 +97,25 @@
}
/**
+ * Read the names of classes in a jar.
+ */
+ protected Set<String> readClassesInJar(Path jar) throws IOException {
+ Set<String> result = new HashSet<>();
+ try (ZipFile zipFile = new ZipFile(jar.toFile())) {
+ final Enumeration<? extends ZipEntry> entries = zipFile.entries();
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = entries.nextElement();
+ String name = entry.getName();
+ if (name.endsWith(".class")) {
+ result.add(name.substring(0, name.length() - ".class".length()).replace('/', '.'));
+ }
+ }
+ }
+ return result;
+ }
+
+
+ /**
* Get the class name generated by javac.
*/
protected String getJavacGeneratedClassName(Class clazz) {
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 54750e5..2c4bc98 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -972,7 +972,7 @@
}
}
- public static void runProguard(Path inJar, Path outJar, Path config) throws IOException {
+ public static String runProguard(Path inJar, Path outJar, Path config) throws IOException {
List<String> command = new ArrayList<>();
command.add(PROGUARD);
command.add("-forceprocessing"); // Proguard just checks the creation time on the in/out jars.
@@ -989,6 +989,7 @@
if (result.exitCode != 0) {
fail("Proguard failed, exit code " + result.exitCode + ", stderr:\n" + result.stderr);
}
+ return result.stdout;
}
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
index dae1163..30d9111 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
@@ -20,6 +20,7 @@
import java.io.File;
import java.nio.file.Path;
import java.util.List;
+import java.util.Set;
import org.junit.Test;
public class ForceProguardCompatibilityTest extends TestBase {
@@ -125,4 +126,50 @@
runDefaultConstructorTest(false, TestClassWithDefaultConstructor.class, true);
runDefaultConstructorTest(false, TestClassWithoutDefaultConstructor.class, false);
}
+
+ public void testCheckCast(boolean forceProguardCompatibility, Class mainClass,
+ Class instantiatedClass, boolean containsCheckCast)
+ throws Exception {
+ R8Command.Builder builder = new CompatProguardCommandBuilder(forceProguardCompatibility, false);
+ builder.addProgramFiles(ToolHelper.getClassFileForTestClass(mainClass));
+ builder.addProgramFiles(ToolHelper.getClassFileForTestClass(instantiatedClass));
+ List<String> proguardConfig = ImmutableList.of(
+ "-keep class " + mainClass.getCanonicalName() + " {",
+ " public static void main(java.lang.String[]);",
+ "}",
+ "-dontobfuscate");
+ builder.addProguardConfiguration(proguardConfig);
+
+ DexInspector inspector = new DexInspector(ToolHelper.runR8(builder.build()));
+ assertTrue(inspector.clazz(getJavacGeneratedClassName(mainClass)).isPresent());
+ ClassSubject clazz = inspector.clazz(getJavacGeneratedClassName(instantiatedClass));
+ assertEquals(containsCheckCast, clazz.isPresent());
+ assertEquals(containsCheckCast, clazz.isPresent());
+ if (clazz.isPresent()) {
+ assertEquals(forceProguardCompatibility && containsCheckCast, !clazz.isAbstract());
+ }
+
+ if (RUN_PROGUARD) {
+ Path proguardedJar = File.createTempFile("proguarded", ".jar", temp.getRoot()).toPath();
+ Path proguardConfigFile = File.createTempFile("proguard", ".config", temp.getRoot()).toPath();
+ FileUtils.writeTextFile(proguardConfigFile, proguardConfig);
+ ToolHelper.runProguard(jarTestClasses(ImmutableList.of(mainClass, instantiatedClass)),
+ proguardedJar, proguardConfigFile);
+ Set<String> classesAfterProguard = readClassesInJar(proguardedJar);
+ assertTrue(classesAfterProguard.contains(mainClass.getCanonicalName()));
+ assertEquals(
+ containsCheckCast, classesAfterProguard.contains(instantiatedClass.getCanonicalName()));
+ }
+ }
+
+ @Test
+ public void checkCastTest() throws Exception {
+ testCheckCast(true, TestMainWithCheckCast.class, TestClassWithDefaultConstructor.class, true);
+ testCheckCast(
+ true, TestMainWithoutCheckCast.class, TestClassWithDefaultConstructor.class, false);
+ testCheckCast(
+ false, TestMainWithCheckCast.class, TestClassWithDefaultConstructor.class, true);
+ testCheckCast(
+ false, TestMainWithoutCheckCast.class, TestClassWithDefaultConstructor.class, false);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/TestMainWithCheckCast.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/TestMainWithCheckCast.java
new file mode 100644
index 0000000..d1eba28
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/TestMainWithCheckCast.java
@@ -0,0 +1,15 @@
+// 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.forceproguardcompatibility;
+
+public class TestMainWithCheckCast {
+
+ public static void main(String[] args) throws Exception {
+ String thisPackage = TestMainWithCheckCast.class.getPackage().getName();
+ Object o = (TestClassWithDefaultConstructor)
+ Class.forName(thisPackage + ".TestClassWithDefaultConstructor").newInstance();
+ System.out.println("Instance of: " + o.getClass().getCanonicalName());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/TestMainWithoutCheckCast.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/TestMainWithoutCheckCast.java
new file mode 100644
index 0000000..1ba2549
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/TestMainWithoutCheckCast.java
@@ -0,0 +1,14 @@
+// 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.forceproguardcompatibility;
+
+public class TestMainWithoutCheckCast {
+
+ public static void main(String[] args) throws Exception {
+ String thisPackage = TestMainWithoutCheckCast.class.getPackage().getName();
+ Object o = Class.forName(thisPackage + ".TestClassWithDefaultConstructor").newInstance();
+ System.out.println("Instantiated " + o.getClass().getCanonicalName());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
index 8bff0b3..308ce69 100644
--- a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
@@ -24,21 +24,6 @@
public class IncludeDescriptorClassesTest extends TestBase {
- private Set<String> readJarClasses(Path jar) throws IOException {
- Set<String> result = new HashSet<>();
- try (ZipFile zipFile = new ZipFile(jar.toFile())) {
- final Enumeration<? extends ZipEntry> entries = zipFile.entries();
- while (entries.hasMoreElements()) {
- ZipEntry entry = entries.nextElement();
- String name = entry.getName();
- if (name.endsWith(".class")) {
- result.add(name.substring(0, name.length() - ".class".length()).replace('/', '.'));
- }
- }
- }
- return result;
- }
-
private class Result {
final DexInspector inspector;
final Set<String> classesAfterProguard;
@@ -92,7 +77,7 @@
if (false) {
Path proguardedJar = temp.newFile("proguarded.jar").toPath();
ToolHelper.runProguard(jarTestClasses(classes), proguardedJar, proguardConfig);
- classesAfterProguard = readJarClasses(proguardedJar);
+ classesAfterProguard = readClassesInJar(proguardedJar);
}
return new Result(inspector, classesAfterProguard);