| // Copyright (c) 2017, 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.optimize; |
| |
| import com.android.tools.r8.graph.AppInfoWithSubtyping; |
| import com.android.tools.r8.graph.DexApplication; |
| import com.android.tools.r8.graph.DexClass; |
| import com.android.tools.r8.graph.DexEncodedMethod; |
| import com.android.tools.r8.graph.DexMethod; |
| import com.android.tools.r8.graph.DexType; |
| import com.android.tools.r8.logging.Log; |
| import com.android.tools.r8.optimize.InvokeSingleTargetExtractor.InvokeKind; |
| import com.google.common.collect.Sets; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.stream.Collectors; |
| |
| public class VisibilityBridgeRemover { |
| private final AppInfoWithSubtyping appInfo; |
| private final DexApplication application; |
| private final Set<DexEncodedMethod> unneededVisibilityBridges = Sets.newIdentityHashSet(); |
| |
| public VisibilityBridgeRemover(AppInfoWithSubtyping appInfo, DexApplication application) { |
| this.appInfo = appInfo; |
| this.application = application; |
| } |
| |
| private void identifyBridgeMethod(DexEncodedMethod method) { |
| if (method.accessFlags.isBridge()) { |
| InvokeSingleTargetExtractor targetExtractor = new InvokeSingleTargetExtractor(); |
| method.getCode().registerCodeReferences(targetExtractor); |
| DexMethod target = targetExtractor.getTarget(); |
| InvokeKind kind = targetExtractor.getKind(); |
| // javac-generated visibility forward bridge method has same descriptor (name, signature and |
| // return type). |
| if (target != null && target.hasSameProtoAndName(method.method)) { |
| assert !method.accessFlags.isPrivate() && !method.accessFlags.isConstructor(); |
| if (kind == InvokeKind.SUPER) { |
| // This is a visibility forward, so check for the direct target. |
| DexEncodedMethod targetMethod |
| = appInfo.resolveMethod(target.getHolder(), target).asSingleTarget(); |
| if (targetMethod != null && targetMethod.accessFlags.isPublic()) { |
| if (Log.ENABLED) { |
| Log.info(getClass(), "Removing visibility forwarding %s -> %s", method.method, |
| targetMethod.method); |
| } |
| unneededVisibilityBridges.add(method); |
| } |
| } |
| } |
| } |
| } |
| |
| private void removeUnneededVisibilityBridges() { |
| Set<DexType> classes = unneededVisibilityBridges.stream() |
| .map(method -> method.method.getHolder()) |
| .collect(Collectors.toSet()); |
| for (DexType type : classes) { |
| DexClass clazz = appInfo.definitionFor(type); |
| clazz.setVirtualMethods(removeMethods(clazz.virtualMethods(), unneededVisibilityBridges)); |
| } |
| } |
| |
| private DexEncodedMethod[] removeMethods(DexEncodedMethod[] methods, |
| Set<DexEncodedMethod> removals) { |
| assert methods != null; |
| List<DexEncodedMethod> newMethods = Arrays.stream(methods) |
| .filter(method -> !removals.contains(method)) |
| .collect(Collectors.toList()); |
| assert newMethods.size() < methods.length; |
| return newMethods.toArray(new DexEncodedMethod[newMethods.size()]); |
| } |
| |
| public DexApplication run() { |
| for (DexClass clazz : appInfo.classes()) { |
| clazz.forEachMethod(this::identifyBridgeMethod); |
| } |
| if (!unneededVisibilityBridges.isEmpty()) { |
| removeUnneededVisibilityBridges(); |
| } |
| return application; |
| } |
| |
| } |