| // 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); |
| } |
| } |