| // 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.errors.Unimplemented; |
| import com.android.tools.r8.graph.DexClass; |
| import com.android.tools.r8.graph.DexEncodedMethod; |
| import com.android.tools.r8.graph.DexField; |
| import com.android.tools.r8.graph.DexMethod; |
| import com.android.tools.r8.graph.DexType; |
| import com.android.tools.r8.graph.GraphLense; |
| import com.android.tools.r8.ir.code.Invoke.Type; |
| import com.android.tools.r8.logging.Log; |
| import com.android.tools.r8.optimize.InvokeSingleTargetExtractor.InvokeKind; |
| import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness; |
| import java.util.IdentityHashMap; |
| import java.util.Map; |
| |
| public class BridgeMethodAnalysis { |
| |
| private final GraphLense lense; |
| private final AppInfoWithLiveness appInfo; |
| private final Map<DexMethod, DexMethod> bridgeTargetToBridgeMap = new IdentityHashMap<>(); |
| |
| public BridgeMethodAnalysis(GraphLense lense, AppInfoWithLiveness appInfo) { |
| this.lense = lense; |
| this.appInfo = appInfo; |
| } |
| |
| public GraphLense run() { |
| for (DexClass clazz : appInfo.classes()) { |
| clazz.forEachMethod(this::identifyBridgeMethod); |
| } |
| return new BridgeLense(lense, bridgeTargetToBridgeMap); |
| } |
| |
| private void identifyBridgeMethod(DexEncodedMethod method) { |
| // The tree pruner can mark bridge methods abstract if they are not reachable but cannot |
| // be removed. |
| if (method.accessFlags.isBridge() && !method.accessFlags.isAbstract()) { |
| InvokeSingleTargetExtractor targetExtractor = |
| new InvokeSingleTargetExtractor(appInfo.dexItemFactory); |
| method.getCode().registerCodeReferences(targetExtractor); |
| DexMethod target = targetExtractor.getTarget(); |
| InvokeKind kind = targetExtractor.getKind(); |
| if (target != null && target.getArity() == method.method.getArity()) { |
| assert !method.accessFlags.isPrivate() && !method.accessFlags.isConstructor(); |
| if (kind == InvokeKind.STATIC) { |
| assert method.accessFlags.isStatic(); |
| DexMethod actualTarget = lense.lookupMethod(target, method, Type.STATIC).getMethod(); |
| DexEncodedMethod targetMethod = appInfo.lookupStaticTarget(actualTarget); |
| if (targetMethod != null) { |
| addForwarding(method, targetMethod); |
| } |
| } else if (kind == InvokeKind.VIRTUAL) { |
| // TODO(herhut): Add support for bridges with multiple targets. |
| DexMethod actualTarget = lense.lookupMethod(target, method, Type.VIRTUAL).getMethod(); |
| DexEncodedMethod targetMethod = appInfo.lookupSingleVirtualTarget(actualTarget); |
| if (targetMethod != null) { |
| addForwarding(method, targetMethod); |
| } |
| } |
| } |
| } |
| } |
| |
| private void addForwarding(DexEncodedMethod method, DexEncodedMethod target) { |
| // This is a single target bridge we can inline. |
| if (Log.ENABLED) { |
| Log.info(getClass(), "Adding bridge forwarding %s -> %s.", method.method, |
| target.method); |
| } |
| // If we manage to rewrite all invocations, the bridge will be the only invocation of the target |
| // of the bridge and the target will get inlined. This should happen in most cases. For the few |
| // other cases, we might have inserted some extra checkcast instructions for the return type. |
| bridgeTargetToBridgeMap.put(target.method, method.method); |
| } |
| |
| |
| private static class BridgeLense extends GraphLense { |
| |
| private final GraphLense previousLense; |
| private final Map<DexMethod, DexMethod> bridgeTargetToBridgeMap; |
| |
| private BridgeLense(GraphLense previousLense, |
| Map<DexMethod, DexMethod> bridgeTargetToBridgeMap) { |
| this.previousLense = previousLense; |
| this.bridgeTargetToBridgeMap = bridgeTargetToBridgeMap; |
| } |
| |
| @Override |
| public DexField getOriginalFieldSignature(DexField field) { |
| return previousLense.getOriginalFieldSignature(field); |
| } |
| |
| @Override |
| public DexMethod getOriginalMethodSignature(DexMethod method) { |
| // TODO(b/79143143): implement this when re-enable bridge analysis. |
| throw new Unimplemented("BridgeLense.getOriginalMethodSignature() not implemented"); |
| } |
| |
| @Override |
| public DexField getRenamedFieldSignature(DexField originalField) { |
| return previousLense.getRenamedFieldSignature(originalField); |
| } |
| |
| @Override |
| public DexMethod getRenamedMethodSignature(DexMethod originalMethod) { |
| // TODO(b/79143143): implement this when re-enabling bridge analysis. |
| throw new Unimplemented("BridgeLense.getRenamedMethodSignature() not implemented"); |
| } |
| |
| @Override |
| public DexType lookupType(DexType type) { |
| return previousLense.lookupType(type); |
| } |
| |
| @Override |
| public GraphLenseLookupResult lookupMethod( |
| DexMethod method, DexEncodedMethod context, Type type) { |
| GraphLenseLookupResult previous = previousLense.lookupMethod(method, context, type); |
| DexMethod bridge = bridgeTargetToBridgeMap.get(previous.getMethod()); |
| // Do not forward calls from a bridge method to itself while the bridge method is still |
| // a bridge. |
| if (bridge == null || (context.accessFlags.isBridge() && bridge == context.method)) { |
| return previous; |
| } |
| return new GraphLenseLookupResult(bridge, type); |
| } |
| |
| @Override |
| public RewrittenPrototypeDescription lookupPrototypeChanges(DexMethod method) { |
| return previousLense.lookupPrototypeChanges(method); |
| } |
| |
| @Override |
| public DexField lookupField(DexField field) { |
| return previousLense.lookupField(field); |
| } |
| |
| @Override |
| public boolean isContextFreeForMethods() { |
| return false; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder builder = new StringBuilder(); |
| builder.append("------ BridgeMap ------").append(System.lineSeparator()); |
| for (Map.Entry<DexMethod, DexMethod> entry : bridgeTargetToBridgeMap.entrySet()) { |
| builder.append(entry.getKey().toSourceString()).append(" -> "); |
| builder.append(entry.getValue().toSourceString()).append(System.lineSeparator()); |
| } |
| builder.append("-----------------------").append(System.lineSeparator()); |
| builder.append(previousLense.toString()); |
| return builder.toString(); |
| } |
| } |
| } |