blob: 0b0039e6bd16bbe547dbf7a01dac9b2adad85b67 [file] [log] [blame]
// Copyright (c) 2021, 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.ir.desugar.desugaredlibrary;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexLibraryClass;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaring;
import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeterInstructionEventConsumer.DesugaredLibraryRetargeterPostProcessingEventConsumer;
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.collections.DexClassAndMethodSet;
import com.google.common.collect.Maps;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
// The rewrite of virtual calls requires to go through emulate dispatch. This class is responsible
// for inserting interfaces on library boundaries and forwarding methods in the program, and to
// synthesize the interfaces and emulated dispatch classes in the desugared library.
public class DesugaredLibraryRetargeterPostProcessor implements CfPostProcessingDesugaring {
private final AppView<?> appView;
private final DesugaredLibraryRetargeterSyntheticHelper syntheticHelper;
private final DexClassAndMethodSet emulatedDispatchMethods;
public DesugaredLibraryRetargeterPostProcessor(
AppView<?> appView, RetargetingInfo retargetingInfo) {
this.appView = appView;
this.syntheticHelper = new DesugaredLibraryRetargeterSyntheticHelper(appView);
emulatedDispatchMethods = retargetingInfo.getEmulatedDispatchMethods();
}
@Override
public void postProcessingDesugaring(CfPostProcessingDesugaringEventConsumer eventConsumer) {
if (appView.options().isDesugaredLibraryCompilation()) {
ensureEmulatedDispatchMethodsSynthesized(eventConsumer);
} else {
ensureInterfacesAndForwardingMethodsSynthesized(eventConsumer);
}
}
private void ensureInterfacesAndForwardingMethodsSynthesized(
DesugaredLibraryRetargeterPostProcessingEventConsumer eventConsumer) {
assert !appView.options().isDesugaredLibraryCompilation();
Map<DexType, List<DexClassAndMethod>> map = Maps.newIdentityHashMap();
for (DexClassAndMethod emulatedDispatchMethod : emulatedDispatchMethods) {
map.putIfAbsent(emulatedDispatchMethod.getHolderType(), new ArrayList<>(1));
map.get(emulatedDispatchMethod.getHolderType()).add(emulatedDispatchMethod);
}
for (DexProgramClass clazz : appView.appInfo().classes()) {
if (clazz.superType == null) {
assert clazz.type == appView.dexItemFactory().objectType : clazz.type.toSourceString();
continue;
}
DexClass superclass = appView.definitionFor(clazz.superType);
// Only performs computation if superclass is a library class, but not object to filter out
// the most common case.
if (superclass != null
&& superclass.isLibraryClass()
&& superclass.type != appView.dexItemFactory().objectType) {
map.forEach(
(type, methods) -> {
if (inherit(superclass.asLibraryClass(), type, emulatedDispatchMethods)) {
ensureInterfacesAndForwardingMethodsSynthesized(eventConsumer, clazz, methods);
}
});
}
}
}
private boolean inherit(
DexLibraryClass clazz, DexType typeToInherit, DexClassAndMethodSet retarget) {
DexLibraryClass current = clazz;
while (current.type != appView.dexItemFactory().objectType) {
if (current.type == typeToInherit) {
return true;
}
DexClass dexClass = appView.definitionFor(current.superType);
if (dexClass == null || dexClass.isClasspathClass()) {
reportInvalidLibrarySupertype(current, retarget);
return false;
} else if (dexClass.isProgramClass()) {
// If dexClass is a program class, then it is already correctly desugared.
return false;
}
current = dexClass.asLibraryClass();
}
return false;
}
private void ensureInterfacesAndForwardingMethodsSynthesized(
DesugaredLibraryRetargeterPostProcessingEventConsumer eventConsumer,
DexProgramClass clazz,
List<DexClassAndMethod> methods) {
// DesugaredLibraryRetargeter emulate dispatch: insertion of a marker interface & forwarding
// methods.
// We cannot use the ClassProcessor since this applies up to 26, while the ClassProcessor
// applies up to 24.
for (DexClassAndMethod method : methods) {
DexClass newInterface =
syntheticHelper.ensureEmulatedInterfaceDispatchMethod(method, eventConsumer);
if (clazz.interfaces.contains(newInterface.type)) {
// The class has already been desugared.
continue;
}
clazz.addExtraInterfaces(
Collections.singletonList(new ClassTypeSignature(newInterface.type)));
eventConsumer.acceptInterfaceInjection(clazz, newInterface);
if (clazz.lookupVirtualMethod(method.getReference()) == null) {
DexEncodedMethod newMethod = createForwardingMethod(method, clazz);
clazz.addVirtualMethod(newMethod);
eventConsumer.acceptForwardingMethod(new ProgramMethod(clazz, newMethod));
}
}
}
private DexEncodedMethod createForwardingMethod(DexClassAndMethod target, DexClass clazz) {
// NOTE: Never add a forwarding method to methods of classes unknown or coming from
// android.jar
// even if this results in invalid code, these classes are never desugared.
// In desugared library, emulated interface methods can be overridden by retarget lib members.
DexMethod forwardMethod =
appView.options().desugaredLibraryConfiguration.retargetMethod(target, appView);
assert forwardMethod != null && forwardMethod != target.getReference();
DexEncodedMethod desugaringForwardingMethod =
DexEncodedMethod.createDesugaringForwardingMethod(
target, clazz, forwardMethod, appView.dexItemFactory());
desugaringForwardingMethod.setLibraryMethodOverride(OptionalBool.TRUE);
return desugaringForwardingMethod;
}
private void ensureEmulatedDispatchMethodsSynthesized(
DesugaredLibraryRetargeterInstructionEventConsumer eventConsumer) {
assert appView.options().isDesugaredLibraryCompilation();
if (emulatedDispatchMethods.isEmpty()) {
return;
}
for (DexClassAndMethod emulatedDispatchMethod : emulatedDispatchMethods) {
syntheticHelper.ensureEmulatedHolderDispatchMethod(emulatedDispatchMethod, eventConsumer);
}
}
private void reportInvalidLibrarySupertype(
DexLibraryClass libraryClass, DexClassAndMethodSet retarget) {
DexClass dexClass = appView.definitionFor(libraryClass.superType);
String message;
if (dexClass == null) {
message = "missing";
} else if (dexClass.isClasspathClass()) {
message = "a classpath class";
} else {
message = "INVALID";
assert false;
}
appView
.options()
.warningInvalidLibrarySuperclassForDesugar(
dexClass == null ? libraryClass.getOrigin() : dexClass.getOrigin(),
libraryClass.type,
libraryClass.superType,
message,
retarget);
}
}