blob: 9382cd999300f7c010065b033bad2cee0060ccef [file] [log] [blame]
// 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);
}
}