Extend KotlinMetadataSynthesizer with proper type-argument handling
Bug: 151925520
Bug: 152306391
Bug: 152078816
Bug: 152284661
Change-Id: I740453910192cbcaf4f14a557cea92a14a854e39
diff --git a/src/main/java/com/android/tools/r8/kotlin/ClassTypeSignatureToRenamedKmTypeConverter.java b/src/main/java/com/android/tools/r8/kotlin/ClassTypeSignatureToRenamedKmTypeConverter.java
new file mode 100644
index 0000000..ea1ddc3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/ClassTypeSignatureToRenamedKmTypeConverter.java
@@ -0,0 +1,78 @@
+// 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 static kotlinx.metadata.FlagsKt.flagsOf;
+
+import com.android.tools.r8.graph.AppView;
+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.shaking.AppInfoWithLiveness;
+import java.util.List;
+import java.util.function.Function;
+import kotlinx.metadata.KmType;
+import kotlinx.metadata.KmTypeParameter;
+import kotlinx.metadata.KmTypeProjection;
+import kotlinx.metadata.KmVariance;
+
+class ClassTypeSignatureToRenamedKmTypeConverter implements ClassTypeSignature.Converter<KmType> {
+
+ private final AppView<AppInfoWithLiveness> appView;
+ private final List<KmTypeParameter> typeParameters;
+ private final Function<DexType, String> toRenamedClassClassifier;
+
+ ClassTypeSignatureToRenamedKmTypeConverter(
+ AppView<AppInfoWithLiveness> appView,
+ List<KmTypeParameter> typeParameters,
+ Function<DexType, String> toRenamedClassClassifier) {
+ this.appView = appView;
+ this.typeParameters = typeParameters;
+ this.toRenamedClassClassifier = toRenamedClassClassifier;
+ }
+
+ @Override
+ public KmType init() {
+ return new KmType(flagsOf());
+ }
+
+ @Override
+ public KmType visitType(DexType type, KmType result) {
+ String classifier = toRenamedClassClassifier.apply(type);
+ if (classifier == null) {
+ return null;
+ }
+ result.visitClass(classifier);
+ return result;
+ }
+
+ @Override
+ public KmType visitTypeArgument(FieldTypeSignature typeArgument, KmType result) {
+ if (result == null) {
+ return null;
+ }
+ List<KmTypeProjection> arguments = result.getArguments();
+ KotlinMetadataSynthesizerUtils.populateKmTypeFromSignature(
+ typeArgument,
+ () -> {
+ KmType kmTypeArgument = new KmType(flagsOf());
+ arguments.add(new KmTypeProjection(KmVariance.INVARIANT, kmTypeArgument));
+ return kmTypeArgument;
+ },
+ typeParameters,
+ appView.dexItemFactory());
+ return result;
+ }
+
+ @Override
+ public KmType visitInnerTypeSignature(ClassTypeSignature innerTypeSignature, KmType result) {
+ // Do nothing
+ return result;
+ }
+
+ public List<KmTypeParameter> getTypeParameters() {
+ return typeParameters;
+ }
+}
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 00533e9..c891897 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
@@ -6,9 +6,6 @@
import static com.android.tools.r8.kotlin.Kotlin.addKotlinPrefix;
import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toKmType;
-import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedClassifier;
-import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmConstructor;
-import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmType;
import static kotlinx.metadata.Flag.IS_SEALED;
import com.android.tools.r8.graph.AppView;
@@ -76,21 +73,26 @@
@Override
void rewrite(AppView<AppInfoWithLiveness> appView, NamingLens lens) {
+ KotlinMetadataSynthesizer synthesizer = new KotlinMetadataSynthesizer(appView, lens, this);
if (appView.options().enableKotlinMetadataRewritingForRenamedClasses
&& lens.lookupType(clazz.type, appView.dexItemFactory()) != clazz.type) {
- String renamedClassifier = toRenamedClassifier(clazz.type, appView, lens);
+ String renamedClassifier = synthesizer.toRenamedClassifier(clazz.type);
if (renamedClassifier != null) {
assert !kmClass.getName().equals(renamedClassifier);
kmClass.setName(renamedClassifier);
}
}
+ ClassTypeSignatureToRenamedKmTypeConverter converter =
+ new ClassTypeSignatureToRenamedKmTypeConverter(
+ appView, getTypeParameters(), synthesizer::toRenamedClassifier);
+
// Rewriting upward hierarchy.
List<KmType> superTypes = kmClass.getSupertypes();
superTypes.clear();
for (DexType itfType : clazz.interfaces.values) {
// TODO(b/129925954): Use GenericSignature.ClassSignature#superInterfaceSignatures
- KmType kmType = toRenamedKmType(itfType, null, appView, lens);
+ KmType kmType = synthesizer.toRenamedKmType(itfType, null, null, converter);
if (kmType != null) {
superTypes.add(kmType);
}
@@ -98,7 +100,8 @@
assert clazz.superType != null;
if (clazz.superType != appView.dexItemFactory().objectType) {
// TODO(b/129925954): Use GenericSignature.ClassSignature#superClassSignature
- KmType kmTypeForSupertype = toRenamedKmType(clazz.superType, null, appView, lens);
+ KmType kmTypeForSupertype =
+ synthesizer.toRenamedKmType(clazz.superType, null, null, converter);
if (kmTypeForSupertype != null) {
superTypes.add(kmTypeForSupertype);
}
@@ -130,7 +133,7 @@
sealedSubclasses.clear();
if (IS_SEALED.invoke(kmClass.getFlags())) {
for (DexType subtype : appView.appInfo().allImmediateSubtypes(clazz.type)) {
- String classifier = toRenamedClassifier(subtype, appView, lens);
+ String classifier = synthesizer.toRenamedClassifier(subtype);
if (classifier != null) {
sealedSubclasses.add(classifier);
}
@@ -148,7 +151,7 @@
if (!method.isInstanceInitializer()) {
continue;
}
- KmConstructor constructor = toRenamedKmConstructor(method, appView, lens);
+ KmConstructor constructor = synthesizer.toRenamedKmConstructor(method);
if (constructor != null) {
constructors.add(constructor);
}
@@ -161,7 +164,7 @@
// TODO(b/151193864): enum entries
- rewriteDeclarationContainer(appView, lens);
+ rewriteDeclarationContainer(synthesizer);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java
index 627ac9d..fc3fbbe 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java
@@ -4,8 +4,6 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedBinaryName;
-
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexType;
@@ -46,12 +44,13 @@
@Override
void rewrite(AppView<AppInfoWithLiveness> appView, NamingLens lens) {
ListIterator<String> partClassIterator = partClassNames.listIterator();
+ KotlinMetadataSynthesizer synthesizer = new KotlinMetadataSynthesizer(appView, lens, this);
while (partClassIterator.hasNext()) {
String partClassName = partClassIterator.next();
partClassIterator.remove();
DexType partClassType = appView.dexItemFactory().createType(
DescriptorUtils.getDescriptorFromClassBinaryName(partClassName));
- String renamedPartClassName = toRenamedBinaryName(partClassType, appView, lens);
+ String renamedPartClassName = synthesizer.toRenamedBinaryName(partClassType);
if (renamedPartClassName != null) {
partClassIterator.add(renamedPartClassName);
}
@@ -82,6 +81,6 @@
@Override
public String toString(String indent) {
- return indent + "MultiFileClassFacade(" + StringUtils.join(partClassNames, ", ") + ")";
+ return indent + "MetaData.MultiFileClassFacade(" + StringUtils.join(partClassNames, ", ") + ")";
}
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
index 69bdf1e..90f265c 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
@@ -4,8 +4,6 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedBinaryName;
-
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexType;
@@ -44,11 +42,12 @@
void rewrite(AppView<AppInfoWithLiveness> appView, NamingLens lens) {
DexType facadeClassType = appView.dexItemFactory().createType(
DescriptorUtils.getDescriptorFromClassBinaryName(facadeClassName));
- facadeClassName = toRenamedBinaryName(facadeClassType, appView, lens);
+ KotlinMetadataSynthesizer synthesizer = new KotlinMetadataSynthesizer(appView, lens, this);
+ facadeClassName = synthesizer.toRenamedBinaryName(facadeClassType);
if (!appView.options().enableKotlinMetadataRewritingForMembers) {
return;
}
- rewriteDeclarationContainer(appView, lens);
+ rewriteDeclarationContainer(synthesizer);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
index 521181b..8ec966b 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
@@ -38,7 +38,7 @@
if (!appView.options().enableKotlinMetadataRewritingForMembers) {
return;
}
- rewriteDeclarationContainer(appView, lens);
+ rewriteDeclarationContainer(new KotlinMetadataSynthesizer(appView, lens, this));
}
@Override
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 5275c4d..64ed559 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmFunction;
import static com.android.tools.r8.utils.StringUtils.LINE_SEPARATOR;
import static kotlinx.metadata.Flag.Class.IS_COMPANION_OBJECT;
@@ -20,6 +19,7 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.Action;
import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
@@ -49,7 +49,9 @@
// Provides access to package/class-level Kotlin information.
public abstract class KotlinInfo<MetadataKind extends KotlinClassMetadata> {
+
final DexClass clazz;
+ private static final List<KmTypeParameter> EMPTY_TYPE_PARAMS = ImmutableList.of();
KotlinInfo(MetadataKind metadata, DexClass clazz) {
assert clazz != null;
@@ -67,6 +69,13 @@
abstract KotlinClassHeader createHeader();
+ public final List<KmTypeParameter> getTypeParameters() {
+ if (!this.isClass()) {
+ return EMPTY_TYPE_PARAMS;
+ }
+ return this.asClass().kmClass.getTypeParameters();
+ }
+
public enum Kind {
Class, File, Synthetic, Part, Facade
}
@@ -131,23 +140,22 @@
// {@link KmClass} and {@link KmPackage} are inherited from {@link KmDeclarationContainer} that
// abstract functions and properties. Rewriting of those portions can be unified here.
- void rewriteDeclarationContainer(AppView<AppInfoWithLiveness> appView, NamingLens lens) {
+ void rewriteDeclarationContainer(KotlinMetadataSynthesizer synthesizer) {
assert clazz != null;
KmDeclarationContainer kmDeclarationContainer = getDeclarations();
- rewriteFunctions(appView, lens, kmDeclarationContainer.getFunctions());
- rewriteProperties(appView, lens, kmDeclarationContainer.getProperties());
+ rewriteFunctions(synthesizer, kmDeclarationContainer.getFunctions());
+ rewriteProperties(synthesizer, kmDeclarationContainer.getProperties());
}
- private void rewriteFunctions(
- AppView<AppInfoWithLiveness> appView, NamingLens lens, List<KmFunction> functions) {
+ private void rewriteFunctions(KotlinMetadataSynthesizer synthesizer, List<KmFunction> functions) {
functions.clear();
for (DexEncodedMethod method : clazz.methods()) {
if (method.isInitializer()) {
continue;
}
if (method.isKotlinFunction() || method.isKotlinExtensionFunction()) {
- KmFunction function = toRenamedKmFunction(method, appView, lens);
+ KmFunction function = synthesizer.toRenamedKmFunction(method);
if (function != null) {
functions.add(function);
}
@@ -157,11 +165,12 @@
}
private void rewriteProperties(
- AppView<AppInfoWithLiveness> appView, NamingLens lens, List<KmProperty> properties) {
+ KotlinMetadataSynthesizer synthesizer, List<KmProperty> properties) {
Map<String, KmPropertyGroup.Builder> propertyGroupBuilderMap = new HashMap<>();
// Backing fields for a companion object are declared in its host class.
Iterable<DexEncodedField> fields = clazz.fields();
Predicate<DexEncodedField> backingFieldTester = DexEncodedField::isKotlinBackingField;
+ List<KmTypeParameter> classTypeParameters = getTypeParameters();
if (isClass()) {
KotlinClass ktClass = asClass();
if (IS_COMPANION_OBJECT.invoke(ktClass.kmClass.getFlags()) && ktClass.hostClass != null) {
@@ -177,7 +186,8 @@
assert name != null;
KmPropertyGroup.Builder builder =
propertyGroupBuilderMap.computeIfAbsent(
- name, k -> KmPropertyGroup.builder(kotlinFieldInfo.flags, name));
+ name,
+ k -> KmPropertyGroup.builder(kotlinFieldInfo.flags, name, classTypeParameters));
builder.foundBackingField(field);
}
}
@@ -195,19 +205,19 @@
name,
// Hitting here (creating a property builder) after visiting all fields means that
// this property doesn't have a backing field. Don't use members' flags.
- k -> KmPropertyGroup.builder(kotlinPropertyInfo.flags, name));
+ k -> KmPropertyGroup.builder(kotlinPropertyInfo.flags, name, classTypeParameters));
switch (kotlinPropertyInfo.memberKind) {
case EXTENSION_PROPERTY_GETTER:
builder.isExtensionGetter();
// fallthrough;
case PROPERTY_GETTER:
- builder.foundGetter(method, kotlinPropertyInfo.getterFlags);
+ builder.foundGetter(method, kotlinPropertyInfo);
break;
case EXTENSION_PROPERTY_SETTER:
builder.isExtensionSetter();
// fallthrough;
case PROPERTY_SETTER:
- builder.foundSetter(method, kotlinPropertyInfo.setterFlags);
+ builder.foundSetter(method, kotlinPropertyInfo);
break;
case EXTENSION_PROPERTY_ANNOTATIONS:
builder.isExtensionAnnotations();
@@ -227,7 +237,7 @@
if (group == null) {
continue;
}
- KmProperty property = group.toRenamedKmProperty(appView, lens);
+ KmProperty property = group.toRenamedKmProperty(synthesizer);
if (property != null) {
properties.add(property);
}
@@ -563,6 +573,7 @@
"KmType",
sb,
newIndent -> {
+ appendKeyValue(newIndent, "flags", sb, kmType.getFlags() + "");
appendKeyValue(newIndent, "classifier", sb, kmType.classifier.toString());
appendKeyValue(
newIndent,
@@ -686,6 +697,8 @@
"KmTypeParameter",
sb,
newIndent -> {
+ appendKeyValue(newIndent, "id", sb, typeParameter.getId() + "");
+ appendKeyValue(newIndent, "flags", sb, typeParameter.getFlags() + "");
appendKeyValue(newIndent, "name", sb, typeParameter.getName());
appendKeyValue(newIndent, "variance", sb, typeParameter.getVariance().name());
appendKeyValue(
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 4238d3e..ed7b49f 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java
@@ -29,6 +29,7 @@
import kotlinx.metadata.KmDeclarationContainer;
import kotlinx.metadata.KmFunction;
import kotlinx.metadata.KmProperty;
+import kotlinx.metadata.KmType;
import kotlinx.metadata.KmValueParameter;
import kotlinx.metadata.jvm.JvmMethodSignature;
@@ -86,12 +87,18 @@
public static class KotlinFunctionInfo extends KotlinMemberInfo {
// Information from original KmValueParameter(s) if available.
- private final List<KotlinValueParameterInfo> valueParameterInfos;
+ final List<KotlinValueParameterInfo> valueParameterInfos;
+ // Information from original KmFunction.returnType. Null if this is from a KmConstructor.
+ public final KotlinTypeInfo returnType;
private KotlinFunctionInfo(
- MemberKind memberKind, int flags, List<KotlinValueParameterInfo> valueParameterInfos) {
+ MemberKind memberKind,
+ int flags,
+ KotlinTypeInfo returnType,
+ List<KotlinValueParameterInfo> valueParameterInfos) {
super(memberKind, flags);
assert memberKind.isFunction() || memberKind.isConstructor();
+ this.returnType = returnType;
this.valueParameterInfos = valueParameterInfos;
}
@@ -148,6 +155,9 @@
// Original property name for (extension) property. Otherwise, null.
final String propertyName;
+ // Original return type information. This should never be NULL (even for setters without field).
+ final KotlinTypeInfo returnType;
+
// Information from original KmValueParameter if available.
final KotlinValueParameterInfo valueParameterInfo;
@@ -157,11 +167,13 @@
int getterFlags,
int setterFlags,
String propertyName,
+ KotlinTypeInfo returnType,
KotlinValueParameterInfo valueParameterInfo) {
super(memberKind, flags);
this.getterFlags = getterFlags;
this.setterFlags = setterFlags;
this.propertyName = propertyName;
+ this.returnType = returnType;
this.valueParameterInfo = valueParameterInfo;
}
@@ -178,25 +190,31 @@
private static KotlinFunctionInfo createFunctionInfoFromConstructor(KmConstructor kmConstructor) {
return createFunctionInfo(
- CONSTRUCTOR, kmConstructor.getFlags(), kmConstructor.getValueParameters());
+ CONSTRUCTOR, kmConstructor.getFlags(), null, kmConstructor.getValueParameters());
}
private static KotlinFunctionInfo createFunctionInfo(
MemberKind memberKind, KmFunction kmFunction) {
- return createFunctionInfo(memberKind, kmFunction.getFlags(), kmFunction.getValueParameters());
+ return createFunctionInfo(
+ memberKind,
+ kmFunction.getFlags(),
+ kmFunction.getReturnType(),
+ kmFunction.getValueParameters());
}
private static KotlinFunctionInfo createFunctionInfo(
- MemberKind memberKind, int flags, List<KmValueParameter> valueParameters) {
+ MemberKind memberKind, int flags, KmType returnType, List<KmValueParameter> valueParameters) {
+ assert memberKind.isFunction() || memberKind.isConstructor();
+ KotlinTypeInfo returnTypeInfo = KotlinTypeInfo.create(returnType);
assert memberKind.isFunction() || memberKind.isConstructor();
if (valueParameters.isEmpty()) {
- return new KotlinFunctionInfo(memberKind, flags, EMPTY_PARAM_INFO);
+ return new KotlinFunctionInfo(memberKind, flags, returnTypeInfo, EMPTY_PARAM_INFO);
}
List<KotlinValueParameterInfo> valueParameterInfos = new ArrayList<>(valueParameters.size());
for (KmValueParameter kmValueParameter : valueParameters) {
valueParameterInfos.add(KotlinValueParameterInfo.fromKmValueParameter(kmValueParameter));
}
- return new KotlinFunctionInfo(memberKind, flags, valueParameterInfos);
+ return new KotlinFunctionInfo(memberKind, flags, returnTypeInfo, valueParameterInfos);
}
private static KotlinFieldInfo createFieldInfo(MemberKind memberKind, KmProperty kmProperty) {
@@ -213,6 +231,7 @@
kmProperty.getGetterFlags(),
kmProperty.getSetterFlags(),
kmProperty.getName(),
+ KotlinTypeInfo.create(kmProperty.getReturnType()),
KotlinValueParameterInfo.fromKmValueParameter(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 2be6e27..2e90094 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
@@ -3,9 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.kotlin.Kotlin.NAME;
import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.toJvmFieldSignature;
import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.toJvmMethodSignature;
+import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizerUtils.populateKmTypeFromSignature;
+import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizerUtils.toClassifier;
import static com.android.tools.r8.utils.DescriptorUtils.descriptorToKotlinClassifier;
import static com.android.tools.r8.utils.DescriptorUtils.getBinaryNameFromDescriptor;
import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromKmType;
@@ -24,17 +25,21 @@
import com.android.tools.r8.graph.GenericSignature;
import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.GenericSignature.TypeSignature;
import com.android.tools.r8.kotlin.KotlinMemberInfo.KotlinFunctionInfo;
+import com.android.tools.r8.kotlin.KotlinMemberInfo.KotlinPropertyInfo;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Box;
import java.util.List;
import java.util.function.Consumer;
import kotlinx.metadata.KmConstructor;
import kotlinx.metadata.KmFunction;
import kotlinx.metadata.KmProperty;
import kotlinx.metadata.KmType;
+import kotlinx.metadata.KmTypeParameter;
import kotlinx.metadata.KmTypeProjection;
import kotlinx.metadata.KmValueParameter;
import kotlinx.metadata.KmVariance;
@@ -42,6 +47,17 @@
class KotlinMetadataSynthesizer {
+ private final AppView<AppInfoWithLiveness> appView;
+ private final NamingLens lens;
+ private final List<KmTypeParameter> classTypeParameters;
+
+ public KotlinMetadataSynthesizer(
+ AppView<AppInfoWithLiveness> appView, NamingLens lens, KotlinInfo<?> kotlinInfo) {
+ this.appView = appView;
+ this.lens = lens;
+ this.classTypeParameters = kotlinInfo.getTypeParameters();
+ }
+
static boolean isExtension(KmFunction kmFunction) {
return kmFunction.getReceiverParameterType() != null;
}
@@ -56,8 +72,7 @@
return kmType;
}
- static DexType toRenamedType(
- DexType type, AppView<AppInfoWithLiveness> appView, NamingLens lens) {
+ private DexType toRenamedType(DexType type) {
// For library or classpath class, synthesize @Metadata always.
// For a program class, make sure it is live.
if (!appView.appInfo().isNonProgramTypeOrLiveProgramType(type)) {
@@ -71,141 +86,92 @@
return renamedType;
}
- static String toRenamedBinaryName(
- DexType type, AppView<AppInfoWithLiveness> appView, NamingLens lens) {
- DexType renamedType = toRenamedType(type, appView, lens);
+ String toRenamedBinaryName(DexType type) {
+ DexType renamedType = toRenamedType(type);
if (renamedType == null) {
return null;
}
return getBinaryNameFromDescriptor(renamedType.toDescriptorString());
}
- static String toRenamedClassifier(
- DexType type, AppView<AppInfoWithLiveness> appView, NamingLens lens) {
- // E.g., V -> kotlin/Unit, J -> kotlin/Long, [J -> kotlin/LongArray
- if (appView.dexItemFactory().kotlin.knownTypeConversion.containsKey(type)) {
- DexType convertedType = appView.dexItemFactory().kotlin.knownTypeConversion.get(type);
- assert convertedType != null;
- return descriptorToKotlinClassifier(convertedType.toDescriptorString());
- }
- // E.g., [Ljava/lang/String; -> kotlin/Array
- if (type.isArrayType()) {
- return NAME + "/Array";
- }
- DexType renamedType = toRenamedType(type, appView, lens);
- if (renamedType == null) {
+ String toRenamedClassifier(DexType type) {
+ type = type.isClassType() ? toRenamedType(type) : type;
+ if (type == null) {
return null;
}
- return descriptorToKotlinClassifier(renamedType.toDescriptorString());
+ return toClassifier(type, appView.dexItemFactory());
}
// TODO(b/148654451): Canonicalization?
- static KmType toRenamedKmType(
+ KmType toRenamedKmType(
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);
+ KotlinTypeInfo originalKotlinTypeInfo,
+ ClassTypeSignatureToRenamedKmTypeConverter converter) {
+ if (originalKotlinTypeInfo != null && originalKotlinTypeInfo.isTypeAlias()) {
+ KmType kmType = new KmType(flagsOf());
+ kmType.visitTypeAlias(originalKotlinTypeInfo.asTypeAlias().getName());
+ return kmType;
}
+ return toRenamedKmTypeWithClassifier(type, typeSignature, converter);
+ }
- String classifier = toRenamedClassifier(type, appView, lens);
+ private KmType toRenamedKmTypeWithClassifierForFieldSignature(
+ DexType type,
+ FieldTypeSignature fieldSignature,
+ ClassTypeSignatureToRenamedKmTypeConverter converter) {
+ if (fieldSignature.isClassTypeSignature()) {
+ ClassTypeSignature classTypeSignature = fieldSignature.asClassTypeSignature();
+ if (classTypeSignature.type() != type) {
+ return null;
+ }
+ return classTypeSignature.convert(converter);
+ }
+ // If we fail to set kmType.classifier we will get a UninitializedPropertyAccessException:
+ // lateinit property classifier has not been initialized.
+ Box<KmType> kmTypeBox = new Box<>();
+ populateKmTypeFromSignature(
+ fieldSignature,
+ () -> {
+ KmType value = new KmType(flagsOf());
+ kmTypeBox.set(value);
+ return value;
+ },
+ converter.getTypeParameters(),
+ appView.dexItemFactory());
+ return kmTypeBox.get();
+ }
+
+ private KmType toRenamedKmTypeWithClassifier(
+ DexType type,
+ TypeSignature typeSignature,
+ ClassTypeSignatureToRenamedKmTypeConverter converter) {
+ if (typeSignature != null && typeSignature.isFieldTypeSignature()) {
+ KmType renamedKmType =
+ toRenamedKmTypeWithClassifierForFieldSignature(
+ type, typeSignature.asFieldTypeSignature(), converter);
+ if (renamedKmType != null) {
+ return renamedKmType;
+ }
+ }
+ String classifier = toRenamedClassifier(type);
if (classifier == null) {
return null;
}
- // TODO(b/151194869): Mysterious, why attempts to properly set flags bothers kotlinc?
- // and/or why wiping out flags works for KmType but not KmFunction?!
- KmType kmType = new KmType(flagsOf());
- kmType.visitClass(classifier);
+ // Seems like no flags for KmType are ever set, thus passing in flagsOf() seems ok.
+ KmType renamedKmType = new KmType(flagsOf());
+ renamedKmType.visitClass(classifier);
// TODO(b/151194164): 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/151194164): for TypeVariableSignature, there is KmType::visitTypeParameter.
- return result;
- }
-
- @Override
- public KmType visitInnerTypeSignature(ClassTypeSignature innerTypeSignature, KmType result) {
- // Do nothing
- return result;
- }
- }
-
- private static KmType setRenamedKmType(
- DexType type,
- TypeSignature signature,
- AppView<AppInfoWithLiveness> appView,
- NamingLens lens,
- Consumer<KmType> consumer) {
- KmType renamedKmType = toRenamedKmType(type, signature, appView, lens);
- if (renamedKmType != null) {
- consumer.accept(renamedKmType);
+ KmType argumentType = toRenamedKmTypeWithClassifier(elementType, null, converter);
+ renamedKmType.getArguments().add(new KmTypeProjection(KmVariance.OUT, argumentType));
}
return renamedKmType;
}
- static KmConstructor toRenamedKmConstructor(
- DexEncodedMethod method,
- AppView<AppInfoWithLiveness> appView,
- NamingLens lens) {
+ KmConstructor toRenamedKmConstructor(DexEncodedMethod method) {
// Make sure it is an instance initializer and live.
if (!method.isInstanceInitializer()
|| !appView.appInfo().liveMethods.contains(method.method)) {
@@ -214,17 +180,23 @@
KmConstructor kmConstructor = new KmConstructor(method.accessFlags.getAsKotlinFlags());
JvmExtensionsKt.setSignature(kmConstructor, toJvmMethodSignature(method.method));
MethodTypeSignature signature = GenericSignature.Parser.toMethodTypeSignature(method, appView);
+ List<KmTypeParameter> typeParameters =
+ convertFormalTypeParameters(
+ signature.getFormalTypeParameters(),
+ kmTypeParameter -> {
+ assert false : "KmConstructor cannot have additional type parameters";
+ });
+ ClassTypeSignatureToRenamedKmTypeConverter converter =
+ new ClassTypeSignatureToRenamedKmTypeConverter(
+ appView, typeParameters, this::toRenamedClassifier);
List<KmValueParameter> parameters = kmConstructor.getValueParameters();
- if (!populateKmValueParameters(parameters, method, signature, appView, lens)) {
+ if (!populateKmValueParameters(method, signature, parameters, converter)) {
return null;
}
return kmConstructor;
}
- static KmFunction toRenamedKmFunction(
- DexEncodedMethod method,
- AppView<AppInfoWithLiveness> appView,
- NamingLens lens) {
+ KmFunction toRenamedKmFunction(DexEncodedMethod method) {
// For library overrides, synthesize @Metadata always.
// For regular methods, make sure it is live or pinned.
if (!method.isLibraryMethodOverride().isTrue()
@@ -238,9 +210,11 @@
: method.toSourceString() + " -> " + renamedMethod.toSourceString();
// TODO(b/151194869): Should we keep kotlin-specific flags only while synthesizing the base
// value from general JVM flags?
+ KotlinFunctionInfo kotlinMemberInfo = method.getKotlinMemberInfo().asFunctionInfo();
+ assert kotlinMemberInfo != null;
int flag =
- appView.appInfo().isPinned(method.method) && method.getKotlinMemberInfo() != null
- ? method.getKotlinMemberInfo().flags
+ appView.appInfo().isPinned(method.method)
+ ? kotlinMemberInfo.flags
: method.accessFlags.getAsKotlinFlags();
KmFunction kmFunction = new KmFunction(flag, renamedMethod.name.toString());
JvmExtensionsKt.setSignature(kmFunction, toJvmMethodSignature(renamedMethod));
@@ -249,41 +223,50 @@
// 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);
+ List<KmTypeParameter> typeParameters = kmFunction.getTypeParameters();
+ ClassTypeSignatureToRenamedKmTypeConverter converter =
+ new ClassTypeSignatureToRenamedKmTypeConverter(
+ appView,
+ convertFormalTypeParameters(signature.getFormalTypeParameters(), typeParameters::add),
+ this::toRenamedClassifier);
DexProto proto = method.method.proto;
DexType returnType = proto.returnType;
TypeSignature returnSignature = signature.returnType().typeSignature();
- KmType kmReturnType = setRenamedKmType(
- returnType, returnSignature, appView, lens, kmFunction::setReturnType);
+ KmType kmReturnType =
+ toRenamedKmType(returnType, returnSignature, kotlinMemberInfo.returnType, converter);
if (kmReturnType == null) {
return null;
}
-
+ kmFunction.setReturnType(kmReturnType);
if (method.isKotlinExtensionFunction()) {
- assert proto.parameters.values.length > 0
- : method.method.toSourceString();
+ assert proto.parameters.values.length > 0 : method.method.toSourceString();
DexType receiverType = proto.parameters.values[0];
TypeSignature receiverSignature = signature.getParameterTypeSignature(0);
- KmType kmReceiverType = setRenamedKmType(
- receiverType, receiverSignature, appView, lens, kmFunction::setReceiverParameterType);
+ KmType kmReceiverType = toRenamedKmType(receiverType, receiverSignature, null, converter);
if (kmReceiverType == null) {
return null;
}
+ kmFunction.setReceiverParameterType(kmReceiverType);
}
- List<KmValueParameter> parameters = kmFunction.getValueParameters();
- if (!populateKmValueParameters(parameters, method, signature, appView, lens)) {
+ if (!populateKmValueParameters(method, signature, kmFunction.getValueParameters(), converter)) {
return null;
}
return kmFunction;
}
- private static boolean populateKmValueParameters(
- List<KmValueParameter> parameters,
+ private List<KmTypeParameter> convertFormalTypeParameters(
+ List<FormalTypeParameter> parameters, Consumer<KmTypeParameter> addedFromParameters) {
+ return KotlinMetadataSynthesizerUtils.convertFormalTypeParameters(
+ classTypeParameters, parameters, appView.dexItemFactory(), addedFromParameters);
+ }
+
+ private boolean populateKmValueParameters(
DexEncodedMethod method,
MethodTypeSignature signature,
- AppView<AppInfoWithLiveness> appView,
- NamingLens lens) {
+ List<KmValueParameter> parameters,
+ ClassTypeSignatureToRenamedKmTypeConverter converter) {
KotlinFunctionInfo kotlinFunctionInfo = method.getKotlinMemberInfo().asFunctionInfo();
if (kotlinFunctionInfo == null) {
return false;
@@ -298,12 +281,7 @@
TypeSignature parameterTypeSignature = signature.getParameterTypeSignature(i);
KmValueParameter kmValueParameter =
toRewrittenKmValueParameter(
- valueParameterInfo,
- parameterType,
- parameterTypeSignature,
- parameterName,
- appView,
- lens);
+ valueParameterInfo, parameterType, parameterTypeSignature, parameterName, converter);
if (kmValueParameter == null) {
return false;
}
@@ -312,26 +290,28 @@
return true;
}
- private static KmValueParameter toRewrittenKmValueParameter(
+ private KmValueParameter toRewrittenKmValueParameter(
KotlinValueParameterInfo valueParameterInfo,
DexType parameterType,
TypeSignature parameterTypeSignature,
String candidateParameterName,
- AppView<AppInfoWithLiveness> appView,
- NamingLens lens) {
+ ClassTypeSignatureToRenamedKmTypeConverter converter) {
int flag = valueParameterInfo != null ? valueParameterInfo.flag : flagsOf();
String name = valueParameterInfo != null ? valueParameterInfo.name : candidateParameterName;
KmValueParameter kmValueParameter = new KmValueParameter(flag, name);
- KmType kmParamType = setRenamedKmType(
- parameterType, parameterTypeSignature, appView, lens, kmValueParameter::setType);
+ KotlinTypeInfo originalKmTypeInfo = valueParameterInfo != null ? valueParameterInfo.type : null;
+ KmType kmParamType =
+ toRenamedKmType(parameterType, parameterTypeSignature, originalKmTypeInfo, converter);
if (kmParamType == null) {
return null;
}
+ kmValueParameter.setType(kmParamType);
if (valueParameterInfo != null) {
JvmExtensionsKt.getAnnotations(kmParamType).addAll(valueParameterInfo.annotations);
}
if (valueParameterInfo != null && valueParameterInfo.isVararg) {
+ // TODO(b/152389234): Test for arrays in varargs.
if (!parameterType.isArrayType()) {
return null;
}
@@ -339,11 +319,11 @@
TypeSignature elementSignature =
parameterTypeSignature != null
? parameterTypeSignature.toArrayElementTypeSignature(appView) : null;
- KmType kmElementType = setRenamedKmType(
- elementType, elementSignature, appView, lens, kmValueParameter::setVarargElementType);
+ KmType kmElementType = toRenamedKmType(elementType, elementSignature, null, converter);
if (kmElementType == null) {
return null;
}
+ kmValueParameter.setVarargElementType(kmElementType);
}
return kmValueParameter;
@@ -375,58 +355,65 @@
* getter, and so on.
*/
static class KmPropertyGroup {
+
final int flags;
final String name;
final DexEncodedField field;
- final DexEncodedMethod getter;
- final int getterFlags;
- final DexEncodedMethod setter;
- final int setterFlags;
+ private final DexEncodedMethod getter;
+ private final KotlinPropertyInfo getterInfo;
+ private final DexEncodedMethod setter;
+ private final KotlinPropertyInfo setterInfo;
final DexEncodedMethod annotations;
final boolean isExtension;
+ private final List<KmTypeParameter> classTypeParameters;
private KmPropertyGroup(
int flags,
String name,
DexEncodedField field,
DexEncodedMethod getter,
- int getterFlags,
+ KotlinPropertyInfo getterInfo,
DexEncodedMethod setter,
- int setterFlags,
+ KotlinPropertyInfo setterInfo,
DexEncodedMethod annotations,
- boolean isExtension) {
+ boolean isExtension,
+ List<KmTypeParameter> classTypeParameters) {
this.flags = flags;
this.name = name;
this.field = field;
this.getter = getter;
- this.getterFlags = getterFlags;
+ this.getterInfo = getterInfo;
this.setter = setter;
- this.setterFlags = setterFlags;
+ this.setterInfo = setterInfo;
this.annotations = annotations;
this.isExtension = isExtension;
+ this.classTypeParameters = classTypeParameters;
}
- static Builder builder(int flags, String name) {
- return new Builder(flags, name);
+ static Builder builder(int flags, String name, List<KmTypeParameter> classTypeParameters) {
+ return new Builder(flags, name, classTypeParameters);
}
static class Builder {
+
private final int flags;
private final String name;
private DexEncodedField field;
private DexEncodedMethod getter;
- private int getterFlags;
+ private KotlinPropertyInfo getterInfo;
private DexEncodedMethod setter;
- private int setterFlags;
+ private KotlinPropertyInfo setterInfo;
private DexEncodedMethod annotations;
+ private List<KmTypeParameter> classTypeParameters;
private boolean isExtensionGetter;
private boolean isExtensionSetter;
private boolean isExtensionAnnotations;
- private Builder(int flags, String name) {
+ private Builder(int flags, String name, List<KmTypeParameter> classTypeParameters) {
this.flags = flags;
this.name = name;
+ this.classTypeParameters = classTypeParameters;
}
Builder foundBackingField(DexEncodedField field) {
@@ -434,15 +421,15 @@
return this;
}
- Builder foundGetter(DexEncodedMethod getter, int flags) {
+ Builder foundGetter(DexEncodedMethod getter, KotlinPropertyInfo propertyInfo) {
this.getter = getter;
- this.getterFlags = flags;
+ this.getterInfo = propertyInfo;
return this;
}
- Builder foundSetter(DexEncodedMethod setter, int flags) {
+ Builder foundSetter(DexEncodedMethod setter, KotlinPropertyInfo propertyInfo) {
this.setter = setter;
- this.setterFlags = flags;
+ this.setterInfo = propertyInfo;
return this;
}
@@ -481,178 +468,234 @@
}
}
return new KmPropertyGroup(
- flags, name, field, getter, getterFlags, setter, setterFlags, annotations, isExtension);
+ flags,
+ name,
+ field,
+ getter,
+ getterInfo,
+ setter,
+ setterInfo,
+ annotations,
+ isExtension,
+ classTypeParameters);
}
}
- KmProperty toRenamedKmProperty(AppView<AppInfoWithLiveness> appView, NamingLens lens) {
- KmProperty kmProperty = new KmProperty(flags, name, flagsOf(), flagsOf());
- KmType kmPropertyType = null;
- KmType kmReceiverType = null;
-
- // A flag to indicate we can rename the property name. This will become false if any member
- // is pinned. Then, we conservatively assume that users want the property to be pinned too.
- // That is, we won't rename the property even though some other members could be renamed.
- boolean canChangePropertyName = true;
- // A candidate property name. Not overwritten by the following members, hence the order of
- // preference: a backing field, getter, and setter.
- String renamedPropertyName = name;
- if (field != null) {
- if (appView.appInfo().isPinned(field.field)) {
- canChangePropertyName = false;
+ private String setFieldForProperty(
+ KmProperty kmProperty,
+ KmType defaultPropertyType,
+ AppView<AppInfoWithLiveness> appView,
+ NamingLens lens,
+ KotlinMetadataSynthesizer synthesizer) {
+ DexField renamedField = lens.lookupField(field.field, appView.dexItemFactory());
+ ClassTypeSignatureToRenamedKmTypeConverter converter =
+ new ClassTypeSignatureToRenamedKmTypeConverter(
+ appView, this.classTypeParameters, synthesizer::toRenamedClassifier);
+ if (kmProperty.getReturnType() == defaultPropertyType) {
+ KmType kmPropertyType =
+ synthesizer.toRenamedKmType(field.field.type, null, null, converter);
+ if (kmPropertyType != null) {
+ kmProperty.setReturnType(kmPropertyType);
}
- DexField renamedField = lens.lookupField(field.field, appView.dexItemFactory());
- if (canChangePropertyName && renamedField.name != field.field.name) {
- renamedPropertyName = renamedField.name.toString();
- }
- FieldTypeSignature signature =
- GenericSignature.Parser.toFieldTypeSignature(field, appView);
- kmPropertyType =
- setRenamedKmType(field.field.type, signature, appView, lens, kmProperty::setReturnType);
- JvmExtensionsKt.setFieldSignature(kmProperty, toJvmFieldSignature(renamedField));
}
+ JvmExtensionsKt.setFieldSignature(kmProperty, toJvmFieldSignature(renamedField));
+ return appView.appInfo().isPinned(field.field) ? null : renamedField.name.toString();
+ }
- GetterSetterCriteria criteria = checkGetterCriteria();
- if (criteria == GetterSetterCriteria.VIOLATE) {
- return null;
+ private boolean setReceiverParameterTypeForExtensionProperty(
+ KmProperty kmProperty,
+ DexEncodedMethod method,
+ AppView<AppInfoWithLiveness> appView,
+ KotlinMetadataSynthesizer synthesizer) {
+ if (!isExtension) {
+ return true;
}
+ MethodTypeSignature signature =
+ GenericSignature.Parser.toMethodTypeSignature(method, appView);
+ List<KmTypeParameter> typeParameters =
+ KotlinMetadataSynthesizerUtils.convertFormalTypeParameters(
+ classTypeParameters,
+ signature.getFormalTypeParameters(),
+ appView.dexItemFactory(),
+ kmTypeParameter -> {});
+ ClassTypeSignatureToRenamedKmTypeConverter converter =
+ new ClassTypeSignatureToRenamedKmTypeConverter(
+ appView, typeParameters, synthesizer::toRenamedClassifier);
+ DexType receiverType = method.method.proto.parameters.values[0];
+ TypeSignature receiverSignature = signature.getParameterTypeSignature(0);
+ KmType kmReceiverType =
+ synthesizer.toRenamedKmType(receiverType, receiverSignature, null, converter);
+ if (kmProperty.getReceiverParameterType() != null) {
+ // If the receiver type for the extension property is set already make sure it's consistent.
+ return getDescriptorFromKmType(kmReceiverType)
+ .equals(getDescriptorFromKmType(kmProperty.getReceiverParameterType()));
+ }
+ kmProperty.setReceiverParameterType(kmReceiverType);
+ return true;
+ }
- if (criteria == GetterSetterCriteria.MET) {
- assert getter != null && getter.method.proto.parameters.size() == (isExtension ? 1 : 0)
- : "checkGetterCriteria: " + this.toString();
- MethodTypeSignature signature =
- GenericSignature.Parser.toMethodTypeSignature(getter, appView);
- if (isExtension) {
- TypeSignature receiverSignature = signature.getParameterTypeSignature(0);
- kmReceiverType =
- setRenamedKmType(
- getter.method.proto.parameters.values[0],
- receiverSignature,
- appView,
- lens,
- kmProperty::setReceiverParameterType);
- }
-
- DexType returnType = getter.method.proto.returnType;
- TypeSignature returnSignature = signature.returnType().typeSignature();
- if (kmPropertyType == null) {
- // The property type is not set yet.
- kmPropertyType = setRenamedKmType(
- returnType, returnSignature, appView, lens, kmProperty::setReturnType);
- } else {
- // If property type is set already (via backing field), make sure it's consistent.
- KmType kmPropertyTypeFromGetter =
- toRenamedKmType(returnType, returnSignature, appView, lens);
- if (!getDescriptorFromKmType(kmPropertyType)
- .equals(getDescriptorFromKmType(kmPropertyTypeFromGetter))) {
- return null;
- }
- }
- if (appView.appInfo().isPinned(getter.method)) {
- canChangePropertyName = false;
- }
- DexMethod renamedGetter = lens.lookupMethod(getter.method, appView.dexItemFactory());
- if (canChangePropertyName
- && renamedGetter.name != getter.method.name
- && renamedPropertyName.equals(name)) {
- renamedPropertyName = renamedGetter.name.toString();
- }
- kmProperty.setGetterFlags(getterFlags);
- JvmExtensionsKt.setGetterSignature(kmProperty, toJvmMethodSignature(renamedGetter));
- } else if (field != null) {
+ private String setGetterForProperty(
+ KmProperty kmProperty,
+ KmType defaultPropertyType,
+ AppView<AppInfoWithLiveness> appView,
+ NamingLens lens,
+ KotlinMetadataSynthesizer synthesizer) {
+ if (checkGetterCriteria() == GetterSetterCriteria.NOT_AVAILABLE) {
// Property without getter.
// Even though a getter does not exist, `kotlinc` still set getter flags and use them to
// determine when to direct field access v.s. getter calls for property resolution.
- kmProperty.setGetterFlags(field.accessFlags.getAsKotlinFlags());
+ if (field != null) {
+ kmProperty.setGetterFlags(field.accessFlags.getAsKotlinFlags());
+ }
+ return kmProperty.getName();
}
+ assert checkGetterCriteria() == GetterSetterCriteria.MET;
+ assert getter != null;
+ DexProto proto = getter.method.proto;
+ assert proto.parameters.size() == (isExtension ? 1 : 0)
+ : "checkGetterCriteria: " + this.toString();
+ MethodTypeSignature signature =
+ GenericSignature.Parser.toMethodTypeSignature(getter, appView);
+ List<KmTypeParameter> typeParameters =
+ KotlinMetadataSynthesizerUtils.convertFormalTypeParameters(
+ this.classTypeParameters,
+ signature.getFormalTypeParameters(),
+ appView.dexItemFactory(),
+ kmTypeParameter -> {});
+ ClassTypeSignatureToRenamedKmTypeConverter converter =
+ new ClassTypeSignatureToRenamedKmTypeConverter(
+ appView, typeParameters, synthesizer::toRenamedClassifier);
+ DexType returnType = proto.returnType;
+ TypeSignature returnSignature = signature.returnType().typeSignature();
+ KmType kmPropertyType =
+ synthesizer.toRenamedKmType(
+ returnType, returnSignature, getterInfo.returnType, converter);
+ if (kmProperty.getReturnType() == defaultPropertyType) {
+ // The property type is not set yet.
+ kmProperty.setReturnType(kmPropertyType);
+ } else if (!getDescriptorFromKmType(kmPropertyType)
+ .equals(getDescriptorFromKmType(kmProperty.getReturnType()))) {
+ // If property type is set already (via backing field), make sure it's consistent.
+ return null;
+ }
+ DexMethod renamedGetter = lens.lookupMethod(getter.method, appView.dexItemFactory());
+ kmProperty.setGetterFlags(getter.accessFlags.getAsKotlinFlags());
+ JvmExtensionsKt.setGetterSignature(kmProperty, toJvmMethodSignature(renamedGetter));
+ return appView.appInfo().isPinned(getter.method) ? null : renamedGetter.name.toString();
+ }
- criteria = checkSetterCriteria();
+ private String setSetterForProperty(
+ KmProperty kmProperty,
+ KmType defaultPropertyType,
+ AppView<AppInfoWithLiveness> appView,
+ NamingLens lens,
+ KotlinMetadataSynthesizer synthesizer) {
+ GetterSetterCriteria criteria = checkSetterCriteria();
if (criteria == GetterSetterCriteria.VIOLATE) {
- return null;
+ return kmProperty.getName();
}
-
- if (criteria == GetterSetterCriteria.MET) {
- assert setter != null && setter.method.proto.parameters.size() == (isExtension ? 2 : 1)
- : "checkSetterCriteria: " + this.toString();
- MethodTypeSignature signature =
- GenericSignature.Parser.toMethodTypeSignature(setter, appView);
- if (isExtension) {
- DexType receiverType = setter.method.proto.parameters.values[0];
- TypeSignature receiverSignature = signature.getParameterTypeSignature(0);
- if (kmReceiverType == null) {
- kmReceiverType =
- setRenamedKmType(
- receiverType,
- receiverSignature,
- appView,
- lens,
- kmProperty::setReceiverParameterType);
- } else {
- // If the receiver type for the extension property is set already (via getter),
- // make sure it's consistent.
- KmType kmReceiverTypeFromSetter =
- toRenamedKmType(receiverType, receiverSignature, appView, lens);
- if (!getDescriptorFromKmType(kmReceiverType)
- .equals(getDescriptorFromKmType(kmReceiverTypeFromSetter))) {
- return null;
- }
- }
+ if (criteria == GetterSetterCriteria.NOT_AVAILABLE) {
+ if (field != null && IS_VAR.invoke(flags)) {
+ // Editable property without setter.
+ // Even though a setter does not exist, `kotlinc` still set setter flags and use them to
+ // determine when to direct field access v.s. setter calls for property resolution.
+ kmProperty.setSetterFlags(field.accessFlags.getAsKotlinFlags());
}
-
- 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 =
- setRenamedKmType(valueType, valueSignature, appView, lens, kmProperty::setReturnType);
- } else {
- // If property type is set already (via either backing field or getter),
- // make sure it's consistent.
- KmType kmPropertyTypeFromSetter =
- toRenamedKmType(valueType, valueSignature, appView, lens);
- if (!getDescriptorFromKmType(kmPropertyType)
- .equals(getDescriptorFromKmType(kmPropertyTypeFromSetter))) {
- return null;
- }
- }
- assert setter.getKotlinMemberInfo().isPropertyInfo();
- KotlinValueParameterInfo valueParameterInfo =
- setter.getKotlinMemberInfo().asPropertyInfo().valueParameterInfo;
- KmValueParameter kmValueParameter = toRewrittenKmValueParameter(
- valueParameterInfo, valueType, valueSignature, "value", appView, lens);
- if (kmValueParameter != null) {
- kmProperty.setSetterParameter(kmValueParameter);
- }
- if (appView.appInfo().isPinned(setter.method)) {
- canChangePropertyName = false;
- }
- DexMethod renamedSetter = lens.lookupMethod(setter.method, appView.dexItemFactory());
- if (canChangePropertyName
- && renamedSetter.name != setter.method.name
- && renamedPropertyName.equals(name)) {
- renamedPropertyName = renamedSetter.name.toString();
- }
- kmProperty.setSetterFlags(setterFlags);
- JvmExtensionsKt.setSetterSignature(kmProperty, toJvmMethodSignature(renamedSetter));
- } else if (field != null && IS_VAR.invoke(flags)) {
- // Editable property without setter.
- // Even though a setter does not exist, `kotlinc` still set setter flags and use them to
- // determine when to direct field access v.s. setter calls for property resolution.
- kmProperty.setGetterFlags(field.accessFlags.getAsKotlinFlags());
+ return kmProperty.getName();
}
-
- // If the property type remains null at the end, bail out to synthesize this property.
- if (kmPropertyType == null) {
- return null;
+ assert criteria == GetterSetterCriteria.MET;
+ assert setter != null;
+ DexProto proto = setter.method.proto;
+ assert proto.parameters.size() == (isExtension ? 2 : 1)
+ : "checkSetterCriteria: " + this.toString();
+ MethodTypeSignature signature =
+ GenericSignature.Parser.toMethodTypeSignature(setter, appView);
+ List<KmTypeParameter> typeParameters =
+ KotlinMetadataSynthesizerUtils.convertFormalTypeParameters(
+ classTypeParameters,
+ signature.getFormalTypeParameters(),
+ appView.dexItemFactory(),
+ kmTypeParameter -> {});
+ ClassTypeSignatureToRenamedKmTypeConverter converter =
+ new ClassTypeSignatureToRenamedKmTypeConverter(
+ appView, typeParameters, synthesizer::toRenamedClassifier);
+ int valueIndex = isExtension ? 1 : 0;
+ DexType valueType = proto.parameters.values[valueIndex];
+ TypeSignature valueSignature = signature.getParameterTypeSignature(valueIndex);
+ KmType kmPropertyType =
+ synthesizer.toRenamedKmType(valueType, valueSignature, setterInfo.returnType, converter);
+ if (kmProperty.getReturnType() == defaultPropertyType) {
+ // The property type is not set yet.
+ kmProperty.setReturnType(kmPropertyType);
+ } else {
+ // If property type is set already make sure it's consistent.
+ if (!getDescriptorFromKmType(kmPropertyType)
+ .equals(getDescriptorFromKmType(kmProperty.getReturnType()))) {
+ return null;
+ }
}
- // For extension property, if the receiver type remains null at the end, bail out too.
- if (isExtension && kmReceiverType == null) {
- return null;
+ assert setter.getKotlinMemberInfo().isPropertyInfo();
+ KotlinValueParameterInfo valueParameterInfo =
+ setter.getKotlinMemberInfo().asPropertyInfo().valueParameterInfo;
+ KmValueParameter kmValueParameter =
+ synthesizer.toRewrittenKmValueParameter(
+ valueParameterInfo, valueType, valueSignature, "value", converter);
+ if (kmValueParameter != null) {
+ kmProperty.setSetterParameter(kmValueParameter);
+ }
+ DexMethod renamedSetter = lens.lookupMethod(setter.method, appView.dexItemFactory());
+ kmProperty.setSetterFlags(setterInfo.setterFlags);
+ JvmExtensionsKt.setSetterSignature(kmProperty, toJvmMethodSignature(renamedSetter));
+ return appView.appInfo().isPinned(setter.method) ? null : renamedSetter.name.toString();
+ }
+
+ KmProperty toRenamedKmProperty(KotlinMetadataSynthesizer synthesizer) {
+ AppView<AppInfoWithLiveness> appView = synthesizer.appView;
+ NamingLens lens = synthesizer.lens;
+ KmProperty kmProperty = new KmProperty(flags, name, flagsOf(), flagsOf());
+
+ // Set default values
+ KmType defaultPropertyType = new KmType(flagsOf());
+ kmProperty.setReturnType(defaultPropertyType);
+
+ String renamedPropertyName = name;
+ if (getter != null) {
+ if (checkGetterCriteria() == GetterSetterCriteria.VIOLATE) {
+ return null;
+ }
+ if (!setReceiverParameterTypeForExtensionProperty(
+ kmProperty, getter, appView, synthesizer)) {
+ return null;
+ }
+ renamedPropertyName =
+ setGetterForProperty(kmProperty, defaultPropertyType, appView, lens, synthesizer);
+ }
+ if (setter != null) {
+ if (checkSetterCriteria() == GetterSetterCriteria.VIOLATE) {
+ return null;
+ }
+ if (!setReceiverParameterTypeForExtensionProperty(
+ kmProperty, setter, appView, synthesizer)) {
+ return null;
+ }
+ String renamedName =
+ setSetterForProperty(kmProperty, defaultPropertyType, appView, lens, synthesizer);
+ if (renamedPropertyName != null) {
+ renamedPropertyName = renamedName;
+ }
+ }
+ // Setting the property type from the field has to be done after the getter, otherwise we
+ // may potentially loose type-argument information.
+ if (field != null) {
+ String renamedName =
+ setFieldForProperty(kmProperty, defaultPropertyType, appView, lens, synthesizer);
+ if (renamedPropertyName != null) {
+ renamedPropertyName = renamedName;
+ }
}
// Rename the property name if and only if none of participating members is pinned, and
// any of them is indeed renamed (to a new name).
- if (canChangePropertyName && !renamedPropertyName.equals(name)) {
+ if (renamedPropertyName != null) {
kmProperty.setName(renamedPropertyName);
}
return kmProperty;
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizerUtils.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizerUtils.java
new file mode 100644
index 0000000..9382cd9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizerUtils.java
@@ -0,0 +1,174 @@
+// 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 static com.android.tools.r8.kotlin.Kotlin.NAME;
+import static com.android.tools.r8.utils.DescriptorUtils.descriptorToKotlinClassifier;
+import static kotlinx.metadata.FlagsKt.flagsOf;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GenericSignature.ArrayTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter;
+import com.android.tools.r8.graph.GenericSignature.TypeVariableSignature;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import kotlinx.metadata.KmTypeParameter;
+import kotlinx.metadata.KmTypeVisitor;
+import kotlinx.metadata.KmVariance;
+
+class KotlinMetadataSynthesizerUtils {
+
+ static void populateKmTypeFromSignature(
+ FieldTypeSignature typeSignature,
+ Supplier<KmTypeVisitor> typeVisitor,
+ List<KmTypeParameter> allTypeParameters,
+ DexItemFactory factory) {
+ if (typeSignature.isClassTypeSignature()) {
+ populateKmTypeFromClassTypeSignature(
+ typeSignature.asClassTypeSignature(), typeVisitor, allTypeParameters, factory);
+ } else if (typeSignature.isArrayTypeSignature()) {
+ populateKmTypeFromArrayTypeSignature(
+ typeSignature.asArrayTypeSignature(), typeVisitor, allTypeParameters, factory);
+ } else {
+ assert typeSignature.isTypeVariableSignature();
+ populateKmTypeFromTypeVariableSignature(
+ typeSignature.asTypeVariableSignature(), typeVisitor, allTypeParameters);
+ }
+ }
+
+ private static void populateKmTypeFromTypeVariableSignature(
+ TypeVariableSignature typeSignature,
+ Supplier<KmTypeVisitor> visitor,
+ List<KmTypeParameter> allTypeParameters) {
+ for (KmTypeParameter typeParameter : allTypeParameters) {
+ if (typeParameter
+ .getName()
+ .equals(typeSignature.asTypeVariableSignature().getTypeVariable())) {
+ visitor.get().visitTypeParameter(typeParameter.getId());
+ return;
+ }
+ }
+ }
+
+ private static void populateKmTypeFromArrayTypeSignature(
+ ArrayTypeSignature typeSignature,
+ Supplier<KmTypeVisitor> visitor,
+ List<KmTypeParameter> allTypeParameters,
+ DexItemFactory factory) {
+ ArrayTypeSignature arrayTypeSignature = typeSignature.asArrayTypeSignature();
+ if (!arrayTypeSignature.elementSignature().isFieldTypeSignature()) {
+ return;
+ }
+ KmTypeVisitor kmType = visitor.get();
+ kmType.visitClass(NAME + "/Array");
+ populateKmTypeFromSignature(
+ arrayTypeSignature.elementSignature().asFieldTypeSignature(),
+ () -> kmType.visitArgument(flagsOf(), KmVariance.INVARIANT),
+ allTypeParameters,
+ factory);
+ }
+
+ private static void populateKmTypeFromClassTypeSignature(
+ ClassTypeSignature typeSignature,
+ Supplier<KmTypeVisitor> visitor,
+ List<KmTypeParameter> allTypeParameters,
+ DexItemFactory factory) {
+ // No need to record the trivial argument.
+ if (factory.objectType == typeSignature.type()) {
+ return;
+ }
+ KmTypeVisitor kmType = visitor.get();
+ kmType.visitClass(toClassifier(typeSignature.type(), factory));
+ for (FieldTypeSignature typeArgument : typeSignature.typeArguments()) {
+ populateKmTypeFromSignature(
+ typeArgument,
+ () -> kmType.visitArgument(flagsOf(), KmVariance.INVARIANT),
+ allTypeParameters,
+ factory);
+ }
+ }
+
+ static String toClassifier(DexType type, DexItemFactory factory) {
+ // E.g., V -> kotlin/Unit, J -> kotlin/Long, [J -> kotlin/LongArray
+ if (factory.kotlin.knownTypeConversion.containsKey(type)) {
+ DexType convertedType = factory.kotlin.knownTypeConversion.get(type);
+ assert convertedType != null;
+ return descriptorToKotlinClassifier(convertedType.toDescriptorString());
+ }
+ // E.g., [Ljava/lang/String; -> kotlin/Array
+ if (type.isArrayType()) {
+ return NAME + "/Array";
+ }
+ return descriptorToKotlinClassifier(type.toDescriptorString());
+ }
+
+ /**
+ * Utility method building up all type-parameters from {@code classTypeParameters} combined with
+ * {@code parameters}, where the consumer {@code addedFromParameters} is called for every
+ * conversion of {@Code FormalTypeParameter} to {@Code KmTypeParameter}.
+ *
+ * <pre>
+ * classTypeParameters: [KmTypeParameter(T), KmTypeParameter(S)]
+ * parameters: [FormalTypeParameter(R)]
+ * result: [KmTypeParameter(T), KmTypeParameter(S), KmTypeParameter(R)].
+ * </pre>
+ *
+ * @param classTypeParameters
+ * @param parameters
+ * @param factory
+ * @param addedFromParameters
+ * @return
+ */
+ static List<KmTypeParameter> convertFormalTypeParameters(
+ List<KmTypeParameter> classTypeParameters,
+ List<FormalTypeParameter> parameters,
+ DexItemFactory factory,
+ Consumer<KmTypeParameter> addedFromParameters) {
+ if (parameters.isEmpty()) {
+ return classTypeParameters;
+ }
+ ImmutableList.Builder<KmTypeParameter> builder = ImmutableList.builder();
+ builder.addAll(classTypeParameters);
+ int idCounter = classTypeParameters.size();
+ // Assign type-variables ids to names. All generic signatures has been minified at this point,
+ // but it may be that type-variables are used before we can see them (is that allowed?).
+ for (FormalTypeParameter parameter : parameters) {
+ KmTypeParameter element =
+ new KmTypeParameter(flagsOf(), parameter.getName(), idCounter++, KmVariance.INVARIANT);
+ builder.add(element);
+ addedFromParameters.accept(element);
+ }
+ idCounter = 0;
+ ImmutableList<KmTypeParameter> allTypeParameters = builder.build();
+ for (FormalTypeParameter parameter : parameters) {
+ KmTypeParameter kmTypeParameter =
+ allTypeParameters.get(classTypeParameters.size() + idCounter++);
+ visitUpperBound(parameter.getClassBound(), allTypeParameters, kmTypeParameter, factory);
+ if (parameter.getInterfaceBounds() != null) {
+ for (FieldTypeSignature interfaceBound : parameter.getInterfaceBounds()) {
+ visitUpperBound(interfaceBound, allTypeParameters, kmTypeParameter, factory);
+ }
+ }
+ }
+ return allTypeParameters;
+ }
+
+ private static void visitUpperBound(
+ FieldTypeSignature typeSignature,
+ List<KmTypeParameter> allTypeParameters,
+ KmTypeParameter parameter,
+ DexItemFactory factory) {
+ if (typeSignature.isUnknown()) {
+ return;
+ }
+ populateKmTypeFromSignature(
+ typeSignature, () -> parameter.visitUpperBound(flagsOf()), allTypeParameters, factory);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
new file mode 100644
index 0000000..130a660
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.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.kotlin;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import kotlinx.metadata.KmClassifier;
+import kotlinx.metadata.KmType;
+import kotlinx.metadata.KmTypeProjection;
+
+// Provides access to Kotlin information about a kotlin type.
+public class KotlinTypeInfo {
+
+ static final List<KotlinTypeProjectionInfo> EMPTY_ARGUMENTS = ImmutableList.of();
+
+ private final KmClassifier classifier;
+ private final List<KotlinTypeProjectionInfo> arguments;
+
+ private KotlinTypeInfo(KmClassifier classifier, List<KotlinTypeProjectionInfo> arguments) {
+ this.classifier = classifier;
+ this.arguments = arguments;
+ }
+
+ static KotlinTypeInfo create(KmType kmType) {
+ if (kmType == null) {
+ return null;
+ }
+ if (kmType.getArguments().isEmpty()) {
+ return new KotlinTypeInfo(kmType.classifier, EMPTY_ARGUMENTS);
+ }
+ ImmutableList.Builder<KotlinTypeProjectionInfo> arguments = new ImmutableList.Builder<>();
+ for (KmTypeProjection argument : kmType.getArguments()) {
+ arguments.add(KotlinTypeProjectionInfo.create(argument));
+ }
+ return new KotlinTypeInfo(kmType.classifier, arguments.build());
+ }
+
+ public boolean isTypeAlias() {
+ return classifier instanceof KmClassifier.TypeAlias;
+ }
+
+ public KmClassifier.TypeAlias asTypeAlias() {
+ return (KmClassifier.TypeAlias) classifier;
+ }
+
+ public List<KotlinTypeProjectionInfo> getArguments() {
+ return arguments;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java
new file mode 100644
index 0000000..3782bd8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java
@@ -0,0 +1,25 @@
+// 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.KmTypeProjection;
+import kotlinx.metadata.KmVariance;
+
+// Provides access to Kotlin information about the type projection of a type (arguments).
+public class KotlinTypeProjectionInfo {
+
+ final KmVariance variance;
+ final KotlinTypeInfo typeInfo;
+
+ private KotlinTypeProjectionInfo(KmVariance variance, KotlinTypeInfo typeInfo) {
+ this.variance = variance;
+ this.typeInfo = typeInfo;
+ }
+
+ static KotlinTypeProjectionInfo create(KmTypeProjection kmTypeProjection) {
+ return new KotlinTypeProjectionInfo(
+ kmTypeProjection.getVariance(), KotlinTypeInfo.create(kmTypeProjection.getType()));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
index fcf3db6..e358c50 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
@@ -19,15 +19,23 @@
final int flag;
// Indicates whether the formal parameter is originally `vararg`.
final boolean isVararg;
+ // Original information about the type.
+ final KotlinTypeInfo type;
+
// TODO(b/151194869): Should we treat them as normal annotations? E.g., shrinking and renaming?
// Annotations on the type of value parameter.
final List<KmAnnotation> annotations;
private KotlinValueParameterInfo(
- String name, int flag, boolean isVararg, List<KmAnnotation> annotations) {
+ String name,
+ int flag,
+ boolean isVararg,
+ KotlinTypeInfo type,
+ List<KmAnnotation> annotations) {
this.name = name;
this.flag = flag;
this.isVararg = isVararg;
+ this.type = type;
this.annotations = annotations;
}
@@ -40,6 +48,7 @@
kmValueParameter.getName(),
kmValueParameter.getFlags(),
kmValueParameter.getVarargElementType() != null,
+ KotlinTypeInfo.create(kmType),
kmType != null ? JvmExtensionsKt.getAnnotations(kmType) : ImmutableList.of());
}
}
diff --git a/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java b/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
index 582a282..82b6c13 100644
--- a/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
@@ -59,6 +59,7 @@
GenericSignatureTestClassCY.class,
GenericSignatureTestClassCYY.class)
.compile()
+ .disassemble()
.app;
AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(app);
DexItemFactory factory = appView.dexItemFactory();
@@ -306,7 +307,7 @@
class ZZ<TT> extends YY {
public YY yy;
- <R extends Y & I> YY newYY(GenericSignatureTestClassB... bs) {
+ <R extends I> YY newYY(GenericSignatureTestClassB... bs) {
return new YY();
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
index 07fbdd9..3d3d935 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
@@ -5,13 +5,11 @@
import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertNotEquals;
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.StringUtils;
import java.nio.file.Path;
import java.util.Collection;
@@ -25,7 +23,28 @@
@RunWith(Parameterized.class)
public class MetadataRewriteInTypeArgumentsTest extends KotlinMetadataTestBase {
private static final String EXPECTED =
- StringUtils.lines("42", "1", "42", "42", "1", "42", "42", "42", "1", "42");
+ StringUtils.lines(
+ "Hello World!",
+ "42",
+ "1",
+ "42",
+ "42",
+ "1",
+ "42",
+ "42",
+ "42",
+ "1",
+ "42",
+ "1",
+ "42",
+ "42",
+ "42",
+ "42",
+ "42",
+ "1",
+ "2",
+ "7",
+ "42");
private final TestParameters parameters;
@@ -50,6 +69,7 @@
Path typeAliasLibJar =
kotlinc(KOTLINC, targetVersion)
.addSourceFiles(getKotlinFileInTest(typeAliasLibFolder, "lib"))
+ .addSourceFiles(getKotlinFileInTest(typeAliasLibFolder, "lib_minified"))
.compile();
jarMap.put(targetVersion, typeAliasLibJar);
}
@@ -74,26 +94,37 @@
}
@Test
- public void testMetadataInTypeAlias_renamed() throws Exception {
+ public void testMetadataInTypeAlias_keepAll() throws Exception {
Path libJar =
testForR8(parameters.getBackend())
.addProgramFiles(jarMap.get(targetVersion))
.addKeepAllClassesRule()
+ .addKeepAttributes(
+ ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS,
+ ProguardKeepAttributes.SIGNATURE,
+ ProguardKeepAttributes.INNER_CLASSES,
+ ProguardKeepAttributes.ENCLOSING_METHOD)
.compile()
- // TODO(b/151925520): Add inspections when program compiles
+ // TODO(b/151925520): Add inspections when program compiles correctly.
+ // TODO(mkroghj): Also inspect the renaming of lib_minified
+ // (not now, but when program compiles correctly).
.writeToZip();
- ProcessResult kotlinTestCompileResult =
+ Path mainJar =
kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/typeargument_app", "main"))
- .setOutputPath(temp.newFolder().toPath())
- // TODO(b/151925520): update to just .compile() once fixed.
- .compileRaw();
- // TODO(b/151925520): should be able to compile!
- assertNotEquals(0, kotlinTestCompileResult.exitCode);
- assertThat(
- kotlinTestCompileResult.stderr,
- containsString("no type arguments expected for constructor Invariant()"));
+ .compile();
+
+ // TODO(b/152306391): Reified type-parameters are not flagged correctly.
+ testForJvm()
+ .addProgramFiles(mainJar)
+ .addProgramFiles(ToolHelper.getKotlinStdlibJar())
+ .addRunClasspathFiles(libJar)
+ .run(parameters.getRuntime(), PKG + ".typeargument_app.MainKt")
+ .assertFailureWithErrorThatMatches(
+ containsString(
+ "This function has a reified type parameter and thus can only be inlined at"
+ + " compilation time, not called directly"));
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_app/main.kt
index 7fc2b69..c8f4a72 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_app/main.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_app/main.kt
@@ -8,7 +8,10 @@
import com.android.tools.r8.kotlin.metadata.typeargument_lib.ContraVariant
import com.android.tools.r8.kotlin.metadata.typeargument_lib.Invariant
import com.android.tools.r8.kotlin.metadata.typeargument_lib.SomeClass
+import com.android.tools.r8.kotlin.metadata.typeargument_lib.asList
+import com.android.tools.r8.kotlin.metadata.typeargument_lib.asObfuscatedClass
import com.android.tools.r8.kotlin.metadata.typeargument_lib.unBoxAndBox
+import com.android.tools.r8.kotlin.metadata.typeargument_lib.unboxAndPutInBox
import com.android.tools.r8.kotlin.metadata.typeargument_lib.update
class SomeSubClass(val x : Int) : SomeClass(), Comparable<SomeClass> {
@@ -24,7 +27,7 @@
fun testInvariant() {
val subtype1 = SomeSubClass(42)
val subtype2 = SomeSubClass(1)
- val inv = Invariant<SomeSubClass>()
+ val inv = Invariant<SomeSubClass, String>("Hello World!")
println(inv.classGenerics(subtype1).x)
println(inv.funGenerics(subtype2).x)
println(inv.funGenericsWithUpperBound(subtype1).x)
@@ -43,6 +46,12 @@
fun testExtension() {
println(CoVariant(42).unBoxAndBox().t)
println(CoVariant(1).update(42).t)
+ println(CoVariant(1).unboxAndPutInBox(CoVariant(42)).t)
+ println(CoVariant(42).asList().t.get(0))
+ val asList = CoVariant(42).asList(1, 2)
+ println(asList.t.get(0))
+ println(asList.t.get(1))
+ println(CoVariant(7).asObfuscatedClass().t.get(0).get(0).x)
}
fun main() {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_lib/lib.kt
index 7c8b6cb..38234b4 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_lib/lib.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_lib/lib.kt
@@ -6,7 +6,11 @@
open class SomeClass
-class Invariant<T> {
+class Invariant<T, C> {
+
+ constructor(someValue : C) {
+ println(someValue)
+ }
fun classGenerics(t : T) : T {
return t
@@ -52,7 +56,6 @@
}
}
-
fun <T> CoVariant<T>.unBoxAndBox() : CoVariant<T> {
return CoVariant(this.t)
}
@@ -61,3 +64,24 @@
println(this.t)
return CoVariant(t)
}
+
+fun <T> CoVariant<T>.unboxAndPutInBox(box : CoVariant<T>) : CoVariant<T> {
+ println(this.t)
+ println(box.t)
+ return CoVariant(box.t)
+}
+
+inline fun <reified T> CoVariant<T>.asList() : CoVariant<Array<T>> {
+ println(this.t)
+ return CoVariant(arrayOf(this.t))
+}
+
+inline fun <reified T> CoVariant<T>.asList(vararg ts : T) : CoVariant<Array<out T>> {
+ println(this.t)
+ return CoVariant(ts)
+}
+
+fun <T> CoVariant<T>.asObfuscatedClass() : CoVariant<Array<Array<ClassThatWillBeObfuscated>>> {
+ println(this.t)
+ return CoVariant(arrayOf(arrayOf(ClassThatWillBeObfuscated(42))))
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_lib/lib_minified.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_lib/lib_minified.kt
new file mode 100644
index 0000000..78c3742
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/typeargument_lib/lib_minified.kt
@@ -0,0 +1,3 @@
+package com.android.tools.r8.kotlin.metadata.typeargument_lib
+
+class ClassThatWillBeObfuscated(val x : Int)