| // 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.retargeter; |
| |
| 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.retargeter.DesugaredLibraryRetargeterSynthesizerEventConsumer.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.Collection; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.ExecutionException; |
| import java.util.concurrent.ExecutorService; |
| |
| // 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( |
| Collection<DexProgramClass> programClasses, |
| CfPostProcessingDesugaringEventConsumer eventConsumer, |
| ExecutorService executorService) |
| throws ExecutionException { |
| assert !appView.options().isDesugaredLibraryCompilation(); |
| ensureInterfacesAndForwardingMethodsSynthesized(programClasses, eventConsumer); |
| } |
| |
| private void ensureInterfacesAndForwardingMethodsSynthesized( |
| Collection<DexProgramClass> programClasses, |
| 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 : programClasses) { |
| 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. |
| if (appView.isAlreadyLibraryDesugared(clazz)) { |
| return; |
| } |
| for (DexClassAndMethod method : methods) { |
| DexClass newInterface = |
| syntheticHelper.ensureEmulatedInterfaceDispatchMethod(method, eventConsumer); |
| if (clazz.interfaces.contains(newInterface.type)) { |
| // The class has already been desugared. |
| continue; |
| } |
| if (appView |
| .options() |
| .desugaredLibrarySpecification |
| .getDontRetargetLibMember() |
| .contains(clazz.getType())) { |
| 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().desugaredLibrarySpecification.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 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); |
| } |
| } |