Model KmVersionRequirements in kotlin metadata

Bug: 157982139
Bug: 157977713
Change-Id: Idb01871157cab3152f06665756fc7f508c0ad179
diff --git a/src/main/java/com/android/tools/r8/kotlin/KmVisitorProviders.java b/src/main/java/com/android/tools/r8/kotlin/KmVisitorProviders.java
index 84d6e82..7431fb9 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KmVisitorProviders.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KmVisitorProviders.java
@@ -13,6 +13,7 @@
 import kotlinx.metadata.KmTypeVisitor;
 import kotlinx.metadata.KmValueParameterVisitor;
 import kotlinx.metadata.KmVariance;
+import kotlinx.metadata.KmVersionRequirementVisitor;
 
 /**
  * The reason for having these visitor providers is to make the separation of concern a bit easier
@@ -101,4 +102,10 @@
 
     KmTypeVisitor get(int flags, String typeFlexibilityId);
   }
+
+  @FunctionalInterface
+  public interface KmVersionRequirementVisitorProvider {
+
+    KmVersionRequirementVisitor get();
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
index b0b381e..9987457 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
@@ -45,6 +45,7 @@
   private final List<KotlinTypeReference> nestedClasses;
   // TODO(b/154347404): Understand enum entries.
   private final List<String> enumEntries;
+  private final KotlinVersionRequirementInfo versionRequirements;
   private final KotlinTypeReference anonymousObjectOrigin;
   private final String packageName;
 
@@ -59,6 +60,7 @@
       List<KotlinTypeReference> sealedSubClasses,
       List<KotlinTypeReference> nestedClasses,
       List<String> enumEntries,
+      KotlinVersionRequirementInfo versionRequirements,
       KotlinTypeReference anonymousObjectOrigin,
       String packageName) {
     this.flags = flags;
@@ -71,6 +73,7 @@
     this.sealedSubClasses = sealedSubClasses;
     this.nestedClasses = nestedClasses;
     this.enumEntries = enumEntries;
+    this.versionRequirements = versionRequirements;
     this.anonymousObjectOrigin = anonymousObjectOrigin;
     this.packageName = packageName;
   }
@@ -120,6 +123,7 @@
         getSealedSubClasses(kmClass.getSealedSubclasses(), factory),
         getNestedClasses(hostClass, kmClass.getNestedClasses(), factory),
         kmClass.getEnumEntries(),
+        KotlinVersionRequirementInfo.create(kmClass.getVersionRequirements()),
         getAnonymousObjectOrigin(kmClass, factory),
         packageName);
   }
@@ -261,7 +265,7 @@
     }
     // TODO(b/154347404): Understand enum entries.
     kmClass.getEnumEntries().addAll(enumEntries);
-
+    versionRequirements.rewrite(kmClass::visitVersionRequirement);
     JvmExtensionsKt.setModuleName(kmClass, moduleName);
     if (anonymousObjectOrigin != null) {
       String renamedAnon =
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
index 857ef39..c28b4a7 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
@@ -24,16 +24,20 @@
   // Information from original KmValueParameter(s) if available.
   private final int flags;
   // Information about the value parameters.
-  private final List<KotlinValueParameterInfo> valueParameterInfos;
+  private final List<KotlinValueParameterInfo> valueParameters;
+  // Information about version requirements.
+  private final KotlinVersionRequirementInfo versionRequirements;
   // Information about the signature.
   private final KotlinJvmMethodSignatureInfo signature;
 
   private KotlinConstructorInfo(
       int flags,
-      List<KotlinValueParameterInfo> valueParameterInfos,
+      List<KotlinValueParameterInfo> valueParameters,
+      KotlinVersionRequirementInfo versionRequirements,
       KotlinJvmMethodSignatureInfo signature) {
     this.flags = flags;
-    this.valueParameterInfos = valueParameterInfos;
+    this.valueParameters = valueParameters;
+    this.versionRequirements = versionRequirements;
     this.signature = signature;
   }
 
@@ -42,6 +46,7 @@
     return new KotlinConstructorInfo(
         kmConstructor.getFlags(),
         KotlinValueParameterInfo.create(kmConstructor.getValueParameters(), factory, reporter),
+        KotlinVersionRequirementInfo.create(kmConstructor.getVersionRequirements()),
         KotlinJvmMethodSignatureInfo.create(JvmExtensionsKt.getSignature(kmConstructor), factory));
   }
 
@@ -57,9 +62,10 @@
     if (signature != null) {
       JvmExtensionsKt.setSignature(kmConstructor, signature.rewrite(method, appView, namingLens));
     }
-    for (KotlinValueParameterInfo valueParameterInfo : valueParameterInfos) {
+    for (KotlinValueParameterInfo valueParameterInfo : valueParameters) {
       valueParameterInfo.rewrite(kmConstructor::visitValueParameter, appView, namingLens);
     }
+    versionRequirements.rewrite(kmConstructor::visitVersionRequirement);
     kmClass.getConstructors().add(kmConstructor);
   }
 
@@ -75,7 +81,7 @@
 
   @Override
   public void trace(DexDefinitionSupplier definitionSupplier) {
-    forEachApply(valueParameterInfos, param -> param::trace, definitionSupplier);
+    forEachApply(valueParameters, param -> param::trace, definitionSupplier);
     if (signature != null) {
       signature.trace(definitionSupplier);
     }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
index c5be533..d71d574 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
@@ -37,6 +37,8 @@
   private final KotlinJvmMethodSignatureInfo signature;
   // Information about the lambdaClassOrigin.
   private final KotlinTypeReference lambdaClassOrigin;
+  // Information about version requirements.
+  private final KotlinVersionRequirementInfo versionRequirements;
   // A value describing if any of the parameters are crossinline.
   private final boolean crossInlineParameter;
 
@@ -49,6 +51,7 @@
       List<KotlinTypeParameterInfo> typeParameters,
       KotlinJvmMethodSignatureInfo signature,
       KotlinTypeReference lambdaClassOrigin,
+      KotlinVersionRequirementInfo versionRequirements,
       boolean crossInlineParameter) {
     this.flags = flags;
     this.name = name;
@@ -58,6 +61,7 @@
     this.typeParameters = typeParameters;
     this.signature = signature;
     this.lambdaClassOrigin = lambdaClassOrigin;
+    this.versionRequirements = versionRequirements;
     this.crossInlineParameter = crossInlineParameter;
   }
 
@@ -85,6 +89,7 @@
         KotlinTypeParameterInfo.create(kmFunction.getTypeParameters(), factory, reporter),
         KotlinJvmMethodSignatureInfo.create(JvmExtensionsKt.getSignature(kmFunction), factory),
         getlambdaClassOrigin(kmFunction, factory),
+        KotlinVersionRequirementInfo.create(kmFunction.getVersionRequirements()),
         isCrossInline);
   }
 
@@ -123,6 +128,7 @@
     if (receiverParameterType != null) {
       receiverParameterType.rewrite(kmFunction::visitReceiverParameterType, appView, namingLens);
     }
+    versionRequirements.rewrite(kmFunction::visitVersionRequirement);
     JvmFunctionExtensionVisitor extensionVisitor =
         (JvmFunctionExtensionVisitor) kmFunction.visitExtensions(JvmFunctionExtensionVisitor.TYPE);
     if (signature != null && extensionVisitor != null) {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataWriter.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataWriter.java
index fa21f81..bc124c6 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataWriter.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataWriter.java
@@ -33,6 +33,7 @@
 import kotlinx.metadata.KmTypeParameter;
 import kotlinx.metadata.KmTypeProjection;
 import kotlinx.metadata.KmValueParameter;
+import kotlinx.metadata.KmVersionRequirement;
 import kotlinx.metadata.jvm.JvmExtensionsKt;
 import kotlinx.metadata.jvm.JvmFieldSignature;
 import kotlinx.metadata.jvm.JvmMethodSignature;
@@ -351,6 +352,7 @@
                 appendKmProperty(nextNextIndent, sb, kmProperty);
               });
         });
+    appendKmVersionRequirement(indent, sb, kmClass.getVersionRequirements());
     appendKeyValue(
         indent,
         "constructors",
@@ -389,6 +391,7 @@
           JvmMethodSignature signature = JvmExtensionsKt.getSignature(constructor);
           appendKeyValue(
               newIndent, "signature", sb, signature != null ? signature.asString() : "null");
+          appendKmVersionRequirement(newIndent, sb, constructor.getVersionRequirements());
         });
   }
 
@@ -420,6 +423,7 @@
               "valueParameters",
               sb,
               nextIndent -> appendValueParameters(nextIndent, sb, function.getValueParameters()));
+          appendKmVersionRequirement(newIndent, sb, function.getVersionRequirements());
           JvmMethodSignature signature = JvmExtensionsKt.getSignature(function);
           appendKeyValue(
               newIndent, "signature", sb, signature != null ? signature.asString() : "null");
@@ -461,6 +465,7 @@
               "setterParameter",
               sb,
               nextIndent -> appendValueParameter(nextIndent, sb, kmProperty.getSetterParameter()));
+          appendKmVersionRequirement(newIndent, sb, kmProperty.getVersionRequirements());
           appendKeyValue(newIndent, "jvmFlags", sb, JvmExtensionsKt.getJvmFlags(kmProperty) + "");
           JvmFieldSignature fieldSignature = JvmExtensionsKt.getFieldSignature(kmProperty);
           appendKeyValue(
@@ -730,6 +735,7 @@
               nextIndent -> {
                 appendKmType(nextIndent, sb, kmTypeAlias.underlyingType);
               });
+          appendKmVersionRequirement(newIndent, sb, kmTypeAlias.getVersionRequirements());
         });
   }
 
@@ -753,4 +759,44 @@
               });
         });
   }
+
+  private static void appendKmVersionRequirement(
+      String indent, StringBuilder sb, List<KmVersionRequirement> kmVersionRequirements) {
+    appendKeyValue(
+        indent,
+        "versionRequirements",
+        sb,
+        newIndent -> {
+          appendKmList(
+              newIndent,
+              "KmVersionRequirement",
+              sb,
+              kmVersionRequirements,
+              (nextIndent, kmVersionRequirement) -> {
+                appendKmSection(
+                    nextIndent,
+                    "KmVersionRequirement",
+                    sb,
+                    nextNextIndent -> {
+                      appendKeyValue(nextNextIndent, "kind", sb, kmVersionRequirement.kind.name());
+                      appendKeyValue(
+                          nextNextIndent, "level", sb, kmVersionRequirement.level.name());
+                      appendKeyValue(
+                          nextNextIndent,
+                          "errorCode",
+                          sb,
+                          kmVersionRequirement.getErrorCode() == null
+                              ? "null"
+                              : kmVersionRequirement.getErrorCode().toString());
+                      appendKeyValue(
+                          nextNextIndent, "message", sb, kmVersionRequirement.getMessage());
+                      appendKeyValue(
+                          nextNextIndent,
+                          "version",
+                          sb,
+                          kmVersionRequirement.getVersion().toString());
+                    });
+              });
+        });
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java
index 94afee8..4d14c67 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java
@@ -44,6 +44,8 @@
 
   private final List<KotlinTypeParameterInfo> typeParameters;
 
+  private final KotlinVersionRequirementInfo versionRequirements;
+
   private final int jvmFlags;
 
   private final KotlinJvmFieldSignatureInfo fieldSignature;
@@ -63,6 +65,7 @@
       KotlinTypeInfo receiverParameterType,
       KotlinValueParameterInfo setterParameter,
       List<KotlinTypeParameterInfo> typeParameters,
+      KotlinVersionRequirementInfo versionRequirements,
       int jvmFlags,
       KotlinJvmFieldSignatureInfo fieldSignature,
       KotlinJvmMethodSignatureInfo getterSignature,
@@ -76,6 +79,7 @@
     this.receiverParameterType = receiverParameterType;
     this.setterParameter = setterParameter;
     this.typeParameters = typeParameters;
+    this.versionRequirements = versionRequirements;
     this.jvmFlags = jvmFlags;
     this.fieldSignature = fieldSignature;
     this.getterSignature = getterSignature;
@@ -94,6 +98,7 @@
         KotlinTypeInfo.create(kmProperty.getReceiverParameterType(), factory, reporter),
         KotlinValueParameterInfo.create(kmProperty.getSetterParameter(), factory, reporter),
         KotlinTypeParameterInfo.create(kmProperty.getTypeParameters(), factory, reporter),
+        KotlinVersionRequirementInfo.create(kmProperty.getVersionRequirements()),
         JvmExtensionsKt.getJvmFlags(kmProperty),
         KotlinJvmFieldSignatureInfo.create(JvmExtensionsKt.getFieldSignature(kmProperty), factory),
         KotlinJvmMethodSignatureInfo.create(
@@ -146,6 +151,7 @@
     for (KotlinTypeParameterInfo typeParameter : typeParameters) {
       typeParameter.rewrite(kmProperty::visitTypeParameter, appView, namingLens);
     }
+    versionRequirements.rewrite(kmProperty::visitVersionRequirement);
     JvmPropertyExtensionVisitor extensionVisitor =
         (JvmPropertyExtensionVisitor) kmProperty.visitExtensions(JvmPropertyExtensionVisitor.TYPE);
     if (extensionVisitor != null) {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java
index 3872963..bb75b7f 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java
@@ -26,6 +26,7 @@
   private final KotlinTypeInfo expandedType;
   private final List<KotlinTypeParameterInfo> typeParameters;
   private final List<KotlinAnnotationInfo> annotations;
+  private final KotlinVersionRequirementInfo versionRequirements;
 
   private KotlinTypeAliasInfo(
       int flags,
@@ -33,7 +34,8 @@
       KotlinTypeInfo underlyingType,
       KotlinTypeInfo expandedType,
       List<KotlinTypeParameterInfo> typeParameters,
-      List<KotlinAnnotationInfo> annotations) {
+      List<KotlinAnnotationInfo> annotations,
+      KotlinVersionRequirementInfo versionRequirements) {
     this.flags = flags;
     this.name = name;
     assert underlyingType != null;
@@ -42,6 +44,7 @@
     this.expandedType = expandedType;
     this.typeParameters = typeParameters;
     this.annotations = annotations;
+    this.versionRequirements = versionRequirements;
   }
 
   public static KotlinTypeAliasInfo create(
@@ -52,7 +55,8 @@
         KotlinTypeInfo.create(alias.underlyingType, factory, reporter),
         KotlinTypeInfo.create(alias.expandedType, factory, reporter),
         KotlinTypeParameterInfo.create(alias.getTypeParameters(), factory, reporter),
-        KotlinAnnotationInfo.create(alias.getAnnotations(), factory));
+        KotlinAnnotationInfo.create(alias.getAnnotations(), factory),
+        KotlinVersionRequirementInfo.create(alias.getVersionRequirements()));
   }
 
   void rewrite(
@@ -68,6 +72,7 @@
     for (KotlinAnnotationInfo annotation : annotations) {
       annotation.rewrite(kmTypeAliasVisitor::visitAnnotation, appView, namingLens);
     }
+    versionRequirements.rewrite(kmTypeAliasVisitor::visitVersionRequirement);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinVersionRequirementInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinVersionRequirementInfo.java
new file mode 100644
index 0000000..9530f4c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinVersionRequirementInfo.java
@@ -0,0 +1,82 @@
+// Copyright (c) 2020, 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.kotlin;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import kotlinx.metadata.KmVersion;
+import kotlinx.metadata.KmVersionRequirement;
+import kotlinx.metadata.KmVersionRequirementLevel;
+import kotlinx.metadata.KmVersionRequirementVersionKind;
+import kotlinx.metadata.KmVersionRequirementVisitor;
+
+class KotlinVersionRequirementInfo {
+
+  private static final KotlinVersionRequirementInfo NO_VERSION_REQUIREMENTS =
+      new KotlinVersionRequirementInfo(ImmutableList.of());
+
+  private final List<KotlinVersionRequirementPoint> versionRequirements;
+
+  private KotlinVersionRequirementInfo(List<KotlinVersionRequirementPoint> versionRequirements) {
+    this.versionRequirements = versionRequirements;
+  }
+
+  static KotlinVersionRequirementInfo create(List<KmVersionRequirement> kmVersionRequirements) {
+    if (kmVersionRequirements.isEmpty()) {
+      return NO_VERSION_REQUIREMENTS;
+    }
+    ImmutableList.Builder<KotlinVersionRequirementPoint> builder = ImmutableList.builder();
+    for (KmVersionRequirement kmVersionRequirement : kmVersionRequirements) {
+      builder.add(KotlinVersionRequirementPoint.create(kmVersionRequirement));
+    }
+    return new KotlinVersionRequirementInfo(builder.build());
+  }
+
+  public void rewrite(KmVisitorProviders.KmVersionRequirementVisitorProvider visitorProvider) {
+    if (this == NO_VERSION_REQUIREMENTS) {
+      return;
+    }
+    for (KotlinVersionRequirementPoint versionRequirement : versionRequirements) {
+      versionRequirement.rewrite(visitorProvider.get());
+    }
+  }
+
+  private static class KotlinVersionRequirementPoint {
+
+    private final Integer errorCode;
+    private final KmVersionRequirementVersionKind kind;
+    private final KmVersionRequirementLevel level;
+    private final String message;
+    private final KmVersion version;
+
+    private KotlinVersionRequirementPoint(
+        KmVersionRequirementVersionKind kind,
+        KmVersionRequirementLevel level,
+        Integer errorCode,
+        String message,
+        KmVersion version) {
+      this.errorCode = errorCode;
+      this.kind = kind;
+      this.level = level;
+      this.message = message;
+      this.version = version;
+    }
+
+    private static KotlinVersionRequirementPoint create(KmVersionRequirement kmVersionRequirement) {
+      return new KotlinVersionRequirementPoint(
+          kmVersionRequirement.kind,
+          kmVersionRequirement.level,
+          kmVersionRequirement.getErrorCode(),
+          kmVersionRequirement.getMessage(),
+          kmVersionRequirement.version);
+    }
+
+    private void rewrite(KmVersionRequirementVisitor visitor) {
+      visitor.visit(kind, level, errorCode, message);
+      visitor.visitVersion(version.getMajor(), version.getMinor(), version.getPatch());
+      visitor.visitEnd();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java b/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
index 8202a9c..6df1462 100644
--- a/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
+++ b/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
@@ -83,9 +83,8 @@
             .compile()
             .inspect(inspector -> assertEqualMetadata(new CodeInspector(BASE_LIBRARY), inspector))
             .writeToZip();
-    compileTestSources(baseJar);
-    // TODO(b/157977713): We should be able to run tests.
-    // runTestsInJar(testJar, baseJar);
+    Path testJar = compileTestSources(baseJar);
+    runTestsInJar(testJar, baseJar);
   }
 
   private Path compileTestSources(Path baseJar) throws Exception {