Reland "Towards synthesizing Kotlin @Metadata: type projection (type arguments)."
This reverts commit b93b4e950d338f85a7ffc5c7e59dd0c4b9ead244.
Change-Id: Idce97aa98941316d543d619b3cfff94a77f3554a
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignature.java b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
index 47763c5..743f406 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignature.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
@@ -269,6 +269,26 @@
outer.innerTypeSignature = inner;
inner.enclosingTypeSignature = outer;
}
+
+ // TODO(b/129925954): rewrite GenericSignatureRewriter with this pattern?
+ public interface Converter<R> {
+ R init();
+ R visitType(DexType type, R result);
+ R visitTypeArgument(FieldTypeSignature typeArgument, R result);
+ R visitInnerTypeSignature(ClassTypeSignature innerTypeSignature, R result);
+ }
+
+ public <R> R convert(Converter<R> converter) {
+ R result = converter.init();
+ result = converter.visitType(type, result);
+ for (FieldTypeSignature typeArgument : typeArguments) {
+ result = converter.visitTypeArgument(typeArgument, result);
+ }
+ if (innerTypeSignature != null) {
+ result = converter.visitInnerTypeSignature(innerTypeSignature, result);
+ }
+ return result;
+ }
}
public static class ArrayTypeSignature extends FieldTypeSignature {
diff --git a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
index 941d7e2..a1cdd66 100644
--- a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
+++ b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
@@ -83,7 +83,21 @@
.put(factory.boxedNumberType, factory.createType(addKotlinPrefix("Number;")))
.put(factory.comparableType, factory.createType(addKotlinPrefix("Comparable;")))
.put(factory.enumType, factory.createType(addKotlinPrefix("Enum;")))
- // TODO(b/70169921): (Mutable) Collections?
+ // Collections
+ .put(factory.iteratorType, factory.createType(addKotlinPrefix("collections/Iterator;")))
+ .put(
+ factory.collectionType,
+ factory.createType(addKotlinPrefix("collections/Collection;")))
+ .put(factory.listType, factory.createType(addKotlinPrefix("collections/List;")))
+ .put(factory.setType, factory.createType(addKotlinPrefix("collections/Set;")))
+ .put(factory.mapType, factory.createType(addKotlinPrefix("collections/Map;")))
+ .put(
+ factory.listIteratorType,
+ factory.createType(addKotlinPrefix("collections/ListIterator;")))
+ .put(factory.iterableType, factory.createType(addKotlinPrefix("collections/Iterable;")))
+ .put(
+ factory.mapEntryType,
+ factory.createType(addKotlinPrefix("collections/Map$Entry;")))
// .../jvm/functions/FunctionN -> .../FunctionN
.putAll(
IntStream.rangeClosed(0, 22).boxed().collect(Collectors.toMap(
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
index 6085350..4a65b7c 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
@@ -61,14 +61,16 @@
List<KmType> superTypes = kmClass.getSupertypes();
superTypes.clear();
for (DexType itfType : clazz.interfaces.values) {
- KmType kmType = toRenamedKmType(itfType, appView, lens);
+ // TODO(b/129925954): Use GenericSignature.ClassSignature#superInterfaceSignatures
+ KmType kmType = toRenamedKmType(itfType, null, appView, lens);
if (kmType != null) {
superTypes.add(kmType);
}
}
assert clazz.superType != null;
if (clazz.superType != appView.dexItemFactory().objectType) {
- KmType kmTypeForSupertype = toRenamedKmType(clazz.superType, appView, lens);
+ // TODO(b/129925954): Use GenericSignature.ClassSignature#superClassSignature
+ KmType kmTypeForSupertype = toRenamedKmType(clazz.superType, null, appView, lens);
if (kmTypeForSupertype != null) {
superTypes.add(kmTypeForSupertype);
}
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 b16d90a..81324e5 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
@@ -19,14 +19,21 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.TypeSignature;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.graph.GenericSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.List;
import kotlinx.metadata.KmConstructor;
import kotlinx.metadata.KmFunction;
import kotlinx.metadata.KmProperty;
import kotlinx.metadata.KmType;
+import kotlinx.metadata.KmTypeProjection;
import kotlinx.metadata.KmValueParameter;
+import kotlinx.metadata.KmVariance;
import kotlinx.metadata.jvm.JvmExtensionsKt;
class KotlinMetadataSynthesizer {
@@ -88,8 +95,20 @@
return descriptorToKotlinClassifier(renamedType.toDescriptorString());
}
+ // TODO(b/148654451): Canonicalization?
static KmType toRenamedKmType(
- DexType type, AppView<AppInfoWithLiveness> appView, NamingLens lens) {
+ DexType type,
+ TypeSignature typeSignature,
+ AppView<AppInfoWithLiveness> appView,
+ NamingLens lens) {
+ if (typeSignature != null
+ && typeSignature.isFieldTypeSignature()
+ && typeSignature.asFieldTypeSignature().isClassTypeSignature()
+ && typeSignature.asFieldTypeSignature().asClassTypeSignature().type() == type) {
+ return toRenamedKmType(
+ typeSignature.asFieldTypeSignature().asClassTypeSignature(), appView, lens);
+ }
+
String classifier = toRenamedClassifier(type, appView, lens);
if (classifier == null) {
return null;
@@ -98,12 +117,74 @@
// and/or why wiping out flags works for KmType but not KmFunction?!
KmType kmType = new KmType(flagsOf());
kmType.visitClass(classifier);
- // TODO(b/70169921): Need to set arguments as type parameter.
- // E.g., for kotlin/Function1<P1, R>, P1 and R are recorded inside KmType.arguments, which
- // enables kotlinc to resolve `this` type and return type of the lambda.
+ // TODO(b/70169921): Can be generalized too, like ArrayTypeSignature.Converter ?
+ // E.g., java.lang.String[] -> KmType(kotlin/Array, KmTypeProjection(OUT, kotlin/String))
+ if (type.isArrayType() && !type.isPrimitiveArrayType()) {
+ DexType elementType = type.toArrayElementType(appView.dexItemFactory());
+ KmType argumentType = toRenamedKmType(elementType, null, appView, lens);
+ kmType.getArguments().add(new KmTypeProjection(KmVariance.OUT, argumentType));
+ }
return kmType;
}
+ static KmType toRenamedKmType(
+ ClassTypeSignature classTypeSignature,
+ AppView<AppInfoWithLiveness> appView,
+ NamingLens lens) {
+ return classTypeSignature.convert(
+ new ClassTypeSignatureToRenamedKmTypeConverter(appView, lens));
+ }
+
+ static class ClassTypeSignatureToRenamedKmTypeConverter
+ implements ClassTypeSignature.Converter<KmType> {
+
+ private final AppView<AppInfoWithLiveness> appView;
+ private final NamingLens lens;
+
+ ClassTypeSignatureToRenamedKmTypeConverter(
+ AppView<AppInfoWithLiveness> appView, NamingLens lens) {
+ this.appView = appView;
+ this.lens = lens;
+ }
+
+ @Override
+ public KmType init() {
+ return new KmType(flagsOf());
+ }
+
+ @Override
+ public KmType visitType(DexType type, KmType result) {
+ if (result == null) {
+ return result;
+ }
+ String classifier = toRenamedClassifier(type, appView, lens);
+ if (classifier == null) {
+ return null;
+ }
+ result.visitClass(classifier);
+ return result;
+ }
+
+ @Override
+ public KmType visitTypeArgument(FieldTypeSignature typeArgument, KmType result) {
+ if (result == null) {
+ return result;
+ }
+ if (typeArgument.isClassTypeSignature()) {
+ KmType argumentType = typeArgument.asClassTypeSignature().convert(this);
+ result.getArguments().add(new KmTypeProjection(KmVariance.INVARIANT, argumentType));
+ }
+ // TODO(b/70169921): for TypeVariableSignature, there is KmType::visitTypeParameter.
+ return result;
+ }
+
+ @Override
+ public KmType visitInnerTypeSignature(ClassTypeSignature innerTypeSignature, KmType result) {
+ // Do nothing
+ return result;
+ }
+ }
+
static KmConstructor toRenamedKmConstructor(
DexEncodedMethod method,
AppView<AppInfoWithLiveness> appView,
@@ -115,8 +196,9 @@
}
KmConstructor kmConstructor = new KmConstructor(method.accessFlags.getAsKotlinFlags());
JvmExtensionsKt.setSignature(kmConstructor, toJvmMethodSignature(method.method));
+ MethodTypeSignature signature = GenericSignature.Parser.toMethodTypeSignature(method, appView);
List<KmValueParameter> parameters = kmConstructor.getValueParameters();
- if (!populateKmValueParameters(parameters, method, appView, lens)) {
+ if (!populateKmValueParameters(parameters, method, signature, appView, lens)) {
return null;
}
return kmConstructor;
@@ -145,23 +227,34 @@
: method.accessFlags.getAsKotlinFlags();
KmFunction kmFunction = new KmFunction(flag, renamedMethod.name.toString());
JvmExtensionsKt.setSignature(kmFunction, toJvmMethodSignature(renamedMethod));
- KmType kmReturnType = toRenamedKmType(method.method.proto.returnType, appView, lens);
+
+ // TODO(b/129925954): Should this be integrated as part of DexDefinition instead of parsing it
+ // on demand? That may require utils to map internal encoding of signature back to
+ // corresponding backend definitions, like DexAnnotation (DEX) or Signature attribute (CF).
+ MethodTypeSignature signature = GenericSignature.Parser.toMethodTypeSignature(method, appView);
+
+ DexType returnType = method.method.proto.returnType;
+ TypeSignature returnSignature = signature.returnType();
+ KmType kmReturnType = toRenamedKmType(returnType, returnSignature, appView, lens);
if (kmReturnType == null) {
return null;
}
kmFunction.setReturnType(kmReturnType);
+
if (method.isKotlinExtensionFunction()) {
assert method.method.proto.parameters.values.length > 0
: method.method.toSourceString();
- KmType kmReceiverType =
- toRenamedKmType(method.method.proto.parameters.values[0], appView, lens);
+ DexType receiverType = method.method.proto.parameters.values[0];
+ TypeSignature receiverSignature = signature.getParameterTypeSignature(0);
+ KmType kmReceiverType = toRenamedKmType(receiverType, receiverSignature, appView, lens);
if (kmReceiverType == null) {
return null;
}
kmFunction.setReceiverParameterType(kmReceiverType);
}
+
List<KmValueParameter> parameters = kmFunction.getValueParameters();
- if (!populateKmValueParameters(parameters, method, appView, lens)) {
+ if (!populateKmValueParameters(parameters, method, signature, appView, lens)) {
return null;
}
return kmFunction;
@@ -170,6 +263,7 @@
private static boolean populateKmValueParameters(
List<KmValueParameter> parameters,
DexEncodedMethod method,
+ MethodTypeSignature signature,
AppView<AppInfoWithLiveness> appView,
NamingLens lens) {
boolean isExtension = method.isKotlinExtensionFunction();
@@ -179,9 +273,15 @@
String parameterName = debugLocalInfo != null ? debugLocalInfo.name.toString() : ("p" + i);
KotlinValueParameterInfo valueParameterInfo =
method.getKotlinMemberInfo().getValueParameterInfo(isExtension ? i - 1 : i);
+ TypeSignature parameterTypeSignature = signature.getParameterTypeSignature(i);
KmValueParameter kmValueParameter =
toRewrittenKmValueParameter(
- valueParameterInfo, parameterType, parameterName, appView, lens);
+ valueParameterInfo,
+ parameterType,
+ parameterTypeSignature,
+ parameterName,
+ appView,
+ lens);
if (kmValueParameter == null) {
return false;
}
@@ -193,10 +293,11 @@
private static KmValueParameter toRewrittenKmValueParameter(
KotlinValueParameterInfo valueParameterInfo,
DexType parameterType,
+ TypeSignature parameterTypeSignature,
String candidateParameterName,
AppView<AppInfoWithLiveness> appView,
NamingLens lens) {
- KmType kmParamType = toRenamedKmType(parameterType, appView, lens);
+ KmType kmParamType = toRenamedKmType(parameterType, parameterTypeSignature, appView, lens);
if (kmParamType == null) {
return null;
}
@@ -204,17 +305,22 @@
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);
+ DexType elementType = parameterType.toArrayElementType(appView.dexItemFactory());
+ TypeSignature elementSignature =
+ parameterTypeSignature != null
+ ? parameterTypeSignature.toArrayElementTypeSignature(appView) : null;
+ KmType kmElementType = toRenamedKmType(elementType, elementSignature, appView, lens);
if (kmElementType == null) {
return null;
}
kmValueParameter.setVarargElementType(kmElementType);
}
+
return kmValueParameter;
}
@@ -363,7 +469,9 @@
if (canChangePropertyName && renamedField.name != field.field.name) {
renamedPropertyName = renamedField.name.toString();
}
- kmPropertyType = toRenamedKmType(field.field.type, appView, lens);
+ FieldTypeSignature signature =
+ GenericSignature.Parser.toFieldTypeSignature(field, appView);
+ kmPropertyType = toRenamedKmType(field.field.type, signature, appView, lens);
if (kmPropertyType != null) {
kmProperty.setReturnType(kmPropertyType);
}
@@ -378,24 +486,31 @@
if (criteria == GetterSetterCriteria.MET) {
assert getter != null
: "checkGetterCriteria: " + this.toString();
+ MethodTypeSignature signature =
+ GenericSignature.Parser.toMethodTypeSignature(getter, appView);
if (isExtension) {
assert getter.method.proto.parameters.size() == 1
: "checkGetterCriteria: " + this.toString();
- kmReceiverType = toRenamedKmType(getter.method.proto.parameters.values[0], appView, lens);
+ TypeSignature receiverSignature = signature.getParameterTypeSignature(0);
+ kmReceiverType = toRenamedKmType(
+ getter.method.proto.parameters.values[0], receiverSignature, appView, lens);
if (kmReceiverType != null) {
kmProperty.setReceiverParameterType(kmReceiverType);
}
}
+
+ DexType returnType = getter.method.proto.returnType;
+ TypeSignature returnSignature = signature.returnType();
if (kmPropertyType == null) {
// The property type is not set yet.
- kmPropertyType = toRenamedKmType(getter.method.proto.returnType, appView, lens);
+ kmPropertyType = toRenamedKmType(returnType, returnSignature, appView, lens);
if (kmPropertyType != null) {
kmProperty.setReturnType(kmPropertyType);
}
} else {
// If property type is set already (via backing field), make sure it's consistent.
KmType kmPropertyTypeFromGetter =
- toRenamedKmType(getter.method.proto.returnType, appView, lens);
+ toRenamedKmType(returnType, returnSignature, appView, lens);
if (!getDescriptorFromKmType(kmPropertyType)
.equals(getDescriptorFromKmType(kmPropertyTypeFromGetter))) {
return null;
@@ -422,12 +537,15 @@
if (criteria == GetterSetterCriteria.MET) {
assert setter != null && setter.method.proto.parameters.size() >= 1
: "checkSetterCriteria: " + this.toString();
+ MethodTypeSignature signature =
+ GenericSignature.Parser.toMethodTypeSignature(setter, appView);
if (isExtension) {
assert setter.method.proto.parameters.size() == 2
: "checkSetterCriteria: " + this.toString();
+ DexType receiverType = setter.method.proto.parameters.values[0];
+ TypeSignature receiverSignature = signature.getParameterTypeSignature(0);
if (kmReceiverType == null) {
- kmReceiverType =
- toRenamedKmType(setter.method.proto.parameters.values[0], appView, lens);
+ kmReceiverType = toRenamedKmType(receiverType, receiverSignature, appView, lens);
if (kmReceiverType != null) {
kmProperty.setReceiverParameterType(kmReceiverType);
}
@@ -435,25 +553,28 @@
// If the receiver type for the extension property is set already (via getter),
// make sure it's consistent.
KmType kmReceiverTypeFromSetter =
- toRenamedKmType(setter.method.proto.parameters.values[0], appView, lens);
+ toRenamedKmType(receiverType, receiverSignature, appView, lens);
if (!getDescriptorFromKmType(kmReceiverType)
.equals(getDescriptorFromKmType(kmReceiverTypeFromSetter))) {
return null;
}
}
}
+
int valueIndex = isExtension ? 1 : 0;
DexType valueType = setter.method.proto.parameters.values[valueIndex];
+ TypeSignature valueSignature = signature.getParameterTypeSignature(valueIndex);
if (kmPropertyType == null) {
// The property type is not set yet.
- kmPropertyType = toRenamedKmType(valueType, appView, lens);
+ kmPropertyType = toRenamedKmType(valueType, valueSignature, 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(valueType, appView, lens);
+ KmType kmPropertyTypeFromSetter =
+ toRenamedKmType(valueType, valueSignature, appView, lens);
if (!getDescriptorFromKmType(kmPropertyType)
.equals(getDescriptorFromKmType(kmPropertyTypeFromSetter))) {
return null;
@@ -461,8 +582,8 @@
}
KotlinValueParameterInfo valueParameterInfo =
setter.getKotlinMemberInfo().getValueParameterInfo(valueIndex);
- KmValueParameter kmValueParameter =
- toRewrittenKmValueParameter(valueParameterInfo, valueType, "value", appView, lens);
+ KmValueParameter kmValueParameter = toRewrittenKmValueParameter(
+ valueParameterInfo, valueType, valueSignature, "value", appView, lens);
if (kmValueParameter != null) {
kmProperty.setSetterParameter(kmValueParameter);
}
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 54675ca..f5d26ae 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
@@ -7,22 +7,22 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-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.assertFalse;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
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.KmTypeProjectionSubject;
+import com.android.tools.r8.utils.codeinspector.KmTypeSubject;
import com.android.tools.r8.utils.codeinspector.KmValueParameterSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.nio.file.Path;
@@ -75,27 +75,25 @@
// Keep LibKt and applyMap function, along with applyMap$default
.addKeepRules("-keep class **.LibKt { *** applyMap*(...); }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
+ .addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
+ .addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
.compile()
.inspect(this::inspect)
.writeToZip();
- ProcessResult kotlinTestCompileResult =
+ Path output =
kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/default_value_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- // TODO(b/70169921): update to just .compile() once fixed.
- .compileRaw();
+ .compile();
- // TODO(b/70169921): should be able to compile!
- assertNotEquals(0, kotlinTestCompileResult.exitCode);
- assertThat(
- kotlinTestCompileResult.stderr,
- containsString("type mismatch: inferred type is kotlin.collections.Map<String, String"));
- assertThat(
- kotlinTestCompileResult.stderr, containsString("but java.util.Map<K, V> was expected"));
- assertThat(
- kotlinTestCompileResult.stderr, not(containsString("no value passed for parameter 'p2'")));
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".default_value_app.MainKt")
+ .assertSuccessWithOutputLines("a", "b", "c");
}
private void inspect(CodeInspector inspector) {
@@ -116,12 +114,23 @@
KmPackageSubject kmPackage = libKt.getKmPackage();
assertThat(kmPackage, isPresent());
+ // String applyMap(Map<String, String>, (default) String)
KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("applyMap");
assertThat(kmFunction, isExtensionFunction());
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);
+
+ KmValueParameterSubject valueParameter = valueParameters.get(0);
+ assertFalse(valueParameter.declaresDefaultValue());
+ assertEquals("Lkotlin/collections/Map;", valueParameter.type().descriptor());
+ List<KmTypeProjectionSubject> typeArguments = valueParameter.type().typeArguments();
+ assertEquals(2, typeArguments.size());
+ KmTypeSubject typeArgument = typeArguments.get(0).type();
+ assertEquals("Lkotlin/String;", typeArgument.descriptor());
+ typeArgument = typeArguments.get(1).type();
+ assertEquals("Lkotlin/String;", typeArgument.descriptor());
+
+ valueParameter = valueParameters.get(1);
assertTrue(valueParameter.declaresDefaultValue());
assertEquals("Lkotlin/String;", valueParameter.type().descriptor());
}
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 1bf7552..ea12b9e 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
@@ -7,21 +7,22 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-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.assertFalse;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
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.KmTypeProjectionSubject;
+import com.android.tools.r8.utils.codeinspector.KmTypeSubject;
import com.android.tools.r8.utils.codeinspector.KmValueParameterSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.nio.file.Path;
@@ -75,24 +76,25 @@
// Keep LibKt, along with bar function.
.addKeepRules("-keep class **.LibKt { *** bar(...); }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
+ .addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
+ .addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
.compile()
.inspect(this::inspect)
.writeToZip();
- ProcessResult kotlinTestCompileResult =
+ Path output =
kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/vararg_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- // TODO(b/70169921): update to just .compile() once fixed.
- .compileRaw();
+ .compile();
- // TODO(b/70169921): should be able to compile!
- assertNotEquals(0, kotlinTestCompileResult.exitCode);
- assertThat(
- kotlinTestCompileResult.stderr,
- not(containsString("type mismatch: inferred type is String but Array<T> was expected")));
- assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: foo"));
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".vararg_app.MainKt")
+ .assertSuccessWithOutputLines("SomeClass::R8");
}
private void inspect(CodeInspector inspector) {
@@ -118,13 +120,32 @@
KmPackageSubject kmPackage = libKt.getKmPackage();
assertThat(kmPackage, isPresent());
+ // Unit bar(vararg String, (SomeClass, String) -> Unit)
KmFunctionSubject kmFunction = kmPackage.kmFunctionWithUniqueName("bar");
assertThat(kmFunction, not(isExtensionFunction()));
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.
+
+ assertEquals("Lkotlin/Array;", valueParameter.type().descriptor());
+ List<KmTypeProjectionSubject> typeArguments = valueParameter.type().typeArguments();
+ assertEquals(1, typeArguments.size());
+ KmTypeSubject typeArgument = typeArguments.get(0).type();
+ assertEquals("Lkotlin/String;", typeArgument.descriptor());
+
+ valueParameter = valueParameters.get(1);
+ assertFalse(valueParameter.isVararg());
+ typeArguments = valueParameter.type().typeArguments();
+ assertEquals(3, typeArguments.size());
+
+ typeArgument = typeArguments.get(0).type();
+ assertEquals(cls.getFinalDescriptor(), typeArgument.descriptor());
+ typeArgument = typeArguments.get(1).type();
+ assertEquals("Lkotlin/String;", typeArgument.descriptor());
+ typeArgument = typeArguments.get(2).type();
+ assertEquals("Lkotlin/Unit;", typeArgument.descriptor());
}
}
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 28eff88..cdf57aa 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
@@ -54,18 +54,18 @@
public KmTypeSubject receiverParameterType() {
KmType kmType = kmFunction.getReceiverParameterType();
assert !isExtension() || kmType != null;
- return kmType == null ? null : new KmTypeSubject(kmType);
+ return kmType == null ? null : new KmTypeSubject(codeInspector, kmType);
}
@Override
public List<KmValueParameterSubject> valueParameters() {
return kmFunction.getValueParameters().stream()
- .map(KmValueParameterSubject::new)
+ .map(kmValueParameter -> new KmValueParameterSubject(codeInspector, kmValueParameter))
.collect(Collectors.toList());
}
@Override
public KmTypeSubject returnType() {
- return new KmTypeSubject(kmFunction.getReturnType());
+ return new KmTypeSubject(codeInspector, kmFunction.getReturnType());
}
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeProjectionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeProjectionSubject.java
new file mode 100644
index 0000000..790ecd2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeProjectionSubject.java
@@ -0,0 +1,36 @@
+// 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 com.android.tools.r8.errors.Unreachable;
+import kotlinx.metadata.KmTypeProjection;
+
+public class KmTypeProjectionSubject extends Subject {
+ private final CodeInspector codeInspector;
+ private final KmTypeProjection kmTypeProjection;
+
+ KmTypeProjectionSubject(CodeInspector codeInspector, KmTypeProjection kmTypeProjection) {
+ this.codeInspector = codeInspector;
+ this.kmTypeProjection = kmTypeProjection;
+ }
+
+ public KmTypeSubject type() {
+ return new KmTypeSubject(codeInspector, kmTypeProjection.getType());
+ }
+
+ @Override
+ public boolean isPresent() {
+ return true;
+ }
+
+ @Override
+ public boolean isRenamed() {
+ return type().isRenamed();
+ }
+
+ @Override
+ public boolean isSynthetic() {
+ throw new Unreachable("Cannot determine if a type argument is synthetic");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java
index aa52c67..5d7ec81 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java
@@ -6,15 +6,20 @@
import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromKotlinClassifier;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.Box;
+import java.util.List;
+import java.util.stream.Collectors;
import kotlinx.metadata.KmType;
import kotlinx.metadata.KmTypeVisitor;
public class KmTypeSubject extends Subject {
+ private final CodeInspector codeInspector;
private final KmType kmType;
- KmTypeSubject(KmType kmType) {
+ KmTypeSubject(CodeInspector codeInspector, KmType kmType) {
assert kmType != null;
+ this.codeInspector = codeInspector;
this.kmType = kmType;
}
@@ -46,6 +51,12 @@
return getDescriptorFromKmType(kmType);
}
+ public List<KmTypeProjectionSubject> typeArguments() {
+ return kmType.getArguments().stream()
+ .map(kmTypeProjection -> new KmTypeProjectionSubject(codeInspector, kmTypeProjection))
+ .collect(Collectors.toList());
+ }
+
@Override
public boolean isPresent() {
return true;
@@ -53,7 +64,8 @@
@Override
public boolean isRenamed() {
- throw new Unreachable("Cannot determine if a type is renamed");
+ ClassSubject classSubject = codeInspector.clazz(Reference.classFromDescriptor(descriptor()));
+ return classSubject.isRenamed();
}
@Override
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
index 1f96b7d..61a2531 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/KmValueParameterSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/KmValueParameterSubject.java
@@ -9,21 +9,23 @@
import kotlinx.metadata.KmValueParameter;
public class KmValueParameterSubject extends Subject {
+ private final CodeInspector codeInspector;
private final KmValueParameter kmValueParameter;
- KmValueParameterSubject(KmValueParameter kmValueParameter) {
+ KmValueParameterSubject(CodeInspector codeInspector, KmValueParameter kmValueParameter) {
+ this.codeInspector = codeInspector;
this.kmValueParameter = kmValueParameter;
}
public KmTypeSubject type() {
- return new KmTypeSubject(kmValueParameter.getType());
+ return new KmTypeSubject(codeInspector, kmValueParameter.getType());
}
public KmTypeSubject varargElementType() {
if (!isVararg()) {
return null;
}
- return new KmTypeSubject(kmValueParameter.getVarargElementType());
+ return new KmTypeSubject(codeInspector, kmValueParameter.getVarargElementType());
}
public boolean isVararg() {