blob: 685e733298d90083e50021becd0a9f28676e2d92 [file] [log] [blame]
// 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().registerInstructionsReferences(targetExtractor);
DexMethod target = targetExtractor.getTarget();
InvokeKind kind = targetExtractor.getKind();
if (target != null &&
target.proto == method.method.proto) {
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;
}
}