Double Utility class bug
When utility class is used both from
desugared library and program on some
Dalvik VMs the bytecode verifier fails.
Bug: 134732760
Change-Id: I468c4ef7b54706bb4a31ffc733afc8d9e508d141
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index ff771b7..6a5030e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -107,7 +107,7 @@
continue;
}
- DexMethod newMethod = provider.provideMethod(factory);
+ DexMethod newMethod = provider.provideMethod(appView);
iterator.replaceCurrentInstruction(
new InvokeStatic(newMethod, invoke.outValue(), invoke.inValues()));
@@ -162,7 +162,7 @@
MethodProvider provider = methodProviders.get(key);
methodProviders.remove(key);
assert provider.requiresGenerationOfCode();
- DexMethod method = provider.provideMethod(factory);
+ DexMethod method = provider.provideMethod(appView);
// The utility class could have been synthesized, e.g., running R8 then D8,
// or if already processed in this while loop.
if (appView.definitionFor(method.holder) != null) {
@@ -914,7 +914,7 @@
this.proto = proto;
}
- public abstract DexMethod provideMethod(DexItemFactory factory);
+ public abstract DexMethod provideMethod(AppView<?> appView);
public abstract TemplateMethodCode generateTemplateMethod(
InternalOptions options, DexMethod method);
@@ -936,10 +936,11 @@
}
@Override
- public DexMethod provideMethod(DexItemFactory factory) {
+ public DexMethod provideMethod(AppView<?> appView) {
if (dexMethod != null) {
return dexMethod;
}
+ DexItemFactory factory = appView.dexItemFactory();
DexProto newProto =
isStatic ? proto : factory.prependTypeToProto(factory.createType(clazz), proto);
return dexMethod = factory.createMethod(newHolder, newProto, method);
@@ -978,14 +979,23 @@
}
@Override
- public DexMethod provideMethod(DexItemFactory factory) {
+ public DexMethod provideMethod(AppView<?> appView) {
if (dexMethod != null) {
return dexMethod;
}
+ DexItemFactory factory = appView.dexItemFactory();
String unqualifiedName =
DescriptorUtils.getUnqualifiedClassNameFromDescriptor(clazz.toString());
+ // Avoid duplicate class names between core lib dex file and program dex files.
+ String coreLibUtilitySuffix = appView.options().coreLibraryCompilation ? "$corelib" : "";
String descriptor =
- UTILITY_CLASS_DESCRIPTOR_PREFIX + '$' + unqualifiedName + '$' + methodName + ';';
+ UTILITY_CLASS_DESCRIPTOR_PREFIX
+ + '$'
+ + unqualifiedName
+ + coreLibUtilitySuffix
+ + '$'
+ + methodName
+ + ';';
DexType clazz = factory.createType(descriptor);
dexMethod = factory.createMethod(clazz, proto, method);
return dexMethod;
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/DoubleUtilityClassTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/DoubleUtilityClassTest.java
new file mode 100644
index 0000000..d4941ec
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/DoubleUtilityClassTest.java
@@ -0,0 +1,98 @@
+// Copyright (c) 2019, 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.desugar.corelib;
+
+import static org.hamcrest.core.IsNot.not;
+import static org.hamcrest.core.StringContains.containsString;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.time.zone.ZoneOffsetTransition;
+import java.util.Objects;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+// In this test both the desugared library and the program have the same utility class.
+@RunWith(Parameterized.class)
+public class DoubleUtilityClassTest extends CoreLibDesugarTestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public DoubleUtilityClassTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testDoubleUtility() throws Exception {
+ for (Class<?> executor : new Class<?>[] {ExecutorV1.class, ExecutorV2.class}) {
+ testForD8()
+ .addProgramClasses(executor)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel())
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary, parameters.getApiLevel())
+ .run(parameters.getRuntime(), executor)
+ .assertSuccess()
+ // Verification error on some Dalvik VMs (4,api 1;4,api 15;4.4,api 1).
+ .assertStderrMatches(
+ not(
+ containsString(
+ "(Lcom/android/tools/r8/desugar/corelib/DoubleUtilityClassTest$Executor; had"
+ + " used a different"
+ + " L$r8$backportedMethods$utility$Objects$requireNonNullMessage; during"
+ + " pre-verification) (dalvikvm)")));
+ }
+ }
+
+ // ExecutorV1 and V2 look the same, *but* only one of them triggers the verification
+ // error on Dalvik VMs.
+ public static class ExecutorV1 {
+
+ public static void main(String[] args) {
+ programCalls();
+ }
+
+ public static void programCalls() {
+ System.out.println(Objects.requireNonNull("string", "transition"));
+ System.out.println(Objects.hashCode("foo"));
+ System.out.println(Long.hashCode(1L));
+ System.out.println(Long.compareUnsigned(1L, 2L));
+ System.out.println(Integer.hashCode(1));
+ System.out.println(Double.hashCode(1.0));
+ }
+ }
+
+ public static class ExecutorV2 {
+
+ public static void main(String[] args) {
+ programCalls();
+ libraryCalls();
+ }
+
+ public static void libraryCalls() {
+ System.out.println(
+ ZoneOffsetTransition.of(
+ LocalDateTime.of(2008, 3, 9, 2, 0, 0, 0), ZoneOffset.UTC, ZoneOffset.MAX));
+ }
+
+ public static void programCalls() {
+ System.out.println(Objects.requireNonNull("string", "transition"));
+ System.out.println(Objects.hashCode("foo"));
+ System.out.println(Long.hashCode(1L));
+ System.out.println(Long.compareUnsigned(1L, 2L));
+ System.out.println(Integer.hashCode(1));
+ System.out.println(Double.hashCode(1.0));
+ }
+ }
+}