Propagate flags and types in Kotlin value parameter.

KmValueParameter contains parameter info, such as name, flags, type,
and vararg element type if the corresponding parameter is vararg.
Similar to member kinds and flags, we need to extract them from Kotlin
metadata; maintain; and use them when synthesizing Function metadata.

This CL also introduces KmValueParameterSubject to inspect the resulting
Kotlin lib has expected flag, such as DECLARES_DEFAULT_VALUE, and vararg
type for specific value parameter.

Bug: 70169921, 143687784
Change-Id: I654b6f7df30a9292c4be87765513b4ff41980883
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java
index 7a1a343..56d9359 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.kotlin;
 
 import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmFunction;
-import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmFunctionAsExtension;
 
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
@@ -119,21 +118,14 @@
         continue;
       }
 
-      if (method.isKotlinExtensionFunction()) {
-        KmFunction extension = toRenamedKmFunctionAsExtension(method, appView, lens);
-        if (extension != null) {
-          functions.add(extension);
-        }
-        continue;
-      }
-      if (method.isKotlinFunction()) {
+      if (method.isKotlinFunction() || method.isKotlinExtensionFunction()) {
         KmFunction function = toRenamedKmFunction(method, appView, lens);
         if (function != null) {
           functions.add(function);
         }
         continue;
       }
-      if (method.isKotlinProperty()) {
+      if (method.isKotlinProperty() || method.isKotlinExtensionProperty()) {
         String name = method.getKotlinMemberInfo().propertyName;
         assert name != null;
         KmPropertyGroup.Builder builder =
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java
index 62b5355..50058ec 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java
@@ -14,34 +14,84 @@
 import com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.KmFunctionProcessor;
 import com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.KmPropertyProcessor;
 import com.android.tools.r8.utils.Reporter;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import kotlinx.metadata.KmDeclarationContainer;
 import kotlinx.metadata.KmFunction;
 import kotlinx.metadata.KmProperty;
+import kotlinx.metadata.KmValueParameter;
 
 // Provides access to field/method-level Kotlin information.
 public class KotlinMemberInfo {
-  private static KotlinMemberInfo NO_KOTLIN_MEMBER_INFO = new KotlinMemberInfo(MemberKind.NONE, 0);
+  private static final List<KmValueParameter> EMPTY_PARAM = ImmutableList.of();
+  private static final List<KotlinValueParameterInfo> EMPTY_PARAM_INFO = ImmutableList.of();
+
+  private static final KotlinMemberInfo NO_KOTLIN_MEMBER_INFO =
+      new KotlinMemberInfo(MemberKind.NONE, 0, EMPTY_PARAM);
 
   public static KotlinMemberInfo getNoKotlinMemberInfo() {
     return NO_KOTLIN_MEMBER_INFO;
   }
 
   public final MemberKind memberKind;
-  // Original flag. May be necessary to keep Kotlin-specific flag, e.g., inline function.
+  // Original member flag. May be necessary to keep Kotlin-specific flag, e.g., suspend function.
   final int flag;
   // Original property name for (extension) property. Otherwise, null.
   final String propertyName;
+  // Information from original KmValueParameter(s) if available. Otherwise, null.
+  private final List<KotlinValueParameterInfo> valueParameterInfos;
 
-  private KotlinMemberInfo(MemberKind memberKind, int flag) {
-    this(memberKind, flag, null);
+  // Constructor for KmFunction
+  private KotlinMemberInfo(
+      MemberKind memberKind, int flag, List<KmValueParameter> kmValueParameters) {
+    this(memberKind, flag, null, kmValueParameters);
   }
 
+  // Constructor for a backing field and a getter in KmProperty
   private KotlinMemberInfo(MemberKind memberKind, int flag, String propertyName) {
+    this(memberKind, flag, propertyName, EMPTY_PARAM);
+  }
+
+  // Constructor for a setter in KmProperty
+  private KotlinMemberInfo(
+      MemberKind memberKind,
+      int flag,
+      String propertyName,
+      KmValueParameter kmValueParameter) {
+    this(memberKind, flag, propertyName,
+        kmValueParameter != null ? ImmutableList.of(kmValueParameter) : EMPTY_PARAM);
+  }
+
+  private KotlinMemberInfo(
+      MemberKind memberKind,
+      int flag,
+      String propertyName,
+      List<KmValueParameter> kmValueParameters) {
     this.memberKind = memberKind;
     this.flag = flag;
     this.propertyName = propertyName;
+    assert kmValueParameters != null;
+    if (kmValueParameters.isEmpty()) {
+      this.valueParameterInfos = EMPTY_PARAM_INFO;
+    } else {
+      this.valueParameterInfos = new ArrayList<>(kmValueParameters.size());
+      for (KmValueParameter kmValueParameter : kmValueParameters) {
+        valueParameterInfos.add(KotlinValueParameterInfo.fromKmValueParameter(kmValueParameter));
+      }
+    }
+  }
+
+  KotlinValueParameterInfo getValueParameterInfo(int i) {
+    if (valueParameterInfos.isEmpty()) {
+      return null;
+    }
+    if (i < 0 || i >= valueParameterInfos.size()) {
+      return null;
+    }
+    return valueParameterInfos.get(i);
   }
 
   public enum MemberKind {
@@ -151,10 +201,16 @@
         KmFunction kmFunction = kmFunctionMap.get(key);
         if (isExtension(kmFunction)) {
           method.setKotlinMemberInfo(
-              new KotlinMemberInfo(MemberKind.EXTENSION_FUNCTION, kmFunction.getFlags()));
+              new KotlinMemberInfo(
+                  MemberKind.EXTENSION_FUNCTION,
+                  kmFunction.getFlags(),
+                  kmFunction.getValueParameters()));
         } else {
           method.setKotlinMemberInfo(
-              new KotlinMemberInfo(MemberKind.FUNCTION, kmFunction.getFlags()));
+              new KotlinMemberInfo(
+                  MemberKind.FUNCTION,
+                  kmFunction.getFlags(),
+                  kmFunction.getValueParameters()));
         }
       } else if (kmPropertyGetterMap.containsKey(key)) {
         KmProperty kmProperty = kmPropertyGetterMap.get(key);
@@ -176,11 +232,15 @@
               new KotlinMemberInfo(
                   MemberKind.EXTENSION_PROPERTY_SETTER,
                   kmProperty.getFlags(),
-                  kmProperty.getName()));
+                  kmProperty.getName(),
+                  kmProperty.getSetterParameter()));
         } else {
           method.setKotlinMemberInfo(
               new KotlinMemberInfo(
-                  MemberKind.PROPERTY_SETTER, kmProperty.getFlags(), kmProperty.getName()));
+                  MemberKind.PROPERTY_SETTER,
+                  kmProperty.getFlags(),
+                  kmProperty.getName(),
+                  kmProperty.getSetterParameter()));
         }
       }
     }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
