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