Desugared lib API Clock support
Change-Id: I8312c960a4c8dd7afd7f5c0ccd05ec9fa43494f7
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 fe410fa..dbdc087 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
@@ -20,7 +20,6 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.FieldAccessFlags;
@@ -125,8 +124,7 @@
if (dexClass == null) {
return false;
}
- // TODO(b/134732760): Support Abstract class for clock, maybe concrete class for Optional.
- return dexClass.isLibraryClass() && dexClass.isInterface();
+ return dexClass.isLibraryClass();
}
public DexType getTypeWrapper(DexType type) {
@@ -179,7 +177,7 @@
DexEncodedField wrapperField = synthetizeWrappedValueField(typeWrapperType, type);
return synthesizeWrapper(
converter.vivifiedTypeFor(type),
- dexClass.sourceFile,
+ dexClass,
synthesizeVirtualMethodsForTypeWrapper(dexClass.asLibraryClass(), wrapperField),
wrapperField);
}
@@ -191,26 +189,29 @@
synthetizeWrappedValueField(vivifiedTypeWrapperType, converter.vivifiedTypeFor(type));
return synthesizeWrapper(
type,
- dexClass.sourceFile,
+ dexClass,
synthesizeVirtualMethodsForVivifiedTypeWrapper(dexClass.asLibraryClass(), wrapperField),
wrapperField);
}
private DexProgramClass synthesizeWrapper(
DexType wrappingType,
- DexString sourceFile,
+ DexClass clazz,
DexEncodedMethod[] virtualMethods,
DexEncodedField wrapperField) {
- // TODO(b/134732760): support abstract class in addition to interfaces.
+ boolean isItf = clazz.isInterface();
+ DexType superType = isItf ? factory.objectType : wrappingType;
+ DexTypeList interfaces =
+ isItf ? new DexTypeList(new DexType[] {wrappingType}) : DexTypeList.empty();
return new DexProgramClass(
wrapperField.field.holder,
null,
new SynthesizedOrigin("Desugared library API Converter", getClass()),
ClassAccessFlags.fromSharedAccessFlags(
Constants.ACC_FINAL | Constants.ACC_SYNTHETIC | Constants.ACC_PUBLIC),
- factory.objectType,
- new DexTypeList(new DexType[] {wrappingType}),
- sourceFile,
+ superType,
+ interfaces,
+ clazz.sourceFile,
null,
Collections.emptyList(),
null,
@@ -240,6 +241,8 @@
// v3 <- wrappedValue.foo(v2,v1);
// return v3;
for (DexEncodedMethod dexEncodedMethod : dexMethods) {
+ DexClass holderClass = appView.definitionFor(dexEncodedMethod.method.holder);
+ assert holderClass != null;
DexMethod methodToInstall =
factory.createMethod(
wrapperField.field.holder,
@@ -247,7 +250,11 @@
dexEncodedMethod.method.name);
CfCode cfCode =
new APIConverterVivifiedWrapperCfCodeProvider(
- appView, methodToInstall, wrapperField.field, converter)
+ appView,
+ methodToInstall,
+ wrapperField.field,
+ converter,
+ holderClass.isInterface())
.generateCfCode();
DexEncodedMethod newDexEncodedMethod =
newSynthesizedMethod(methodToInstall, dexEncodedMethod, cfCode);
@@ -271,12 +278,17 @@
// v3 <- wrappedValue.foo(v2,v1);
// return v3;
for (DexEncodedMethod dexEncodedMethod : dexMethods) {
-
+ DexClass holderClass = appView.definitionFor(dexEncodedMethod.method.holder);
+ assert holderClass != null;
DexMethod methodToInstall =
methodWithVivifiedTypeInSignature(dexEncodedMethod.method, wrapperField.field.holder);
CfCode cfCode =
new APIConverterWrapperCfCodeProvider(
- appView, dexEncodedMethod.method, wrapperField.field, converter)
+ appView,
+ dexEncodedMethod.method,
+ wrapperField.field,
+ converter,
+ holderClass.isInterface())
.generateCfCode();
DexEncodedMethod newDexEncodedMethod =
newSynthesizedMethod(methodToInstall, dexEncodedMethod, cfCode);
@@ -318,8 +330,6 @@
}
private List<DexEncodedMethod> allImplementedMethods(DexLibraryClass libraryClass) {
- // TODO(b/134732760): Deal with Abstract class for clock and class for Optional.
- assert libraryClass.isInterface();
LinkedList<DexClass> workList = new LinkedList<>();
List<DexEncodedMethod> implementedMethods = new ArrayList<>();
workList.add(libraryClass);
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 c98f1fa..d61ee3e 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
@@ -40,7 +40,10 @@
DesugaredLibraryAPIConverter converter,
AppView<?> appView,
DexString methodName) {
- if (appView.rewritePrefix.hasRewrittenType(type) && converter.canConvert(type)) {
+ if (!appView.rewritePrefix.hasRewrittenType(type)) {
+ return false;
+ }
+ if (converter.canConvert(type)) {
return true;
}
appView
@@ -61,16 +64,19 @@
DexField wrapperField;
DexMethod forwardMethod;
DesugaredLibraryAPIConverter converter;
+ boolean itfCall;
public APIConverterVivifiedWrapperCfCodeProvider(
AppView<?> appView,
DexMethod forwardMethod,
DexField wrapperField,
- DesugaredLibraryAPIConverter converter) {
+ DesugaredLibraryAPIConverter converter,
+ boolean itfCall) {
super(appView, wrapperField.holder);
this.forwardMethod = forwardMethod;
this.wrapperField = wrapperField;
this.converter = converter;
+ this.itfCall = itfCall;
}
@Override
@@ -111,8 +117,12 @@
DexProto newProto = factory.createProto(forwardMethodReturnType, newParameters);
DexMethod newForwardMethod =
factory.createMethod(wrapperField.type, newProto, forwardMethod.name);
- // TODO(b/134732760): Deal with abstract class instead of interfaces.
- instructions.add(new CfInvoke(Opcodes.INVOKEINTERFACE, newForwardMethod, true));
+
+ if (itfCall) {
+ instructions.add(new CfInvoke(Opcodes.INVOKEINTERFACE, newForwardMethod, true));
+ } else {
+ instructions.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, newForwardMethod, false));
+ }
if (shouldConvert(returnType, converter, appView, forwardMethod.name)) {
instructions.add(
@@ -136,16 +146,19 @@
DexField wrapperField;
DexMethod forwardMethod;
DesugaredLibraryAPIConverter converter;
+ boolean itfCall;
public APIConverterWrapperCfCodeProvider(
AppView<?> appView,
DexMethod forwardMethod,
DexField wrapperField,
- DesugaredLibraryAPIConverter converter) {
+ DesugaredLibraryAPIConverter converter,
+ boolean itfCall) {
super(appView, wrapperField.holder);
this.forwardMethod = forwardMethod;
this.wrapperField = wrapperField;
this.converter = converter;
+ this.itfCall = itfCall;
}
@Override
@@ -173,8 +186,11 @@
stackIndex++;
}
- // TODO(b/134732760): Deal with abstract class instead of interfaces.
- instructions.add(new CfInvoke(Opcodes.INVOKEINTERFACE, forwardMethod, true));
+ if (itfCall) {
+ instructions.add(new CfInvoke(Opcodes.INVOKEINTERFACE, forwardMethod, true));
+ } else {
+ instructions.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, forwardMethod, false));
+ }
DexType returnType = forwardMethod.proto.returnType;
if (shouldConvert(returnType, converter, appView, forwardMethod.name)) {
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/ClockAPIConversionTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/ClockAPIConversionTest.java
new file mode 100644
index 0000000..4f52664
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/ClockAPIConversionTest.java
@@ -0,0 +1,60 @@
+// 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.conversionTests;
+
+import com.android.tools.r8.TestRuntime.DexRuntime;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
+import java.time.Clock;
+import org.junit.Test;
+
+public class ClockAPIConversionTest extends APIConversionTestBase {
+
+ @Test
+ public void testClock() throws Exception {
+ Path customLib = testForD8().addProgramClasses(CustomLibClass.class).compile().writeToZip();
+ testForD8()
+ .setMinApi(AndroidApiLevel.B)
+ .addProgramClasses(Executor.class)
+ .addLibraryClasses(CustomLibClass.class)
+ .enableCoreLibraryDesugaring(AndroidApiLevel.B)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+ .addRunClasspathFiles(customLib)
+ .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
+ .assertSuccessWithOutput(StringUtils.lines("Z", "Z", "true"));
+ }
+
+ static class Executor {
+
+ @SuppressWarnings("ConstantConditions")
+ public static void main(String[] args) {
+ Clock clock1 = CustomLibClass.getClock();
+ Clock localClock = Clock.systemUTC();
+ Clock clock2 = CustomLibClass.mixClocks(localClock, Clock.systemUTC());
+ System.out.println(clock1.getZone());
+ System.out.println(clock2.getZone());
+ System.out.println(localClock == clock2);
+ }
+ }
+
+ // 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 {
+ @SuppressWarnings("all")
+ public static Clock getClock() {
+ return Clock.systemUTC();
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ public static Clock mixClocks(Clock clock1, Clock clock2) {
+ return clock1;
+ }
+ }
+}