| // 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.errors.Unreachable; |
| 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.DexValueType; |
| import com.android.tools.r8.graph.lens.GraphLens; |
| import com.android.tools.r8.utils.ArrayUtils; |
| import com.android.tools.r8.utils.ThreadUtils; |
| 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; |
| 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; |
| } |
| } |