blob: 92b8c206fede86b1a593cbb1f2767ad7c54bf50a [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.KotlinMetadataUtils.getKotlinLocalOrAnonymousNameFromDescriptor;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.kotlin.Kotlin.ClassClassifiers;
import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
import com.android.tools.r8.utils.DescriptorUtils;
import java.util.function.Consumer;
/**
* To account for invalid type references in kotlin metadata, the class KotlinTypeReference will
* either hold a DexType reference, or a String, with the original name reference, which is not a
* valid jvm descriptor/name. The values will be disjoint.
*/
class KotlinTypeReference implements EnqueuerMetadataTraceable {
private final DexType known;
private final String originalName;
// A KotlinTypeReference may be either a Kotlin classifier or a binary name.
// We need to maintain the right type to avoid unnecessary difference between original and
// rewritten metadata.
private final boolean exportAsKotlinClassifier;
private KotlinTypeReference(
String originalName, DexType known, boolean exportAsKotlinClassifier) {
this.originalName = originalName;
this.known = known;
this.exportAsKotlinClassifier = exportAsKotlinClassifier;
assert known != null;
}
private KotlinTypeReference(String originalName) {
this.known = null;
this.exportAsKotlinClassifier = false;
this.originalName = originalName;
assert originalName != null;
}
public DexType getKnown() {
return known;
}
public String getOriginalName() {
return originalName;
}
static KotlinTypeReference fromBinaryNameOrKotlinClassifier(
String binaryNameOrKotlinClassifier, DexItemFactory factory, String originalName) {
// Kotlin classifiers are valid binary names.
if (DescriptorUtils.isValidBinaryName(binaryNameOrKotlinClassifier)) {
boolean interpretAsKotlinClassifier =
shouldBeInterpretedAsKotlinClassifier(binaryNameOrKotlinClassifier);
return fromDescriptor(
interpretAsKotlinClassifier
? DescriptorUtils.getDescriptorFromKotlinClassifier(binaryNameOrKotlinClassifier)
: DescriptorUtils.getDescriptorFromClassBinaryName(binaryNameOrKotlinClassifier),
factory,
originalName,
interpretAsKotlinClassifier);
}
return new KotlinTypeReference(binaryNameOrKotlinClassifier);
}
private static boolean shouldBeInterpretedAsKotlinClassifier(
String binaryNameOrKotlinClassifier) {
return binaryNameOrKotlinClassifier.contains(".");
}
static KotlinTypeReference fromDescriptor(String descriptor, DexItemFactory factory) {
return fromDescriptor(descriptor, factory, descriptor, false);
}
static KotlinTypeReference fromDescriptor(
String descriptor,
DexItemFactory factory,
String originalName,
boolean exportAsKotlinClassifier) {
if (DescriptorUtils.isDescriptor(descriptor)) {
DexType type = factory.createType(descriptor);
return new KotlinTypeReference(originalName, type, exportAsKotlinClassifier);
}
return new KotlinTypeReference(originalName);
}
boolean toRenamedDescriptorOrDefault(
Consumer<String> rewrittenConsumer,
AppView<?> appView,
String defaultValue) {
if (known == null) {
rewrittenConsumer.accept(originalName);
return false;
}
DexType rewrittenType = toRewrittenTypeOrNull(appView, known);
if (rewrittenType == null) {
String knownDescriptor = known.toDescriptorString();
// Static known kotlin types can be pruned without rewriting to Any since the types are known
// by kotlinc and kotlin reflect.
if (ClassClassifiers.kotlinStaticallyKnownTypes.contains(knownDescriptor)) {
rewrittenConsumer.accept(knownDescriptor);
return false;
} else {
rewrittenConsumer.accept(defaultValue);
return true;
}
}
String renamedString = appView.getNamingLens().lookupDescriptor(rewrittenType).toString();
rewrittenConsumer.accept(renamedString);
return !known.toDescriptorString().equals(renamedString);
}
String toKotlinClassifier(boolean isLocalOrAnonymous) {
if (known == null) {
return originalName;
}
return getKotlinLocalOrAnonymousNameFromDescriptor(
known.toDescriptorString(), isLocalOrAnonymous);
}
boolean toRenamedBinaryNameOrDefault(
Consumer<String> rewrittenConsumer,
AppView<?> appView,
String defaultValue) {
if (known == null) {
// Unknown values are always on the input form, so we can just return it.
rewrittenConsumer.accept(originalName);
return false;
}
return toRenamedDescriptorOrDefault(
descriptor -> {
// We assume that the default value passed in is already a binary name.
if (descriptor == null || descriptor.equals(defaultValue)) {
rewrittenConsumer.accept(descriptor);
} else {
String rewritten =
exportAsKotlinClassifier
? DescriptorUtils.descriptorToKotlinClassifier(descriptor)
: DescriptorUtils.getBinaryNameFromDescriptor(descriptor);
rewrittenConsumer.accept(rewritten);
}
},
appView,
defaultValue);
}
private static DexType toRewrittenTypeOrNull(AppView<?> appView, DexType type) {
if (type.isArrayType()) {
DexType rewrittenBaseType =
toRewrittenTypeOrNull(appView, type.toBaseType(appView.dexItemFactory()));
return rewrittenBaseType != null
? type.replaceBaseType(rewrittenBaseType, appView.dexItemFactory())
: null;
}
if (!type.isClassType()) {
return type;
}
DexType rewrittenType =
appView.graphLens().lookupClassType(type, appView.getKotlinMetadataLens());
if (appView.appInfo().hasLiveness()
&& !appView.withLiveness().appInfo().isNonProgramTypeOrLiveProgramType(rewrittenType)) {
return null;
}
return rewrittenType;
}
@Override
public String toString() {
return known != null ? known.descriptor.toString() : originalName;
}
@Override
public void trace(DexDefinitionSupplier definitionSupplier) {
if (known != null && known.isClassType()) {
// Lookup the definition, ignoring the result. This populates the sets in the Enqueuer.
definitionSupplier.contextIndependentDefinitionFor(known);
}
}
public DexType rewriteType(GraphLens graphLens, GraphLens codeLens) {
if (known != null && known.isClassType()) {
return graphLens.lookupClassType(known, codeLens);
}
return null;
}
}