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;
+    }
+  }
+}