index 94c65e3..4935c24 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
@@ -97,7 +97,7 @@
     KmConstructor kmConstructor = new KmConstructor(method.accessFlags.getAsKotlinFlags());
     JvmExtensionsKt.setSignature(kmConstructor, toJvmMethodSignature(method.method));
     List<KmValueParameter> parameters = kmConstructor.getValueParameters();
-    if (!populateKmValueParameters(parameters, method, appView, lens, false)) {
+    if (!populateKmValueParameters(parameters, method, appView, lens)) {
       return null;
     }
     return kmConstructor;
@@ -107,21 +107,6 @@
       DexEncodedMethod method,
       AppView<AppInfoWithLiveness> appView,
       NamingLens lens) {
-    return toRenamedKmFunctionHelper(method, appView, lens, false);
-  }
-
-  static KmFunction toRenamedKmFunctionAsExtension(
-      DexEncodedMethod method,
-      AppView<AppInfoWithLiveness> appView,
-      NamingLens lens) {
-    return toRenamedKmFunctionHelper(method, appView, lens, true);
-  }
-
-  private static KmFunction toRenamedKmFunctionHelper(
-      DexEncodedMethod method,
-      AppView<AppInfoWithLiveness> appView,
-      NamingLens lens,
-      boolean isExtension) {
     // For library overrides, synthesize @Metadata always.
     // For regular methods, make sure it is live or pinned.
     if (!method.isLibraryMethodOverride().isTrue()
@@ -146,7 +131,7 @@
       return null;
     }
     kmFunction.setReturnType(kmReturnType);
-    if (isExtension) {
+    if (method.isKotlinExtensionFunction()) {
       assert method.method.proto.parameters.values.length > 0
           : method.method.toSourceString();
       KmType kmReceiverType =
@@ -157,7 +142,7 @@
       kmFunction.setReceiverParameterType(kmReceiverType);
     }
     List<KmValueParameter> parameters = kmFunction.getValueParameters();
-    if (!populateKmValueParameters(parameters, method, appView, lens, isExtension)) {
+    if (!populateKmValueParameters(parameters, method, appView, lens)) {
       return null;
     }
     return kmFunction;
@@ -167,24 +152,53 @@
       List<KmValueParameter> parameters,
       DexEncodedMethod method,
       AppView<AppInfoWithLiveness> appView,
-      NamingLens lens,
-      boolean isExtension) {
+      NamingLens lens) {
+    boolean isExtension = method.isKotlinExtensionFunction();
     for (int i = isExtension ? 1 : 0; i < method.method.proto.parameters.values.length; i++) {
-      DexType paramType = method.method.proto.parameters.values[i];
+      DexType parameterType = method.method.proto.parameters.values[i];
       DebugLocalInfo debugLocalInfo = method.getParameterInfo().get(i);
       String parameterName = debugLocalInfo != null ? debugLocalInfo.name.toString() : ("p" + i);
-      // TODO(b/70169921): Consult kotlinx.metadata.Flag.ValueParameter.
-      KmValueParameter kmValueParameter = new KmValueParameter(flagsOf(), parameterName);
-      KmType kmParamType = toRenamedKmType(paramType, appView, lens);
-      if (kmParamType == null) {
+      KotlinValueParameterInfo valueParameterInfo =
+          method.getKotlinMemberInfo().getValueParameterInfo(isExtension ? i - 1 : i);
+      KmValueParameter kmValueParameter =
+          toRewrittenKmValueParameter(
+              valueParameterInfo, parameterType, parameterName, appView, lens);
+      if (kmValueParameter == null) {
         return false;
       }
-      kmValueParameter.setType(kmParamType);
       parameters.add(kmValueParameter);
     }
     return true;
   }
 
+  private static KmValueParameter toRewrittenKmValueParameter(
+      KotlinValueParameterInfo valueParameterInfo,
+      DexType parameterType,
+      String candidateParameterName,
+      AppView<AppInfoWithLiveness> appView,
+      NamingLens lens) {
+    KmType kmParamType = toRenamedKmType(parameterType, appView, lens);
+    if (kmParamType == null) {
+      return null;
+    }
+    int flag = valueParameterInfo != null ? valueParameterInfo.flag : flagsOf();
+    String name = valueParameterInfo != null ? valueParameterInfo.name : candidateParameterName;
+    KmValueParameter kmValueParameter = new KmValueParameter(flag, name);
+    kmValueParameter.setType(kmParamType);
+    if (valueParameterInfo != null && valueParameterInfo.isVararg) {
+      if (!parameterType.isArrayType()) {
+        return null;
+      }
+      DexType elementType = parameterType.toBaseType(appView.dexItemFactory());
+      KmType kmElementType = toRenamedKmType(elementType, appView, lens);
+      if (kmElementType == null) {
+        return null;
+      }
+      kmValueParameter.setVarargElementType(kmElementType);
+    }
+    return kmValueParameter;
+  }
+
   /**
    * A group of a field, and methods that correspond to a Kotlin property.
    *
@@ -393,23 +407,29 @@
           }
         }
         int valueIndex = isExtension ? 1 : 0;
+        DexType valueType = setter.method.proto.parameters.values[valueIndex];
         if (kmPropertyType == null) {
           // The property type is not set yet.
-          kmPropertyType =
-              toRenamedKmType(setter.method.proto.parameters.values[valueIndex], appView, lens);
+          kmPropertyType = toRenamedKmType(valueType, appView, lens);
           if (kmPropertyType != null) {
             kmProperty.setReturnType(kmPropertyType);
           }
         } else {
           // If property type is set already (via either backing field or getter),
           // make sure it's consistent.
-          KmType kmPropertyTypeFromSetter =
-              toRenamedKmType(setter.method.proto.parameters.values[valueIndex], appView, lens);
+          KmType kmPropertyTypeFromSetter = toRenamedKmType(valueType, appView, lens);
           if (!getDescriptorFromKmType(kmPropertyType)
               .equals(getDescriptorFromKmType(kmPropertyTypeFromSetter))) {
             return null;
           }
         }
+        KotlinValueParameterInfo valueParameterInfo =
+            setter.getKotlinMemberInfo().getValueParameterInfo(valueIndex);
+        KmValueParameter kmValueParameter =
+            toRewrittenKmValueParameter(valueParameterInfo, valueType, "value", appView, lens);
+        if (kmValueParameter != null) {
+          kmProperty.setSetterParameter(kmValueParameter);
+        }
         kmProperty.setSetterFlags(setter.accessFlags.getAsKotlinFlags());
         JvmExtensionsKt.setSetterSignature(kmProperty, toJvmMethodSignature(setter.method));
       }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
new file mode 100644
index 0000000..95d91f8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
@@ -0,0 +1,30 @@
+// 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 kotlinx.metadata.KmValueParameter;
+
+// Provides access to Kotlin information about value parameter.
+class KotlinValueParameterInfo {
+  // TODO(b/70169921): When to use original param name v.s. when to *not* use?
+  // Original parameter name.
+  final String name;
+  // Original parameter flag, e.g., has default value.
+  final int flag;
+  // Indicates whether the formal parameter is originally `vararg`.
+  final boolean isVararg;
+
+  private KotlinValueParameterInfo(String name, int flag, boolean isVararg) {
+    this.name = name;
+    this.flag = flag;
+    this.isVararg = isVararg;
+  }
+
+  static KotlinValueParameterInfo fromKmValueParameter(KmValueParameter kmValueParameter) {
+    return new KotlinValueParameterInfo(
+        kmValueParameter.getName(),
+        kmValueParameter.getFlags(),
+        kmValueParameter.getVarargElementType() != null);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java
index ec9cdc7..54675ca 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java
@@ -10,7 +10,9 @@
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
@@ -21,10 +23,12 @@
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
 import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
+import com.android.tools.r8.utils.codeinspector.KmValueParameterSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import java.nio.file.Path;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -91,7 +95,7 @@
     assertThat(
         kotlinTestCompileResult.stderr, containsString("but java.util.Map<K, V> was expected"));
     assertThat(
-        kotlinTestCompileResult.stderr, containsString("no value passed for parameter 'p2'"));
+        kotlinTestCompileResult.stderr, not(containsString("no value passed for parameter 'p2'")));
   }
 
   private void inspect(CodeInspector inspector) {
@@ -114,7 +118,11 @@
 
     KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("applyMap");
     assertThat(kmFunction, isExtensionFunction());
-    // TODO(b/70169921): inspect 2nd arg has flag that says it has a default value.
-    //  https://github.com/JetBrains/kotlin/blob/master/libraries/kotlinx-metadata/src/kotlinx/metadata/Flag.kt#L455
+    List<KmValueParameterSubject> valueParameters = kmFunction.valueParameters();
+    assertEquals(2, valueParameters.size());
+    // TODO(b/70169921): inspect 1st arg is Map with correct type parameter.
+    KmValueParameterSubject valueParameter = valueParameters.get(1);
+    assertTrue(valueParameter.declaresDefaultValue());
+    assertEquals("Lkotlin/String;", valueParameter.type().descriptor());
   }
 }
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
index f4e79f3..1bf7552 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
@@ -10,7 +10,9 @@
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
@@ -20,10 +22,12 @@
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
 import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
+import com.android.tools.r8.utils.codeinspector.KmValueParameterSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import java.nio.file.Path;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -66,7 +70,7 @@
     Path libJar =
         testForR8(parameters.getBackend())
             .addProgramFiles(varargLibJarMap.get(targetVersion))
-            // keep SomClass#foo, since there is a method reference in the app.
+            // keep SomeClass#foo, since there is a method reference in the app.
             .addKeepRules("-keep class **.SomeClass { *** foo(...); }")
             // Keep LibKt, along with bar function.
             .addKeepRules("-keep class **.LibKt { *** bar(...); }")
@@ -87,7 +91,8 @@
     assertNotEquals(0, kotlinTestCompileResult.exitCode);
     assertThat(
         kotlinTestCompileResult.stderr,
-        containsString("type mismatch: inferred type is String but Array<T> was expected"));
+        not(containsString("type mismatch: inferred type is String but Array<T> was expected")));
+    assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: foo"));
   }
 
   private void inspect(CodeInspector inspector) {
@@ -115,6 +120,11 @@
 
     KmFunctionSubject kmFunction = kmPackage.kmFunctionWithUniqueName("bar");
     assertThat(kmFunction, not(isExtensionFunction()));
-    // TODO(b/70169921): inspect 1st arg is `vararg`.
+    List<KmValueParameterSubject> valueParameters = kmFunction.valueParameters();
+    assertEquals(2, valueParameters.size());
+    KmValueParameterSubject valueParameter = valueParameters.get(0);
+    assertTrue(valueParameter.isVararg());
+    assertEquals("Lkotlin/String;", valueParameter.varargElementType().descriptor());
+    // TODO(b/70169921): inspect 2nd arg is lambda with correct type parameter.
   }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmFunctionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmFunctionSubject.java
index c72d305..5efcf6e 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmFunctionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmFunctionSubject.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.utils.codeinspector;
 
+import java.util.List;
 import kotlinx.metadata.jvm.JvmMethodSignature;
 
 public class AbsentKmFunctionSubject extends KmFunctionSubject {
@@ -38,6 +39,11 @@
   }
 
   @Override
+  public List<KmValueParameterSubject> valueParameters() {
+    return null;
+  }
+
+  @Override
   public KmTypeSubject returnType() {
     return null;
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmFunctionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmFunctionSubject.java
index 3bbf235..28eff88 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmFunctionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmFunctionSubject.java
@@ -4,6 +4,8 @@
 package com.android.tools.r8.utils.codeinspector;
 
 import com.android.tools.r8.utils.codeinspector.FoundKmDeclarationContainerSubject.KmFunctionProcessor;
+import java.util.List;
+import java.util.stream.Collectors;
 import kotlinx.metadata.KmFunction;
 import kotlinx.metadata.KmType;
 import kotlinx.metadata.jvm.JvmMethodSignature;
@@ -56,6 +58,13 @@
   }
 
   @Override
+  public List<KmValueParameterSubject> valueParameters() {
+    return kmFunction.getValueParameters().stream()
+        .map(KmValueParameterSubject::new)
+        .collect(Collectors.toList());
+  }
+
+  @Override
   public KmTypeSubject returnType() {
     return new KmTypeSubject(kmFunction.getReturnType());
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/KmFunctionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/KmFunctionSubject.java
index d0e0a37..161aa3f 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/KmFunctionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/KmFunctionSubject.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.utils.codeinspector;
 
+import java.util.List;
 import kotlinx.metadata.KmFunction;
 import kotlinx.metadata.jvm.JvmMethodSignature;
 
@@ -18,5 +19,7 @@
 
   public abstract KmTypeSubject receiverParameterType();
 
+  public abstract List<KmValueParameterSubject> valueParameters();
+
   public abstract KmTypeSubject returnType();
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/KmValueParameterSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/KmValueParameterSubject.java
new file mode 100644
index 0000000..1f96b7d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/KmValueParameterSubject.java
@@ -0,0 +1,51 @@
+// 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.utils.codeinspector;
+
+import static kotlinx.metadata.Flag.ValueParameter.DECLARES_DEFAULT_VALUE;
+
+import com.android.tools.r8.errors.Unreachable;
+import kotlinx.metadata.KmValueParameter;
+
+public class KmValueParameterSubject extends Subject {
+  private final KmValueParameter kmValueParameter;
+
+  KmValueParameterSubject(KmValueParameter kmValueParameter) {
+    this.kmValueParameter = kmValueParameter;
+  }
+
+  public KmTypeSubject type() {
+    return new KmTypeSubject(kmValueParameter.getType());
+  }
+
+  public KmTypeSubject varargElementType() {
+    if (!isVararg()) {
+      return null;
+    }
+    return new KmTypeSubject(kmValueParameter.getVarargElementType());
+  }
+
+  public boolean isVararg() {
+    return kmValueParameter.getVarargElementType() != null;
+  }
+
+  public boolean declaresDefaultValue() {
+    return DECLARES_DEFAULT_VALUE.invoke(kmValueParameter.getFlags());
+  }
+
+  @Override
+  public boolean isPresent() {
+    return true;
+  }
+
+  @Override
+  public boolean isRenamed() {
+    throw new Unreachable("Cannot determine if a parameter is renamed");
+  }
+
+  @Override
+  public boolean isSynthetic() {
+    throw new Unreachable("Cannot determine if a parameter is synthetic");
+  }
+}