blob: f83796f9a2c539b2c4b99339a926e1256256fa45 [file] [log] [blame]
// Copyright (c) 2019, 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.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationElement;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedAnnotation;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueArray;
import com.android.tools.r8.graph.DexValue.DexValueInt;
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ThreadUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import kotlinx.metadata.jvm.KotlinClassHeader;
public class KotlinMetadataRewriter {
private final AppView<AppInfoWithLiveness> appView;
private final NamingLens lens;
private final DexItemFactory factory;
private final Kotlin kotlin;
public KotlinMetadataRewriter(AppView<AppInfoWithLiveness> appView, NamingLens lens) {
this.appView = appView;
this.lens = lens;
this.factory = appView.dexItemFactory();
this.kotlin = factory.kotlin;
}
public static void removeKotlinMetadataFromRenamedClass(AppView<?> appView, DexType type) {
DexClass clazz = appView.definitionFor(type);
if (clazz == null) {
return;
}
removeKotlinMetadataFromRenamedClass(appView, clazz);
}
public static void removeKotlinMetadataFromRenamedClass(AppView<?> appView, DexClass clazz) {
// Remove @Metadata in DexAnnotation form if a class is renamed.
clazz.setAnnotations(clazz.annotations().keepIf(anno -> isNotKotlinMetadata(appView, anno)));
// Clear associated {@link KotlinInfo} to avoid accidentally deserialize it back to
// DexAnnotation we've just removed above.
if (clazz.isProgramClass()) {
clazz.asProgramClass().setKotlinInfo(null);
}
}
private static boolean isNotKotlinMetadata(AppView<?> appView, DexAnnotation annotation) {
return annotation.annotation.type
!= appView.dexItemFactory().kotlin.metadata.kotlinMetadataType;
}
public void run(ExecutorService executorService) throws ExecutionException {
// TODO(b/152283077): Don't disable the assert.
appView.appInfo().disableDefinitionForAssert();
ThreadUtils.processItems(
appView.appInfo().classes(),
clazz -> {
KotlinInfo<?> kotlinInfo = clazz.getKotlinInfo();
if (kotlinInfo != null) {
// If @Metadata is still associated, this class should not be renamed
// (by {@link ClassNameMinifier} of course).
// Or, we start maintaining @Metadata for renamed classes.
// TODO(b/151194540): if this option is settled down, this assertion is meaningless.
assert lens.lookupType(clazz.type, appView.dexItemFactory()) == clazz.type
|| appView.options().enableKotlinMetadataRewritingForRenamedClasses
: clazz.toSourceString()
+ " != "
+ lens.lookupType(clazz.type, appView.dexItemFactory());
DexAnnotation oldMeta =
clazz.annotations().getFirstMatching(kotlin.metadata.kotlinMetadataType);
// If @Metadata is already gone, e.g., by {@link AnnotationRemover} if type Metadata is
// determined as dead (e.g., due to no keep rule), nothing to do.
if (oldMeta == null) {
return;
}
kotlinInfo.rewrite(appView, lens);
DexAnnotation newMeta = createKotlinMetadataAnnotation(kotlinInfo.createHeader());
clazz.setAnnotations(
clazz.annotations().rewrite(anno -> anno == oldMeta ? newMeta : anno));
}
},
executorService);
appView.appInfo().enableDefinitionForAssert();
}
private DexAnnotation createKotlinMetadataAnnotation(KotlinClassHeader header) {
List<DexAnnotationElement> elements = new ArrayList<>();
elements.add(
new DexAnnotationElement(kotlin.metadata.kind, DexValueInt.create(header.getKind())));
elements.add(
new DexAnnotationElement(
kotlin.metadata.metadataVersion, createIntArray(header.getMetadataVersion())));
elements.add(
new DexAnnotationElement(
kotlin.metadata.bytecodeVersion, createIntArray(header.getBytecodeVersion())));
elements.add(
new DexAnnotationElement(kotlin.metadata.data1, createStringArray(header.getData1())));
elements.add(
new DexAnnotationElement(kotlin.metadata.data2, createStringArray(header.getData2())));
elements.add(
new DexAnnotationElement(
kotlin.metadata.extraString,
new DexValueString(factory.createString(header.getExtraString()))));
elements.add(
new DexAnnotationElement(
kotlin.metadata.packageName,
new DexValueString(factory.createString(header.getPackageName()))));
elements.add(
new DexAnnotationElement(
kotlin.metadata.extraInt, DexValueInt.create(header.getExtraInt())));
DexEncodedAnnotation encodedAnnotation =
new DexEncodedAnnotation(
kotlin.metadata.kotlinMetadataType, elements.toArray(DexAnnotationElement.EMPTY_ARRAY));
return new DexAnnotation(DexAnnotation.VISIBILITY_RUNTIME, encodedAnnotation);
}
private DexValueArray createIntArray(int[] data) {
DexValue[] values = new DexValue[data.length];
for (int i = 0; i < data.length; i++) {
values[i] = DexValueInt.create(data[i]);
}
return new DexValueArray(values);
}
private DexValueArray createStringArray(String[] data) {
DexValue[] values = new DexValue[data.length];
for (int i = 0; i < data.length; i++) {
values[i] = new DexValueString(factory.createString(data[i]));
}
return new DexValueArray(values);
}
}