| // 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.shaking; | |
| 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.DexEncodedAnnotation; | |
| import com.android.tools.r8.graph.DexEncodedField; | |
| import com.android.tools.r8.graph.DexEncodedMethod; | |
| import com.android.tools.r8.graph.DexField; | |
| import com.android.tools.r8.graph.DexProgramClass; | |
| import com.android.tools.r8.graph.DexReference; | |
| import com.android.tools.r8.graph.DexType; | |
| import com.android.tools.r8.graph.DexValue; | |
| import com.android.tools.r8.graph.DexValue.DexItemBasedValueString; | |
| import com.android.tools.r8.graph.DexValue.DexValueAnnotation; | |
| import com.android.tools.r8.graph.DexValue.DexValueArray; | |
| import com.android.tools.r8.graph.DexValue.DexValueEnum; | |
| import com.android.tools.r8.graph.DexValue.DexValueInt; | |
| import com.android.tools.r8.graph.DexValue.DexValueType; | |
| import com.android.tools.r8.graph.lens.GraphLens; | |
| import com.android.tools.r8.ir.optimize.enums.EnumDataMap; | |
| import com.android.tools.r8.utils.ThreadUtils; | |
| import com.android.tools.r8.utils.internal.ArrayUtils; | |
| import com.android.tools.r8.utils.internal.exceptions.Unreachable; | |
| import java.util.Collection; | |
| import java.util.concurrent.ExecutionException; | |
| import java.util.concurrent.ExecutorService; | |
| public class AnnotationFixer { | |
| private final AppView<?> appView; | |
| private final GraphLens lens; | |
| private final GraphLens annotationLens; | |
| public AnnotationFixer(AppView<?> appView, GraphLens lens) { | |
| this.appView = appView; | |
| this.lens = lens; | |
| this.annotationLens = appView.graphLens(); | |
| } | |
| private DexType lookupType(DexType type) { | |
| return lens.lookupType(type, annotationLens); | |
| } | |
| public void run(Collection<DexProgramClass> classes, ExecutorService executorService) | |
| throws ExecutionException { | |
| ThreadUtils.processItems( | |
| classes, this::processClass, appView.options().getThreadingModule(), executorService); | |
| } | |
| private void processClass(DexProgramClass clazz) { | |
| clazz.setAnnotations(clazz.annotations().rewrite(this::rewriteAnnotation)); | |
| clazz.forEachMethod(this::processMethod); | |
| clazz.forEachField(this::processField); | |
| } | |
| private void processMethod(DexEncodedMethod method) { | |
| method.rewriteAllAnnotations( | |
| (annotation, isParameterAnnotation) -> rewriteAnnotation(annotation)); | |
| } | |
| private void processField(DexEncodedField field) { | |
| field.setAnnotations(field.annotations().rewrite(this::rewriteAnnotation)); | |
| } | |
| private DexAnnotation rewriteAnnotation(DexAnnotation original) { | |
| return original.rewrite(this::rewriteEncodedAnnotation); | |
| } | |
| private DexEncodedAnnotation rewriteEncodedAnnotation(DexEncodedAnnotation original) { | |
| DexEncodedAnnotation rewritten = | |
| original.rewrite(this::lookupType, this::rewriteAnnotationElement); | |
| assert rewritten != null; | |
| return rewritten; | |
| } | |
| @SuppressWarnings("ReferenceEquality") | |
| private DexAnnotationElement rewriteAnnotationElement(DexAnnotationElement original) { | |
| DexValue rewrittenValue = rewriteComplexValue(original.value); | |
| if (rewrittenValue != original.value) { | |
| return new DexAnnotationElement(original.name, rewrittenValue); | |
| } | |
| return original; | |
| } | |
| @SuppressWarnings("ReferenceEquality") | |
| private DexValue rewriteComplexValue(DexValue value) { | |
| if (value.isDexValueArray()) { | |
| DexValue[] originalValues = value.asDexValueArray().getValues(); | |
| DexValue[] rewrittenValues = | |
| ArrayUtils.map(originalValues, this::rewriteComplexValue, DexValue.EMPTY_ARRAY); | |
| if (rewrittenValues != originalValues) { | |
| return new DexValueArray(rewrittenValues); | |
| } | |
| } else if (value.isDexValueAnnotation()) { | |
| DexValueAnnotation original = value.asDexValueAnnotation(); | |
| DexEncodedAnnotation rewritten = rewriteEncodedAnnotation(original.getValue()); | |
| if (original.value == rewritten) { | |
| return value; | |
| } | |
| return new DexValueAnnotation(rewritten); | |
| } | |
| return rewriteNestedValue(value); | |
| } | |
| @SuppressWarnings("ReferenceEquality") | |
| private DexValue rewriteNestedValue(DexValue value) { | |
| if (value.isDexItemBasedValueString()) { | |
| DexItemBasedValueString valueString = value.asDexItemBasedValueString(); | |
| DexReference original = valueString.value; | |
| DexReference rewritten = lens.getRenamedReference(original, annotationLens); | |
| if (original != rewritten) { | |
| return new DexItemBasedValueString(rewritten, valueString.getNameComputationInfo()); | |
| } | |
| } else if (value.isDexValueEnum()) { | |
| DexField original = value.asDexValueEnum().value; | |
| if (lens.isEnumUnboxerLens()) { | |
| EnumDataMap unboxedEnums = lens.asEnumUnboxerLens().getUnboxedEnums(); | |
| if (unboxedEnums.hasUnboxedValueFor(original)) { | |
| return DexValueInt.create(unboxedEnums.getUnboxedValue(original)); | |
| } | |
| } | |
| DexField rewritten = lens.lookupField(original, annotationLens); | |
| if (original != rewritten) { | |
| return new DexValueEnum(rewritten); | |
| } | |
| } else if (value.isDexValueField()) { | |
| throw new Unreachable("Unexpected field in annotation"); | |
| } else if (value.isDexValueMethod()) { | |
| throw new Unreachable("Unexpected method in annotation"); | |
| } else if (value.isDexValueMethodHandle()) { | |
| throw new Unreachable("Unexpected method handle in annotation"); | |
| } else if (value.isDexValueMethodType()) { | |
| throw new Unreachable("Unexpected method type in annotation"); | |
| } else if (value.isDexValueString()) { | |
| // If we identified references in the string it would be a DexItemBasedValueString. | |
| } else if (value.isDexValueType()) { | |
| DexType originalType = value.asDexValueType().value; | |
| DexType rewrittenType = lookupType(originalType); | |
| if (rewrittenType != originalType) { | |
| return new DexValueType(rewrittenType); | |
| } | |
| } else { | |
| // Assert that we have not forgotten a value. | |
| assert !value.isNestedDexValue(); | |
| } | |
| return value; | |
| } | |
| } |