ok
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java index d45a68c..2393956 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
@@ -273,22 +273,22 @@ appView.options().reporter.warning(new StringDiagnostic(sb.toString())); } - private void warnInvalidInvoke(DexType type, DexMethod invokedMethod, String debugString) { + public void reportInvalidInvoke(DexType type, DexMethod invokedMethod, String debugString) { DexType desugaredType = appView.rewritePrefix.rewrittenType(type); appView .options() .reporter - .warning( + .info( new StringDiagnostic( "Invoke to " + invokedMethod.holder + "#" + invokedMethod.name - + " may not work correctly at runtime (" + + " may not work correctly at runtime (Cannot convert " + debugString - + " type " + + "type " + desugaredType - + " is a desugared type).")); + + ").")); } public static DexType vivifiedTypeFor(DexType type, AppView<?> appView) { @@ -306,6 +306,7 @@ InstructionListIterator iterator, ListIterator<BasicBlock> blockIterator) { DexMethod invokedMethod = invokeMethod.getInvokedMethod(); + boolean invalidConversion = false; if (trackedAPIs != null) { trackedAPIs.add(invokedMethod); } @@ -320,12 +321,13 @@ // Return conversion added only if return value is used. if (invokeMethod.outValue() != null && invokeMethod.outValue().numberOfUsers() + invokeMethod.outValue().numberOfPhiUsers() - > 0) { + > 0) { returnConversion = createReturnConversionAndReplaceUses(code, invokeMethod, returnType, newReturnType); } } else { - warnInvalidInvoke(returnType, invokeMethod.getInvokedMethod(), "return"); + reportInvalidInvoke(returnType, invokeMethod.getInvokedMethod(), "return "); + invalidConversion = true; newReturnType = returnType; } } else { @@ -353,7 +355,8 @@ createParameterConversion(code, argType, argVivifiedType, inValue)); newInValues.add(parameterConversions.get(parameterConversions.size() - 1).outValue()); } else { - warnInvalidInvoke(argType, invokeMethod.getInvokedMethod(), "parameter"); + reportInvalidInvoke(argType, invokeMethod.getInvokedMethod(), "parameter "); + invalidConversion = true; newInValues.add(invokeMethod.inValues().get(i + receiverShift)); } } else { @@ -373,7 +376,8 @@ invokeMethod.outValue(), newInValues); assert newDexMethod - == methodWithVivifiedTypeInSignature(invokedMethod, invokedMethod.holder, appView); + == methodWithVivifiedTypeInSignature(invokedMethod, invokedMethod.holder, appView) + || invalidConversion; // Insert and reschedule all instructions. iterator.previous();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java index 99994fe..ed86a23 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
@@ -125,7 +125,7 @@ boolean canGenerateWrapper(DexType type) { DexClass dexClass = appView.definitionFor(type); - if (dexClass == null) { + if (dexClass == null || dexClass.accessFlags.isFinal()) { return false; } return dexClass.isLibraryClass() || appView.options().isDesugaredLibraryCompilation(); @@ -162,18 +162,9 @@ private DexClass getValidClassToWrap(DexType type) { DexClass dexClass = appView.definitionFor(type); // The dexClass should be a library class, so it cannot be null. - assert dexClass != null - && (dexClass.isLibraryClass() || appView.options().isDesugaredLibraryCompilation()); - if (dexClass.accessFlags.isFinal()) { - throw appView - .options() - .reporter - .fatalError( - new StringDiagnostic( - "Cannot generate a wrapper for final class " - + dexClass.type - + ". Add a custom conversion in the desugared library.")); - } + assert dexClass != null; + assert dexClass.isLibraryClass() || appView.options().isDesugaredLibraryCompilation(); + assert !dexClass.accessFlags.isFinal(); return dexClass; }
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java index 5597bb5..3cb579f 100644 --- a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java +++ b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
@@ -30,7 +30,6 @@ import com.android.tools.r8.ir.code.If; import com.android.tools.r8.ir.code.ValueType; import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter; -import com.android.tools.r8.utils.StringDiagnostic; import java.util.ArrayList; import java.util.List; import org.objectweb.asm.Opcodes; @@ -41,24 +40,14 @@ super(appView, holder); } - boolean shouldConvert( - DexType type, DesugaredLibraryAPIConverter converter, DexString methodName) { + boolean shouldConvert(DexType type, DesugaredLibraryAPIConverter converter, DexMethod method) { if (!appView.rewritePrefix.hasRewrittenType(type)) { return false; } if (converter.canConvert(type)) { return true; } - appView - .options() - .reporter - .warning( - new StringDiagnostic( - "Desugared library API conversion failed for " - + type - + ", unexpected behavior for method " - + methodName - + ".")); + converter.reportInvalidInvoke(type, method, ""); return false; } @@ -101,7 +90,7 @@ DexType[] newParameters = forwardMethod.proto.parameters.values.clone(); for (DexType param : forwardMethod.proto.parameters.values) { instructions.add(new CfLoad(ValueType.fromDexType(param), stackIndex)); - if (shouldConvert(param, converter, forwardMethod.name)) { + if (shouldConvert(param, converter, forwardMethod)) { instructions.add( new CfInvoke( Opcodes.INVOKESTATIC, @@ -132,7 +121,7 @@ instructions.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, newForwardMethod, false)); } - if (shouldConvert(returnType, converter, forwardMethod.name)) { + if (shouldConvert(returnType, converter, forwardMethod)) { instructions.add( new CfInvoke( Opcodes.INVOKESTATIC, @@ -188,7 +177,7 @@ int stackIndex = 1; for (DexType param : forwardMethod.proto.parameters.values) { instructions.add(new CfLoad(ValueType.fromDexType(param), stackIndex)); - if (shouldConvert(param, converter, forwardMethod.name)) { + if (shouldConvert(param, converter, forwardMethod)) { instructions.add( new CfInvoke( Opcodes.INVOKESTATIC, @@ -208,7 +197,7 @@ } DexType returnType = forwardMethod.proto.returnType; - if (shouldConvert(returnType, converter, forwardMethod.name)) { + if (shouldConvert(returnType, converter, forwardMethod)) { instructions.add( new CfInvoke( Opcodes.INVOKESTATIC,
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassErrorTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassErrorTest.java deleted file mode 100644 index 403ae3c..0000000 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassErrorTest.java +++ /dev/null
@@ -1,57 +0,0 @@ -// 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.desugaredlibrary.conversiontests; - -import static junit.framework.TestCase.assertEquals; -import static junit.framework.TestCase.fail; - -import com.android.tools.r8.CompilationFailedException; -import com.android.tools.r8.TestDiagnosticMessages; -import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase; -import com.android.tools.r8.utils.AndroidApiLevel; -import java.time.Year; -import org.junit.Test; - -public class APIConversionFinalClassErrorTest extends DesugaredLibraryTestBase { - - @Test - public void testFinalMethod() { - try { - testForD8() - .setMinApi(AndroidApiLevel.B) - .addProgramClasses(Executor.class) - .addLibraryClasses(CustomLibClass.class) - .enableCoreLibraryDesugaring(AndroidApiLevel.B) - .compileWithExpectedDiagnostics(this::assertDiagnosis); - fail("Expected compilation error"); - } catch (CompilationFailedException ignored) { - - } - } - - private void assertDiagnosis(TestDiagnosticMessages d) { - assertEquals( - "Cannot generate a wrapper for final class java.time.Year." - + " Add a custom conversion in the desugared library.", - d.getErrors().get(0).getDiagnosticMessage()); - } - - static class Executor { - - public static void main(String[] args) { - System.out.println(CustomLibClass.call(Year.now())); - } - } - - // This class will be put at compilation time as library and on the runtime class path. - // This class is convenient for easy testing. Each method plays the role of methods in the - // platform APIs for which argument/return values need conversion. - static class CustomLibClass { - - public static long call(Year year) { - return 0L; - } - } -}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassTest.java new file mode 100644 index 0000000..0c13b40 --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassTest.java
@@ -0,0 +1,89 @@ +// 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.desugaredlibrary.conversiontests; + +import static org.hamcrest.core.StringContains.containsString; + +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.BooleanUtils; +import java.nio.file.Path; +import java.time.Year; +import java.util.List; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class APIConversionFinalClassTest extends DesugaredLibraryTestBase { + + private final TestParameters parameters; + private final boolean shrinkDesugaredLibrary; + + private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.O; + + private static Path customLib; + + @Parameterized.Parameters(name = "{0}, shrinkDesugaredLibrary: {1}") + public static List<Object[]> data() { + return buildParameters(getTestParameters().withDexRuntimesStartingFromIncluding(ToolHelper.DexVm.Version.V7_0_0).withApiLevelsEndingAtIncluding(AndroidApiLevel.M).build(), BooleanUtils.values()); + } + + public APIConversionFinalClassTest(TestParameters parameters, boolean shrinkDesugaredLibrary) { + this.shrinkDesugaredLibrary = shrinkDesugaredLibrary; + this.parameters = parameters; + } + + @BeforeClass + public static void compileCustomLib() throws Exception { + customLib = + testForD8(getStaticTemp()) + .addProgramClasses(CustomLibClass.class) + .setMinApi(MIN_SUPPORTED) + .compile() + .writeToZip(); + } + + @Test + public void testFinalMethod() throws Exception { + KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters); + testForD8() + .setMinApi(AndroidApiLevel.B) + .addProgramClasses(Executor.class) + .addLibraryClasses(CustomLibClass.class) + .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer) + .compile() + .addDesugaredCoreLibraryRunClassPath( + this::buildDesugaredLibrary, + parameters.getApiLevel(), + keepRuleConsumer.get(), + shrinkDesugaredLibrary) + .addRunClasspathFiles(customLib) + .run(parameters.getRuntime(), Executor.class) + .assertFailureWithErrorThatMatches(containsString("NoSuchMethodError")); + } + + static class Executor { + + public static void main(String[] args) { + System.out.println(CustomLibClass.libCall(Year.now())); + } + } + + // This class will be put at compilation time as library and on the runtime class path. + // This class is convenient for easy testing. Each method plays the role of methods in the + // platform APIs for which argument/return values need conversion. + static class CustomLibClass { + + // We use Year because Year is a final class with no custom conversion but Year has been + // unused in the Android library so far. + public static long libCall(Year year) { + return 0L; + } + } +}