blob: b3e0eb461c538b00829741bac40cb16863cbed52 [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 static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO;
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.DexProgramClass;
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) {
// TODO(b/154294232): Seems like this should never be called. Either we explicitly keep the
// class and allow obfuscation - in which we should not remove it, or we should never have
// metadata in the first place.
DexProgramClass clazz = appView.definitionForProgramType(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(NO_KOTLIN_INFO);
}
}
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 -> {
KotlinClassLevelInfo kotlinInfo = clazz.getKotlinInfo();
DexAnnotation oldMeta =
clazz.annotations().getFirstMatching(kotlin.metadata.kotlinMetadataType);
if (kotlinInfo == NO_KOTLIN_INFO) {
// TODO(b/154346948): Track invalid meta-data objects such that we can enable this
// assert oldMeta == null;
return;
}
if (oldMeta != null) {
try {
KotlinClassHeader kotlinClassHeader = kotlinInfo.rewrite(clazz, appView, lens);
DexAnnotation newMeta = createKotlinMetadataAnnotation(kotlinClassHeader);
clazz.setAnnotations(
clazz.annotations().rewrite(anno -> anno == oldMeta ? newMeta : anno));
} catch (Throwable t) {
appView
.options()
.reporter
.warning(KotlinMetadataDiagnostic.unexpectedErrorWhenRewriting(clazz.type, t));
}
}
},
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);
}
}