Reland "Split BackportedMethodRewriter and RetargetLibraryMember"
Bug: 151703213
Change-Id: I21bbd379a1bea012e17f4d16c364f6534df5a328
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 100b5aa..b843181 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -34,7 +34,7 @@
import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.analysis.proto.GeneratedExtensionRegistryShrinker;
import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter;
import com.android.tools.r8.ir.desugar.NestedPrivateMethodLense;
import com.android.tools.r8.ir.desugar.R8NestBasedAccessDesugaring;
import com.android.tools.r8.ir.optimize.AssertionsRewriter;
@@ -278,7 +278,7 @@
MainDexListBuilder.checkForAssumedLibraryTypes(appView.appInfo());
}
if (!options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()) {
- BackportedMethodRewriter.checkForAssumedLibraryTypes(appView);
+ DesugaredLibraryRetargeter.checkForAssumedLibraryTypes(appView);
}
List<ProguardConfigurationRule> synthesizedProguardRules = new ArrayList<>();
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index 4b8c6d4..01c5298 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter;
import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring;
import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
import com.android.tools.r8.ir.optimize.ServiceLoaderRewriter;
@@ -44,8 +45,7 @@
private String toStringCache = null;
DexType(DexString descriptor) {
- assert !descriptor.toString().contains(".")
- : "Malformed descriptor: " + descriptor.toString();
+ assert !descriptor.toString().contains(".") : "Malformed descriptor: " + descriptor.toString();
this.descriptor = descriptor;
}
@@ -145,11 +145,12 @@
}
@Override
- public void collectIndexedItems(IndexedItemCollection collection,
- DexMethod method, int instructionOffset) {
+ public void collectIndexedItems(
+ IndexedItemCollection collection, DexMethod method, int instructionOffset) {
if (collection.addType(this)) {
- collection.getRenamedDescriptor(this).collectIndexedItems(collection, method,
- instructionOffset);
+ collection
+ .getRenamedDescriptor(this)
+ .collectIndexedItems(collection, method, instructionOffset);
}
}
@@ -272,6 +273,7 @@
|| name.contains(TwrCloseResourceRewriter.UTILITY_CLASS_NAME)
|| name.contains(NestBasedAccessDesugaring.NEST_CONSTRUCTOR_NAME)
|| name.contains(BackportedMethodRewriter.UTILITY_CLASS_NAME_PREFIX)
+ || name.contains(DesugaredLibraryRetargeter.DESUGAR_LIB_RETARGET_CLASS_NAME_PREFIX)
|| name.contains(ServiceLoaderRewriter.SERVICE_LOADER_CLASS_NAME)
|| oldSynthesizedName(name);
}
@@ -298,17 +300,17 @@
public int elementSizeForPrimitiveArrayType() {
assert isPrimitiveArrayType();
switch (descriptor.content[1]) {
- case 'Z': // boolean
- case 'B': // byte
+ case 'Z': // boolean
+ case 'B': // byte
return 1;
- case 'S': // short
- case 'C': // char
+ case 'S': // short
+ case 'C': // char
return 2;
- case 'I': // int
- case 'F': // float
+ case 'I': // int
+ case 'F': // float
return 4;
- case 'J': // long
- case 'D': // double
+ case 'J': // long
+ case 'D': // double
return 8;
default:
throw new Unreachable("Not array of primitives '" + descriptor + "'");
@@ -328,8 +330,11 @@
if (leadingSquareBrackets == 0) {
return this;
}
- DexString newDesc = dexItemFactory.createString(descriptor.size - leadingSquareBrackets,
- Arrays.copyOfRange(descriptor.content, leadingSquareBrackets, descriptor.content.length));
+ DexString newDesc =
+ dexItemFactory.createString(
+ descriptor.size - leadingSquareBrackets,
+ Arrays.copyOfRange(
+ descriptor.content, leadingSquareBrackets, descriptor.content.length));
return dexItemFactory.createType(newDesc);
}
@@ -364,8 +369,10 @@
public DexType toArrayElementType(DexItemFactory dexItemFactory) {
assert this.isArrayType();
- DexString newDesc = dexItemFactory.createString(descriptor.size - 1,
- Arrays.copyOfRange(descriptor.content, 1, descriptor.content.length));
+ DexString newDesc =
+ dexItemFactory.createString(
+ descriptor.size - 1,
+ Arrays.copyOfRange(descriptor.content, 1, descriptor.content.length));
return dexItemFactory.createType(newDesc);
}
@@ -376,7 +383,8 @@
if (lastSeparator == -1) {
return packagePart ? "" : descriptor.substring(1, descriptor.length() - 1);
} else {
- return packagePart ? descriptor.substring(1, lastSeparator)
+ return packagePart
+ ? descriptor.substring(1, lastSeparator)
: descriptor.substring(lastSeparator + 1, descriptor.length() - 1);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 4ac8995..4336f61 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -45,6 +45,7 @@
import com.android.tools.r8.ir.desugar.D8NestBasedAccessDesugaring;
import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter;
import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter.Mode;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter;
import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor;
import com.android.tools.r8.ir.desugar.LambdaRewriter;
@@ -143,6 +144,7 @@
private final InterfaceMethodRewriter interfaceMethodRewriter;
private final TwrCloseResourceRewriter twrCloseResourceRewriter;
private final BackportedMethodRewriter backportedMethodRewriter;
+ private final DesugaredLibraryRetargeter desugaredLibraryRetargeter;
private final LambdaMerger lambdaMerger;
private final ClassInliner classInliner;
private final ClassStaticizer classStaticizer;
@@ -217,13 +219,16 @@
.collect(Collectors.toList());
if (options.isDesugaredLibraryCompilation()) {
// Specific L8 Settings.
- // BackportedMethodRewriter is needed for retarget core library members and backports.
+ // DesugaredLibraryRetargeter is needed for retarget core library members and backports.
// InterfaceMethodRewriter is needed for emulated interfaces.
// LambdaRewriter is needed because if it is missing there are invoke custom on
// default/static interface methods, and this is not supported by the compiler.
// DesugaredLibraryAPIConverter is here to duplicate APIs.
// The rest is nulled out. In addition the rewriting logic fails without lambda rewriting.
- this.backportedMethodRewriter = new BackportedMethodRewriter(appView, this);
+ this.desugaredLibraryRetargeter =
+ options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()
+ ? null
+ : new DesugaredLibraryRetargeter(appView);
this.interfaceMethodRewriter =
options.desugaredLibraryConfiguration.getEmulateLibraryInterface().isEmpty()
? null
@@ -231,6 +236,10 @@
this.lambdaRewriter = new LambdaRewriter(appView);
this.desugaredLibraryAPIConverter =
new DesugaredLibraryAPIConverter(appView, Mode.GENERATE_CALLBACKS_AND_WRAPPERS);
+ this.backportedMethodRewriter =
+ options.testing.forceLibBackportsInL8CfToCf
+ ? new BackportedMethodRewriter(appView, this)
+ : null;
this.twrCloseResourceRewriter = null;
this.lambdaMerger = null;
this.covariantReturnTypeAnnotationTransformer = null;
@@ -268,6 +277,10 @@
? new TwrCloseResourceRewriter(appView, this)
: null;
this.backportedMethodRewriter = new BackportedMethodRewriter(appView, this);
+ this.desugaredLibraryRetargeter =
+ options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()
+ ? null
+ : new DesugaredLibraryRetargeter(appView);
this.covariantReturnTypeAnnotationTransformer =
options.processCovariantReturnTypeAnnotations
? new CovariantReturnTypeAnnotationTransformer(this, appView.dexItemFactory())
@@ -450,7 +463,16 @@
private void synthesizeJava8UtilityClass(
Builder<?> builder, ExecutorService executorService) throws ExecutionException {
- backportedMethodRewriter.synthesizeUtilityClasses(builder, executorService);
+ if (backportedMethodRewriter != null) {
+ backportedMethodRewriter.synthesizeUtilityClasses(builder, executorService);
+ }
+ }
+
+ private void synthesizeRetargetClass(Builder<?> builder, ExecutorService executorService)
+ throws ExecutionException {
+ if (desugaredLibraryRetargeter != null) {
+ desugaredLibraryRetargeter.synthesizeRetargetClasses(builder, executorService, this);
+ }
}
private void synthesizeEnumUnboxingUtilityClass(
@@ -482,6 +504,8 @@
desugarInterfaceMethods(builder, ExcludeDexResources, executor);
synthesizeTwrCloseResourceUtilityClass(builder, executor);
synthesizeJava8UtilityClass(builder, executor);
+ synthesizeRetargetClass(builder, executor);
+
processCovariantReturnTypeAnnotations(builder);
generateDesugaredLibraryAPIWrappers(builder, executor);
@@ -747,6 +771,7 @@
printPhase("Utility classes synthesis");
synthesizeTwrCloseResourceUtilityClass(builder, executorService);
synthesizeJava8UtilityClass(builder, executorService);
+ synthesizeRetargetClass(builder, executorService);
handleSynthesizedClassMapping(builder);
synthesizeEnumUnboxingUtilityClass(builder, executorService);
@@ -1370,9 +1395,20 @@
codeRewriter.rewriteThrowableAddAndGetSuppressed(code);
timing.end();
}
- timing.begin("Rewrite backport methods");
- backportedMethodRewriter.desugar(code);
- timing.end();
+
+ if (desugaredLibraryRetargeter != null) {
+ // The desugaredLibraryRetargeter should run before backportedMethodRewriter to be able to
+ // perform backport rewriting before the methods can be retargeted.
+ timing.begin("Retarget library methods");
+ desugaredLibraryRetargeter.desugar(code);
+ timing.end();
+ }
+
+ if (backportedMethodRewriter != null) {
+ timing.begin("Rewrite backport methods");
+ backportedMethodRewriter.desugar(code);
+ timing.end();
+ }
timing.begin("Desugar string concat");
stringConcatRewriter.desugarStringConcats(method.method, code);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 962170d..43db91d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -21,7 +21,6 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
-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.DexProgramClass.ChecksumSupplier;
@@ -31,7 +30,6 @@
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
-import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -54,13 +52,10 @@
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.DesugarState;
-import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.Timing;
-import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.IdentityHashMap;
@@ -113,15 +108,17 @@
if (androidApp != null) {
DexApplication app =
new ApplicationReader(androidApp, options, Timing.empty()).read(executor);
- appInfo =
- options.desugaredLibraryConfiguration.getRewritePrefix().isEmpty()
- ? new AppInfo(app)
- : new AppInfoWithClassHierarchy(app);
+ appInfo = new AppInfoWithClassHierarchy(app);
}
AppView<?> appView = AppView.createForD8(appInfo, options, rewritePrefix);
BackportedMethodRewriter.RewritableMethods rewritableMethods =
new BackportedMethodRewriter.RewritableMethods(options, appView);
rewritableMethods.visit(methods::add);
+ if (appInfo != null) {
+ DesugaredLibraryRetargeter desugaredLibraryRetargeter =
+ new DesugaredLibraryRetargeter(appView);
+ desugaredLibraryRetargeter.visit(methods::add);
+ }
return methods;
} catch (ExecutionException e) {
throw unwrapExecutionException(e);
@@ -146,49 +143,10 @@
&& !invoke.isInvokeStatic()) {
continue;
}
+
MethodProvider provider = getMethodProviderOrNull(invoke.getInvokedMethod());
if (provider == null) {
- if (!rewritableMethods.matchesVirtualRewrite(invoke.getInvokedMethod())) {
- continue;
- }
- // We need to force resolution, even on d8, to know if the invoke has to be rewritten.
- ResolutionResult resolutionResult =
- appView
- .appInfo()
- .resolveMethod(invoke.getInvokedMethod().holder, invoke.getInvokedMethod());
- if (resolutionResult.isFailedResolution()) {
- continue;
- }
- DexEncodedMethod singleTarget = resolutionResult.getSingleTarget();
- assert singleTarget != null;
- provider = getMethodProviderOrNull(singleTarget.method);
- if (provider == null) {
- continue;
- }
- }
-
- // Due to emulated dispatch, we have to rewrite invoke-super differently or we end up in
- // infinite loops. We do direct resolution. This is a very uncommon case.
- if (invoke.isInvokeSuper()
- && rewritableMethods.matchesVirtualRewrite(invoke.getInvokedMethod())) {
- DexEncodedMethod dexEncodedMethod =
- appView
- .appInfo()
- .withClassHierarchy()
- .lookupSuperTarget(invoke.getInvokedMethod(), code.method.method.holder);
- // Final methods can be rewritten as a normal invoke.
- if (dexEncodedMethod != null && !dexEncodedMethod.isFinal()) {
- DexMethod retargetMethod =
- appView
- .options()
- .desugaredLibraryConfiguration
- .retargetMethod(dexEncodedMethod.method, appView);
- if (retargetMethod != null) {
- iterator.replaceCurrentInstruction(
- new InvokeStatic(retargetMethod, invoke.outValue(), invoke.arguments()));
- }
- continue;
- }
+ continue;
}
provider.rewriteInvoke(invoke, iterator, code, appView, affectedValues);
@@ -222,11 +180,6 @@
if (!enabled) {
return;
}
- if (appView.options().isDesugaredLibraryCompilation()) {
- synthesizeEmulatedDispatchMethods(builder);
- } else {
- addInterfacesAndForwardingMethods(executorService);
- }
if (holders.isEmpty()) {
return;
}
@@ -243,8 +196,9 @@
}
}
- MethodAccessFlags flags = MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC, false);
+ MethodAccessFlags flags =
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC, false);
ClassAccessFlags classAccessFlags =
ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC);
// Generate the utility classes in a loop since utility classes can require the
@@ -262,7 +216,12 @@
Code code = provider.generateTemplateMethod(appView.options(), method);
DexEncodedMethod dexEncodedMethod =
new DexEncodedMethod(
- method, flags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), code, true);
+ method,
+ flags,
+ DexAnnotationSet.empty(),
+ ParameterAnnotationsList.empty(),
+ code,
+ true);
boolean addToMainDexList =
referencingClasses.stream()
.anyMatch(clazz -> appView.appInfo().isInMainDexList(clazz.type));
@@ -273,140 +232,27 @@
method.holder,
dexEncodedMethod,
"java8 methods utility class",
- addToMainDexList);
+ addToMainDexList,
+ appView);
// The following may add elements to methodsProviders.
converter.optimizeSynthesizedClass(utilityClass, executorService);
}
}
- private void addInterfacesAndForwardingMethods(ExecutorService executorService)
- throws ExecutionException {
- assert !appView.options().isDesugaredLibraryCompilation();
- Map<DexType, List<DexMethod>> map = Maps.newIdentityHashMap();
- for (DexMethod emulatedDispatchMethod : rewritableMethods.getEmulatedDispatchMethods()) {
- map.putIfAbsent(emulatedDispatchMethod.holder, new ArrayList<>(1));
- map.get(emulatedDispatchMethod.holder).add(emulatedDispatchMethod);
- }
- List<DexEncodedMethod> addedMethods = new ArrayList<>();
- for (DexProgramClass clazz : appView.appInfo().classes()) {
- if (clazz.superType == null) {
- assert clazz.type == appView.dexItemFactory().objectType : clazz.type.toSourceString();
- continue;
- }
- DexClass dexClass = appView.definitionFor(clazz.superType);
- // Only performs computation if superclass is a library class, but not object to filter out
- // the most common case.
- if (dexClass != null
- && dexClass.isLibraryClass()
- && dexClass.type != appView.dexItemFactory().objectType) {
- for (DexType dexType : map.keySet()) {
- if (inherit(dexClass.asLibraryClass(), dexType)) {
- addedMethods.addAll(addInterfacesAndForwardingMethods(clazz, map.get(dexType)));
- }
- }
- }
- }
- if (addedMethods.isEmpty()) {
- return;
- }
- converter.processMethodsConcurrently(addedMethods, executorService);
- }
-
- private boolean inherit(DexLibraryClass clazz, DexType typeToInherit) {
- DexLibraryClass current = clazz;
- while (current.type != appView.dexItemFactory().objectType) {
- if (current.type == typeToInherit) {
- return true;
- }
- current = appView.definitionFor(current.superType).asLibraryClass();
- }
- return false;
- }
-
- private List<DexEncodedMethod> addInterfacesAndForwardingMethods(
- DexProgramClass clazz, List<DexMethod> dexMethods) {
- // BackportedMethodRewriter 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.
- List<DexEncodedMethod> newForwardingMethods = new ArrayList<>();
- for (DexMethod dexMethod : dexMethods) {
- DexType[] newInterfaces = Arrays.copyOf(clazz.interfaces.values, clazz.interfaces.size() + 1);
- newInterfaces[newInterfaces.length - 1] =
- BackportedMethodRewriter.dispatchInterfaceTypeFor(appView, dexMethod);
- clazz.interfaces = new DexTypeList(newInterfaces);
- DexEncodedMethod dexEncodedMethod = clazz.lookupVirtualMethod(dexMethod);
- if (dexEncodedMethod == null) {
- DexEncodedMethod newMethod = createForwardingMethod(dexMethod, clazz);
- clazz.addVirtualMethod(newMethod);
- newForwardingMethods.add(newMethod);
- }
- }
- return newForwardingMethods;
- }
-
- private DexEncodedMethod createForwardingMethod(DexMethod 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;
- return DexEncodedMethod.createDesugaringForwardingMethod(
- appView.definitionFor(target), clazz, forwardMethod, appView.dexItemFactory());
- }
-
- private void synthesizeEmulatedDispatchMethods(Builder<?> builder) {
- assert appView.options().isDesugaredLibraryCompilation();
- if (rewritableMethods.getEmulatedDispatchMethods().isEmpty()) {
- return;
- }
- ClassAccessFlags itfAccessFlags =
- ClassAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC
- | Constants.ACC_SYNTHETIC
- | Constants.ACC_ABSTRACT
- | Constants.ACC_INTERFACE);
- ClassAccessFlags holderAccessFlags =
- ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC);
- for (DexMethod emulatedDispatchMethod : rewritableMethods.getEmulatedDispatchMethods()) {
- // Dispatch interface.
- DexType interfaceType = dispatchInterfaceTypeFor(appView, emulatedDispatchMethod);
- DexEncodedMethod itfMethod =
- generateInterfaceDispatchMethod(emulatedDispatchMethod, interfaceType);
- synthesizeClassWithUniqueMethod(
- builder,
- itfAccessFlags,
- interfaceType,
- itfMethod,
- "desugared library dispatch interface",
- false);
- // Dispatch holder.
- DexType holderType = dispatchHolderTypeFor(appView, emulatedDispatchMethod);
- DexEncodedMethod dispatchMethod =
- generateHolderDispatchMethod(emulatedDispatchMethod, holderType, itfMethod.method);
- synthesizeClassWithUniqueMethod(
- builder,
- holderAccessFlags,
- holderType,
- dispatchMethod,
- "desugared library dispatch class",
- false);
- }
- }
-
- private DexProgramClass synthesizeClassWithUniqueMethod(
+ static DexProgramClass synthesizeClassWithUniqueMethod(
Builder<?> builder,
ClassAccessFlags accessFlags,
DexType type,
DexEncodedMethod uniqueMethod,
String origin,
- boolean addToMainDexList) {
+ boolean addToMainDexList,
+ AppView<?> appView) {
+ DexItemFactory factory = appView.dexItemFactory();
DexProgramClass newClass =
new DexProgramClass(
type,
null,
- new SynthesizedOrigin(origin, getClass()),
+ new SynthesizedOrigin(origin, BackportedMethodRewriter.class),
accessFlags,
factory.objectType,
DexTypeList.empty(),
@@ -425,165 +271,31 @@
? DexEncodedMethod.EMPTY_ARRAY
: new DexEncodedMethod[] {uniqueMethod},
factory.getSkipNameValidationForTesting(),
- getChecksumSupplier(uniqueMethod));
+ getChecksumSupplier(uniqueMethod, appView));
appView.appInfo().addSynthesizedClass(newClass);
builder.addSynthesizedClass(newClass, addToMainDexList);
return newClass;
}
- private DexEncodedMethod generateInterfaceDispatchMethod(
- DexMethod emulatedDispatchMethod, DexType interfaceType) {
- MethodAccessFlags flags =
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC | Constants.ACC_ABSTRACT | Constants.ACC_SYNTHETIC, false);
- DexMethod newMethod =
- factory.createMethod(
- interfaceType, emulatedDispatchMethod.proto, emulatedDispatchMethod.name);
- return new DexEncodedMethod(
- newMethod, flags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), null, true);
- }
-
- private DexEncodedMethod generateHolderDispatchMethod(
- DexMethod emulatedDispatchMethod, DexType dispatchHolder, DexMethod itfMethod) {
- // The method should look like:
- // static foo(rcvr, arg0, arg1) {
- // if (rcvr instanceof interfaceType) {
- // return invoke-interface receiver.foo(arg0, arg1);
- // } else {
- // return DesugarX.foo(rcvr, arg0, arg1)
- // }
- // We do not deal with complex cases (multiple retargeting of the same signature in the
- // same inheritance tree, etc., since they do not happen in the most common desugared library.
- DexMethod desugarMethod =
- appView
- .options()
- .desugaredLibraryConfiguration
- .retargetMethod(emulatedDispatchMethod, appView);
- assert desugarMethod != null; // This method is reached only for retarget core lib members.
- DexMethod newMethod =
- appView
- .dexItemFactory()
- .createMethod(dispatchHolder, desugarMethod.proto, emulatedDispatchMethod.name);
- return DexEncodedMethod.toEmulateDispatchLibraryMethod(
- emulatedDispatchMethod.holder,
- newMethod,
- desugarMethod,
- itfMethod,
- Collections.emptyList(),
- appView);
- }
-
- private ChecksumSupplier getChecksumSupplier(DexEncodedMethod method) {
+ private static ChecksumSupplier getChecksumSupplier(DexEncodedMethod method, AppView<?> appView) {
if (!appView.options().encodeChecksums) {
return DexProgramClass::invalidChecksumRequest;
}
return c -> method.method.hashCode();
}
-
- public static DexType dispatchInterfaceTypeFor(AppView<?> appView, DexMethod method) {
- return dispatchTypeFor(appView, method, "dispatchInterface");
- }
-
- static DexType dispatchHolderTypeFor(AppView<?> appView, DexMethod method) {
- return dispatchTypeFor(appView, method, "dispatchHolder");
- }
-
- private static DexType dispatchTypeFor(AppView<?> appView, DexMethod method, String suffix) {
- String prefix =
- appView.options().desugaredLibraryConfiguration.getSynthesizedLibraryClassesPackagePrefix();
- String descriptor =
- "L"
- + prefix
- + UTILITY_CLASS_NAME_PREFIX
- + '$'
- + method.holder.getName()
- + '$'
- + method.name
- + '$'
- + suffix
- + ';';
- return appView.dexItemFactory().createType(descriptor);
- }
-
+
private MethodProvider getMethodProviderOrNull(DexMethod method) {
DexMethod original = appView.graphLense().getOriginalMethodSignature(method);
assert original != null;
- Map<DexType, DexType> backportCoreLibraryMembers =
- appView.options().desugaredLibraryConfiguration.getBackportCoreLibraryMember();
- if (backportCoreLibraryMembers.containsKey(original.holder)) {
- DexType newHolder = backportCoreLibraryMembers.get(original.holder);
- DexMethod newMethod =
- appView.dexItemFactory().createMethod(newHolder, original.proto, original.name);
- MethodProvider provider = rewritableMethods.getProvider(newMethod);
- if (provider != null) {
- return provider;
- }
- RetargetCoreLibraryMethodProvider extraProvider =
- new RetargetCoreLibraryMethodProvider(newHolder, original, true);
- // TODO(b/139788786): cache this entry, but without writing into a lock free structure.
- // rewritableMethods.addProvider(extraProvider);
- return extraProvider;
- }
return rewritableMethods.getProvider(original);
}
- public static void checkForAssumedLibraryTypes(AppView<?> appView) {
- Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
- appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember();
- for (DexString methodName : retargetCoreLibMember.keySet()) {
- for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
- DexClass typeClass = appView.definitionFor(inType);
- if (typeClass == null) {
- RewritableMethods.warnMissingRetargetCoreLibraryMember(inType, appView);
- }
- }
- }
- }
-
private static final class RewritableMethods {
// Map backported method to a provider for creating the actual target method (with code).
private final Map<DexMethod, MethodProvider> rewritable = new IdentityHashMap<>();
- // Map virtualRewrites hold a methodName->method mapping for virtual methods which are
- // rewritten while the holder is non final but no superclass implement the method. In this case
- // d8 needs to force resolution of given methods to see if the invoke needs to be rewritten.
- private final Map<DexString, List<DexMethod>> virtualRewrites = new IdentityHashMap<>();
- // non final virtual library methods requiring generation of emulated dispatch.
- private final Set<DexMethod> emulatedDispatchMethods = Sets.newHashSet();
RewritableMethods(InternalOptions options, AppView<?> appView) {
- if (options.shouldBackportMethods()) {
- DexItemFactory factory = options.itemFactory;
- if (options.minApiLevel < AndroidApiLevel.K.getLevel()) {
- initializeAndroidKMethodProviders(factory);
- }
- if (options.minApiLevel < AndroidApiLevel.N.getLevel()) {
- initializeAndroidNMethodProviders(factory);
- }
- if (options.minApiLevel < AndroidApiLevel.O.getLevel()) {
- initializeAndroidOMethodProviders(factory);
- }
-
- // The following providers are currently not implemented at any API level in Android.
- // They however require the Optional/Stream class to be present, either through desugared
- // libraries or natively. If Optional/Stream class is not present, we do not desugar to
- // avoid confusion in error messages.
- if (appView.rewritePrefix.hasRewrittenType(factory.optionalType, appView)
- || options.minApiLevel >= AndroidApiLevel.N.getLevel()) {
- initializeJava9OptionalMethodProviders(factory);
- initializeJava10OptionalMethodProviders(factory);
- initializeJava11OptionalMethodProviders(factory);
- }
- if (appView.rewritePrefix.hasRewrittenType(factory.streamType, appView)
- || options.minApiLevel >= AndroidApiLevel.N.getLevel()) {
- initializeStreamMethodProviders(factory);
- }
-
- // These are currently not implemented at any API level in Android.
- initializeJava9MethodProviders(factory);
- initializeJava10MethodProviders(factory);
- initializeJava11MethodProviders(factory);
- }
if (options.testing.forceLibBackportsInL8CfToCf) {
DexItemFactory factory = options.itemFactory;
@@ -591,28 +303,44 @@
initializeJava10OptionalMethodProviders(factory);
initializeJava11OptionalMethodProviders(factory);
initializeStreamMethodProviders(factory);
+ return;
}
- if (!options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()) {
- initializeRetargetCoreLibraryMembers(appView);
+ if (!options.shouldBackportMethods()) {
+ return;
}
- }
- boolean matchesVirtualRewrite(DexMethod method) {
- List<DexMethod> dexMethods = virtualRewrites.get(method.name);
- if (dexMethods == null) {
- return false;
- }
- for (DexMethod dexMethod : dexMethods) {
- if (method.match(dexMethod)) {
- return true;
- }
- }
- return false;
- }
+ DexItemFactory factory = options.itemFactory;
- public Set<DexMethod> getEmulatedDispatchMethods() {
- return emulatedDispatchMethods;
+ if (options.minApiLevel < AndroidApiLevel.K.getLevel()) {
+ initializeAndroidKMethodProviders(factory);
+ }
+ if (options.minApiLevel < AndroidApiLevel.N.getLevel()) {
+ initializeAndroidNMethodProviders(factory);
+ }
+ if (options.minApiLevel < AndroidApiLevel.O.getLevel()) {
+ initializeAndroidOMethodProviders(factory);
+ }
+
+ // The following providers are currently not implemented at any API level in Android.
+ // They however require the Optional/Stream class to be present, either through desugared
+ // libraries or natively. If Optional/Stream class is not present, we do not desugar to
+ // avoid confusion in error messages.
+ if (appView.rewritePrefix.hasRewrittenType(factory.optionalType, appView)
+ || options.minApiLevel >= AndroidApiLevel.N.getLevel()) {
+ initializeJava9OptionalMethodProviders(factory);
+ initializeJava10OptionalMethodProviders(factory);
+ initializeJava11OptionalMethodProviders(factory);
+ }
+ if (appView.rewritePrefix.hasRewrittenType(factory.streamType, appView)
+ || options.minApiLevel >= AndroidApiLevel.N.getLevel()) {
+ initializeStreamMethodProviders(factory);
+ }
+
+ // These are currently not implemented at any API level in Android.
+ initializeJava9MethodProviders(factory);
+ initializeJava10MethodProviders(factory);
+ initializeJava11MethodProviders(factory);
}
boolean isEmpty() {
@@ -677,8 +405,9 @@
// int Objects.compare(T a, T b, Comparator<? super T> c)
name = factory.createString("compare");
- proto = factory.createProto(factory.intType, factory.objectType, factory.objectType,
- factory.comparatorType);
+ proto =
+ factory.createProto(
+ factory.intType, factory.objectType, factory.objectType, factory.comparatorType);
method = factory.createMethod(type, proto, name);
addProvider(new MethodGenerator(method, BackportedMethods::ObjectsMethods_compare));
@@ -1251,8 +980,9 @@
// String String.join(CharSequence, CharSequence...)
name = factory.createString("join");
- proto = factory.createProto(factory.stringType, factory.charSequenceType,
- factory.charSequenceArrayType);
+ proto =
+ factory.createProto(
+ factory.stringType, factory.charSequenceType, factory.charSequenceArrayType);
method = factory.createMethod(type, proto, name);
addProvider(
new MethodGenerator(method, BackportedMethods::StringMethods_joinArray, "joinArray"));
@@ -1287,19 +1017,13 @@
name = factory.createString("multiplyFull");
proto = factory.createProto(factory.longType, factory.intType, factory.intType);
method = factory.createMethod(type, proto, name);
- addProvider(
- new MethodGenerator(
- method,
- BackportedMethods::MathMethods_multiplyFull));
+ addProvider(new MethodGenerator(method, BackportedMethods::MathMethods_multiplyFull));
// long {Math,StrictMath}.multiplyHigh(long, long)
name = factory.createString("multiplyHigh");
proto = factory.createProto(factory.longType, factory.longType, factory.longType);
method = factory.createMethod(type, proto, name);
- addProvider(
- new MethodGenerator(
- method,
- BackportedMethods::MathMethods_multiplyHigh));
+ addProvider(new MethodGenerator(method, BackportedMethods::MathMethods_multiplyHigh));
// long {Math,StrictMath}.floorDiv(long, int)
name = factory.createString("floorDiv");
@@ -1393,8 +1117,7 @@
proto = factory.createProto(type, factory.objectArrayType);
method = factory.createMethod(type, proto, name);
addProvider(
- new MethodGenerator(
- method, BackportedMethods::CollectionMethods_listOfArray, "ofArray"));
+ new MethodGenerator(method, BackportedMethods::CollectionMethods_listOfArray, "ofArray"));
// Set<E> Set.of(<args>) for 0 to 10 arguments and Set.of(E[])
type = factory.setType;
@@ -1414,8 +1137,7 @@
proto = factory.createProto(type, factory.objectArrayType);
method = factory.createMethod(type, proto, name);
addProvider(
- new MethodGenerator(
- method, BackportedMethods::CollectionMethods_setOfArray, "ofArray"));
+ new MethodGenerator(method, BackportedMethods::CollectionMethods_setOfArray, "ofArray"));
// Map<K, V> Map.of(<K, V args>) for 0 to 10 pairs and Map.ofEntries(Map.Entry<K, V>[])
type = factory.mapType;
@@ -1562,24 +1284,24 @@
// Optional{void,Int,Long,Double}.stream()
DexType[] optionalTypes =
new DexType[] {
- optionalType,
- factory.optionalDoubleType,
- factory.optionalLongType,
- factory.optionalIntType,
+ optionalType,
+ factory.optionalDoubleType,
+ factory.optionalLongType,
+ factory.optionalIntType,
};
DexType[] streamReturnTypes =
new DexType[] {
- factory.streamType,
- factory.createType(factory.createString("Ljava/util/stream/DoubleStream;")),
- factory.createType(factory.createString("Ljava/util/stream/LongStream;")),
- factory.createType(factory.createString("Ljava/util/stream/IntStream;")),
+ factory.streamType,
+ factory.createType(factory.createString("Ljava/util/stream/DoubleStream;")),
+ factory.createType(factory.createString("Ljava/util/stream/LongStream;")),
+ factory.createType(factory.createString("Ljava/util/stream/IntStream;")),
};
TemplateMethodFactory[] streamMethodFactories =
new TemplateMethodFactory[] {
- BackportedMethods::OptionalMethods_stream,
- BackportedMethods::OptionalMethods_streamDouble,
- BackportedMethods::OptionalMethods_streamLong,
- BackportedMethods::OptionalMethods_streamInt,
+ BackportedMethods::OptionalMethods_stream,
+ BackportedMethods::OptionalMethods_streamDouble,
+ BackportedMethods::OptionalMethods_streamLong,
+ BackportedMethods::OptionalMethods_streamInt,
};
name = factory.createString("stream");
for (int i = 0; i < optionalTypes.length; i++) {
@@ -1588,24 +1310,20 @@
proto = factory.createProto(streamReturnType);
method = factory.createMethod(optional, proto, name);
addProvider(
- new StatifyingMethodGenerator(
- method, streamMethodFactories[i], "stream", optional));
+ new StatifyingMethodGenerator(method, streamMethodFactories[i], "stream", optional));
}
// Optional{void,Int,Long,Double}.ifPresentOrElse(consumer,runnable)
DexType[] consumerTypes =
- new DexType[]{
- factory.consumerType,
- factory.doubleConsumer,
- factory.longConsumer,
- factory.intConsumer
+ new DexType[] {
+ factory.consumerType, factory.doubleConsumer, factory.longConsumer, factory.intConsumer
};
TemplateMethodFactory[] methodFactories =
- new TemplateMethodFactory[]{
- BackportedMethods::OptionalMethods_ifPresentOrElse,
- BackportedMethods::OptionalMethods_ifPresentOrElseDouble,
- BackportedMethods::OptionalMethods_ifPresentOrElseLong,
- BackportedMethods::OptionalMethods_ifPresentOrElseInt
+ new TemplateMethodFactory[] {
+ BackportedMethods::OptionalMethods_ifPresentOrElse,
+ BackportedMethods::OptionalMethods_ifPresentOrElseDouble,
+ BackportedMethods::OptionalMethods_ifPresentOrElseLong,
+ BackportedMethods::OptionalMethods_ifPresentOrElseInt
};
for (int i = 0; i < optionalTypes.length; i++) {
DexType optional = optionalTypes[i];
@@ -1622,17 +1340,14 @@
// Optional{void,Int,Long,Double}.orElseThrow()
DexType[] optionalTypes =
new DexType[] {
- factory.optionalType,
- factory.optionalDoubleType,
- factory.optionalLongType,
- factory.optionalIntType,
+ factory.optionalType,
+ factory.optionalDoubleType,
+ factory.optionalLongType,
+ factory.optionalIntType,
};
DexType[] returnTypes =
new DexType[] {
- factory.objectType,
- factory.doubleType,
- factory.longType,
- factory.intType,
+ factory.objectType, factory.doubleType, factory.longType, factory.intType,
};
MethodInvokeRewriter[] rewriters =
new MethodInvokeRewriter[] {
@@ -1653,17 +1368,17 @@
// Optional{void,Int,Long,Double}.isEmpty()
DexType[] optionalTypes =
new DexType[] {
- factory.optionalType,
- factory.optionalDoubleType,
- factory.optionalLongType,
- factory.optionalIntType,
+ factory.optionalType,
+ factory.optionalDoubleType,
+ factory.optionalLongType,
+ factory.optionalIntType,
};
TemplateMethodFactory[] methodFactories =
- new TemplateMethodFactory[]{
- BackportedMethods::OptionalMethods_isEmpty,
- BackportedMethods::OptionalMethods_isEmptyDouble,
- BackportedMethods::OptionalMethods_isEmptyLong,
- BackportedMethods::OptionalMethods_isEmptyInt
+ new TemplateMethodFactory[] {
+ BackportedMethods::OptionalMethods_isEmpty,
+ BackportedMethods::OptionalMethods_isEmptyDouble,
+ BackportedMethods::OptionalMethods_isEmptyLong,
+ BackportedMethods::OptionalMethods_isEmptyInt
};
DexString name = factory.createString("isEmpty");
for (int i = 0; i < optionalTypes.length; i++) {
@@ -1684,76 +1399,7 @@
DexProto proto = factory.createProto(factory.streamType, factory.objectType);
DexMethod method = factory.createMethod(streamType, proto, name);
addProvider(
- new MethodGenerator(
- method, BackportedMethods::StreamMethods_ofNullable, "ofNullable"));
- }
-
- private static void warnMissingRetargetCoreLibraryMember(DexType type, AppView<?> appView) {
- StringDiagnostic warning =
- new StringDiagnostic(
- "Cannot retarget core library member "
- + type.getName()
- + " because the class is missing.");
- appView.options().reporter.warning(warning);
- }
-
- private void initializeRetargetCoreLibraryMembers(AppView<?> appView) {
- assert appView.appInfo().hasClassHierarchy()
- : "Class hierarchy required for desugared library.";
- Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
- appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember();
- for (DexString methodName : retargetCoreLibMember.keySet()) {
- for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
- DexClass typeClass = appView.definitionFor(inType);
- if (typeClass != null) {
- DexType newHolder = retargetCoreLibMember.get(methodName).get(inType);
- List<DexEncodedMethod> found = findDexEncodedMethodsWithName(methodName, typeClass);
- for (DexEncodedMethod encodedMethod : found) {
- if (!encodedMethod.isStatic()) {
- virtualRewrites.putIfAbsent(encodedMethod.method.name, new ArrayList<>());
- virtualRewrites.get(encodedMethod.method.name).add(encodedMethod.method);
- if (InterfaceMethodRewriter.isEmulatedInterfaceDispatch(appView, encodedMethod)) {
- // In this case interface method rewriter takes care of it.
- continue;
- } else if (!encodedMethod.isFinal()) {
- // Virtual rewrites require emulated dispatch for inheritance.
- // The call is rewritten to the dispatch holder class instead.
- handleEmulateDispatch(appView, encodedMethod.method);
- newHolder = dispatchHolderTypeFor(appView, encodedMethod.method);
- }
- }
- DexProto proto = encodedMethod.method.proto;
- DexMethod method = appView.dexItemFactory().createMethod(inType, proto, methodName);
- addProvider(
- new RetargetCoreLibraryMethodProvider(
- newHolder, method, encodedMethod.isStatic()));
- }
- }
- }
- }
- }
-
- private List<DexEncodedMethod> findDexEncodedMethodsWithName(
- DexString methodName, DexClass clazz) {
- List<DexEncodedMethod> found = new ArrayList<>();
- for (DexEncodedMethod encodedMethod : clazz.methods()) {
- if (encodedMethod.method.name == methodName) {
- found.add(encodedMethod);
- }
- }
- assert found.size() > 0 : "Should have found a method (library specifications).";
- return found;
- }
-
- private void handleEmulateDispatch(AppView<?> appView, DexMethod method) {
- emulatedDispatchMethods.add(method);
- if (!appView.options().isDesugaredLibraryCompilation()) {
- // Add rewrite rules so keeps rules are correctly generated in the program.
- DexType dispatchInterfaceType = dispatchInterfaceTypeFor(appView, method);
- appView.rewritePrefix.rewriteType(dispatchInterfaceType, dispatchInterfaceType);
- DexType dispatchHolderType = dispatchHolderTypeFor(appView, method);
- appView.rewritePrefix.rewriteType(dispatchHolderType, dispatchHolderType);
- }
+ new MethodGenerator(method, BackportedMethods::StreamMethods_ofNullable, "ofNullable"));
}
private void addProvider(MethodProvider generator) {
@@ -1788,52 +1434,6 @@
public abstract boolean requiresGenerationOfCode();
}
- private static class RetargetCoreLibraryMethodProvider extends MethodProvider {
-
- private final DexType newHolder;
- private DexMethod targetMethod;
- private boolean isStatic;
-
- RetargetCoreLibraryMethodProvider(DexType newHolder, DexMethod method, boolean isStatic) {
- super(method);
- this.newHolder = newHolder;
- this.isStatic = isStatic;
- }
-
- @Override
- public void rewriteInvoke(
- InvokeMethod invoke,
- InstructionListIterator iterator,
- IRCode code,
- AppView<?> appView,
- Set<Value> affectedValues) {
- iterator.replaceCurrentInstruction(
- new InvokeStatic(provideMethod(appView), invoke.outValue(), invoke.inValues()));
- }
-
- @Override
- public DexMethod provideMethod(AppView<?> appView) {
- if (targetMethod != null) {
- return targetMethod;
- }
- DexItemFactory factory = appView.dexItemFactory();
- DexProto newProto =
- isStatic ? method.proto : factory.prependTypeToProto(method.holder, method.proto);
- targetMethod = factory.createMethod(newHolder, newProto, method.name);
- return targetMethod;
- }
-
- @Override
- public Code generateTemplateMethod(InternalOptions options, DexMethod method) {
- throw new Unreachable("Does not generate any method.");
- }
-
- @Override
- public boolean requiresGenerationOfCode() {
- return false;
- }
- }
-
private static final class InvokeRewriter extends MethodProvider {
private final MethodInvokeRewriter rewriter;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
index bdb4cd9..baa6317 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
@@ -110,12 +110,6 @@
return libraryCompilation;
}
- public String getSynthesizedLibraryClassesPackagePrefix(AppView<?> appView) {
- return appView.options().isDesugaredLibraryCompilation()
- ? synthesizedLibraryClassesPackagePrefix
- : "";
- }
-
public String getSynthesizedLibraryClassesPackagePrefix() {
return synthesizedLibraryClassesPackagePrefix;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
new file mode 100644
index 0000000..3949767
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
@@ -0,0 +1,441 @@
+// Copyright (c) 2020, 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;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.*;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.utils.StringDiagnostic;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.function.Consumer;
+
+public class DesugaredLibraryRetargeter {
+
+ public static final String DESUGAR_LIB_RETARGET_CLASS_NAME_PREFIX =
+ "$r8$retargetLibraryMember$virtualDispatch";
+
+ private final AppView<AppInfoWithClassHierarchy> appView;
+ private final Map<DexMethod, DexMethod> retargetLibraryMember = new IdentityHashMap<>();
+ // Map virtualRewrites hold a methodName->method mapping for virtual methods which are
+ // rewritten while the holder is non final but no superclass implement the method. In this case
+ // d8 needs to force resolution of given methods to see if the invoke needs to be rewritten.
+ private final Map<DexString, List<DexMethod>> virtualRewrites = new IdentityHashMap<>();
+ // Non final virtual library methods requiring generation of emulated dispatch.
+ private final Set<DexMethod> emulatedDispatchMethods = Sets.newHashSet();
+
+ public DesugaredLibraryRetargeter(AppView<?> appView) {
+ assert appView.appInfo().hasClassHierarchy()
+ : "Class hierarchy required for desugared library.";
+ this.appView = appView.withClassHierarchy();
+ if (appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()) {
+ return;
+ }
+ new RetargetingSetup().setUpRetargeting();
+ }
+
+ public static void checkForAssumedLibraryTypes(AppView<?> appView) {
+ Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
+ appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember();
+ for (DexString methodName : retargetCoreLibMember.keySet()) {
+ for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
+ DexClass typeClass = appView.definitionFor(inType);
+ if (typeClass == null) {
+ warnMissingRetargetCoreLibraryMember(inType, appView);
+ }
+ }
+ }
+ }
+
+ private static void warnMissingRetargetCoreLibraryMember(DexType type, AppView<?> appView) {
+ StringDiagnostic warning =
+ new StringDiagnostic(
+ "Cannot retarget core library member "
+ + type.getName()
+ + " because the class is missing.");
+ appView.options().reporter.warning(warning);
+ }
+
+ // Used by the ListOfBackportedMethods utility.
+ void visit(Consumer<DexMethod> consumer) {
+ retargetLibraryMember.keySet().forEach(consumer);
+ }
+
+ public void desugar(IRCode code) {
+ if (retargetLibraryMember.isEmpty()) {
+ return;
+ }
+
+ InstructionListIterator iterator = code.instructionListIterator();
+ while (iterator.hasNext()) {
+ Instruction instruction = iterator.next();
+ if (!instruction.isInvokeMethod()) {
+ continue;
+ }
+
+ InvokeMethod invoke = instruction.asInvokeMethod();
+ DexMethod retarget = getRetargetLibraryMember(invoke.getInvokedMethod());
+ if (retarget == null) {
+ if (!matchesVirtualRewrite(invoke.getInvokedMethod())) {
+ continue;
+ }
+ // We need to force resolution, even on d8, to know if the invoke has to be rewritten.
+ ResolutionResult resolutionResult =
+ appView
+ .appInfo()
+ .resolveMethod(invoke.getInvokedMethod().holder, invoke.getInvokedMethod());
+ if (resolutionResult.isFailedResolution()) {
+ continue;
+ }
+ DexEncodedMethod singleTarget = resolutionResult.getSingleTarget();
+ assert singleTarget != null;
+ retarget = getRetargetLibraryMember(singleTarget.method);
+ if (retarget == null) {
+ continue;
+ }
+ }
+
+ // Due to emulated dispatch, we have to rewrite invoke-super differently or we end up in
+ // infinite loops. We do direct resolution. This is a very uncommon case.
+ if (invoke.isInvokeSuper() && matchesVirtualRewrite(invoke.getInvokedMethod())) {
+ DexEncodedMethod dexEncodedMethod =
+ appView
+ .appInfo()
+ .withClassHierarchy()
+ .lookupSuperTarget(invoke.getInvokedMethod(), code.method.method.holder);
+ // Final methods can be rewritten as a normal invoke.
+ if (dexEncodedMethod != null && !dexEncodedMethod.isFinal()) {
+ DexMethod retargetMethod =
+ appView
+ .options()
+ .desugaredLibraryConfiguration
+ .retargetMethod(dexEncodedMethod.method, appView);
+ if (retargetMethod != null) {
+ iterator.replaceCurrentInstruction(
+ new InvokeStatic(retargetMethod, invoke.outValue(), invoke.arguments()));
+ }
+ continue;
+ }
+ }
+
+ iterator.replaceCurrentInstruction(
+ new InvokeStatic(retarget, invoke.outValue(), invoke.inValues()));
+ }
+ }
+
+ private DexMethod getRetargetLibraryMember(DexMethod method) {
+ Map<DexType, DexType> backportCoreLibraryMembers =
+ appView.options().desugaredLibraryConfiguration.getBackportCoreLibraryMember();
+ if (backportCoreLibraryMembers.containsKey(method.holder)) {
+ DexType newHolder = backportCoreLibraryMembers.get(method.holder);
+ return appView.dexItemFactory().createMethod(newHolder, method.proto, method.name);
+ }
+ return retargetLibraryMember.get(method);
+ }
+
+ private boolean matchesVirtualRewrite(DexMethod method) {
+ List<DexMethod> dexMethods = virtualRewrites.get(method.name);
+ if (dexMethods == null) {
+ return false;
+ }
+ for (DexMethod dexMethod : dexMethods) {
+ if (method.match(dexMethod)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private class RetargetingSetup {
+
+ private void setUpRetargeting() {
+ Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
+ appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember();
+ for (DexString methodName : retargetCoreLibMember.keySet()) {
+ for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
+ DexClass typeClass = appView.definitionFor(inType);
+ if (typeClass != null) {
+ DexType newHolder = retargetCoreLibMember.get(methodName).get(inType);
+ List<DexEncodedMethod> found = findDexEncodedMethodsWithName(methodName, typeClass);
+ for (DexEncodedMethod encodedMethod : found) {
+ if (!encodedMethod.isStatic()) {
+ virtualRewrites.putIfAbsent(encodedMethod.method.name, new ArrayList<>());
+ virtualRewrites.get(encodedMethod.method.name).add(encodedMethod.method);
+ if (InterfaceMethodRewriter.isEmulatedInterfaceDispatch(appView, encodedMethod)) {
+ // In this case interface method rewriter takes care of it.
+ continue;
+ } else if (!encodedMethod.isFinal()) {
+ // Virtual rewrites require emulated dispatch for inheritance.
+ // The call is rewritten to the dispatch holder class instead.
+ handleEmulateDispatch(appView, encodedMethod.method);
+ newHolder = dispatchHolderTypeFor(encodedMethod.method);
+ }
+ }
+ DexProto proto = encodedMethod.method.proto;
+ DexMethod method = appView.dexItemFactory().createMethod(inType, proto, methodName);
+ retargetLibraryMember.put(
+ method, computeRetargetMethod(method, encodedMethod.isStatic(), newHolder));
+ }
+ }
+ }
+ }
+ }
+
+ private DexMethod computeRetargetMethod(DexMethod method, boolean isStatic, DexType newHolder) {
+ DexItemFactory factory = appView.dexItemFactory();
+ DexProto newProto =
+ isStatic ? method.proto : factory.prependTypeToProto(method.holder, method.proto);
+ return factory.createMethod(newHolder, newProto, method.name);
+ }
+
+ private List<DexEncodedMethod> findDexEncodedMethodsWithName(
+ DexString methodName, DexClass clazz) {
+ List<DexEncodedMethod> found = new ArrayList<>();
+ for (DexEncodedMethod encodedMethod : clazz.methods()) {
+ if (encodedMethod.method.name == methodName) {
+ found.add(encodedMethod);
+ }
+ }
+ assert found.size() > 0 : "Should have found a method (library specifications).";
+ return found;
+ }
+
+ private void handleEmulateDispatch(AppView<?> appView, DexMethod method) {
+ emulatedDispatchMethods.add(method);
+ if (!appView.options().isDesugaredLibraryCompilation()) {
+ // Add rewrite rules so keeps rules are correctly generated in the program.
+ DexType dispatchInterfaceType = dispatchInterfaceTypeFor(method);
+ appView.rewritePrefix.rewriteType(dispatchInterfaceType, dispatchInterfaceType);
+ DexType dispatchHolderType = dispatchHolderTypeFor(method);
+ appView.rewritePrefix.rewriteType(dispatchHolderType, dispatchHolderType);
+ }
+ }
+ }
+
+ public void synthesizeRetargetClasses(
+ DexApplication.Builder<?> builder, ExecutorService executorService, IRConverter converter)
+ throws ExecutionException {
+ new EmulatedDispatchTreeFixer().fixApp(builder, executorService, converter);
+ }
+
+ // 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.
+ class EmulatedDispatchTreeFixer {
+
+ void fixApp(
+ DexApplication.Builder<?> builder, ExecutorService executorService, IRConverter converter)
+ throws ExecutionException {
+ if (appView.options().isDesugaredLibraryCompilation()) {
+ synthesizeEmulatedDispatchMethods(builder);
+ } else {
+ addInterfacesAndForwardingMethods(executorService, converter);
+ }
+ }
+
+ private void addInterfacesAndForwardingMethods(
+ ExecutorService executorService, IRConverter converter) throws ExecutionException {
+ assert !appView.options().isDesugaredLibraryCompilation();
+ Map<DexType, List<DexMethod>> map = Maps.newIdentityHashMap();
+ for (DexMethod emulatedDispatchMethod : emulatedDispatchMethods) {
+ map.putIfAbsent(emulatedDispatchMethod.holder, new ArrayList<>(1));
+ map.get(emulatedDispatchMethod.holder).add(emulatedDispatchMethod);
+ }
+ List<DexEncodedMethod> addedMethods = new ArrayList<>();
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ if (clazz.superType == null) {
+ assert clazz.type == appView.dexItemFactory().objectType : clazz.type.toSourceString();
+ continue;
+ }
+ DexClass dexClass = appView.definitionFor(clazz.superType);
+ // Only performs computation if superclass is a library class, but not object to filter out
+ // the most common case.
+ if (dexClass != null
+ && dexClass.isLibraryClass()
+ && dexClass.type != appView.dexItemFactory().objectType) {
+ for (DexType dexType : map.keySet()) {
+ if (inherit(dexClass.asLibraryClass(), dexType)) {
+ addedMethods.addAll(addInterfacesAndForwardingMethods(clazz, map.get(dexType)));
+ }
+ }
+ }
+ }
+ if (addedMethods.isEmpty()) {
+ return;
+ }
+ converter.processMethodsConcurrently(addedMethods, executorService);
+ }
+
+ private boolean inherit(DexLibraryClass clazz, DexType typeToInherit) {
+ DexLibraryClass current = clazz;
+ while (current.type != appView.dexItemFactory().objectType) {
+ if (current.type == typeToInherit) {
+ return true;
+ }
+ current = appView.definitionFor(current.superType).asLibraryClass();
+ }
+ return false;
+ }
+
+ private List<DexEncodedMethod> addInterfacesAndForwardingMethods(
+ DexProgramClass clazz, List<DexMethod> dexMethods) {
+ // 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.
+ List<DexEncodedMethod> newForwardingMethods = new ArrayList<>();
+ for (DexMethod dexMethod : dexMethods) {
+ DexType[] newInterfaces =
+ Arrays.copyOf(clazz.interfaces.values, clazz.interfaces.size() + 1);
+ newInterfaces[newInterfaces.length - 1] = dispatchInterfaceTypeFor(dexMethod);
+ clazz.interfaces = new DexTypeList(newInterfaces);
+ DexEncodedMethod dexEncodedMethod = clazz.lookupVirtualMethod(dexMethod);
+ if (dexEncodedMethod == null) {
+ DexEncodedMethod newMethod = createForwardingMethod(dexMethod, clazz);
+ clazz.addVirtualMethod(newMethod);
+ newForwardingMethods.add(newMethod);
+ }
+ }
+ return newForwardingMethods;
+ }
+
+ private DexEncodedMethod createForwardingMethod(DexMethod 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;
+ return DexEncodedMethod.createDesugaringForwardingMethod(
+ appView.definitionFor(target), clazz, forwardMethod, appView.dexItemFactory());
+ }
+
+ private void synthesizeEmulatedDispatchMethods(DexApplication.Builder<?> builder) {
+ assert appView.options().isDesugaredLibraryCompilation();
+ if (emulatedDispatchMethods.isEmpty()) {
+ return;
+ }
+ ClassAccessFlags itfAccessFlags =
+ ClassAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_PUBLIC
+ | Constants.ACC_SYNTHETIC
+ | Constants.ACC_ABSTRACT
+ | Constants.ACC_INTERFACE);
+ ClassAccessFlags holderAccessFlags =
+ ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC);
+ for (DexMethod emulatedDispatchMethod : emulatedDispatchMethods) {
+ // Dispatch interface.
+ DexType interfaceType = dispatchInterfaceTypeFor(emulatedDispatchMethod);
+ DexEncodedMethod itfMethod =
+ generateInterfaceDispatchMethod(emulatedDispatchMethod, interfaceType);
+ BackportedMethodRewriter.synthesizeClassWithUniqueMethod(
+ builder,
+ itfAccessFlags,
+ interfaceType,
+ itfMethod,
+ "desugared library dispatch interface",
+ false,
+ appView);
+ // Dispatch holder.
+ DexType holderType = dispatchHolderTypeFor(emulatedDispatchMethod);
+ DexEncodedMethod dispatchMethod =
+ generateHolderDispatchMethod(emulatedDispatchMethod, holderType, itfMethod.method);
+ BackportedMethodRewriter.synthesizeClassWithUniqueMethod(
+ builder,
+ holderAccessFlags,
+ holderType,
+ dispatchMethod,
+ "desugared library dispatch class",
+ false,
+ appView);
+ }
+ }
+
+ private DexEncodedMethod generateInterfaceDispatchMethod(
+ DexMethod emulatedDispatchMethod, DexType interfaceType) {
+ MethodAccessFlags flags =
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_PUBLIC | Constants.ACC_ABSTRACT | Constants.ACC_SYNTHETIC, false);
+ DexMethod newMethod =
+ appView
+ .dexItemFactory()
+ .createMethod(
+ interfaceType, emulatedDispatchMethod.proto, emulatedDispatchMethod.name);
+ return new DexEncodedMethod(
+ newMethod, flags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), null, true);
+ }
+
+ private DexEncodedMethod generateHolderDispatchMethod(
+ DexMethod emulatedDispatchMethod, DexType dispatchHolder, DexMethod itfMethod) {
+ // The method should look like:
+ // static foo(rcvr, arg0, arg1) {
+ // if (rcvr instanceof interfaceType) {
+ // return invoke-interface receiver.foo(arg0, arg1);
+ // } else {
+ // return DesugarX.foo(rcvr, arg0, arg1)
+ // }
+ // We do not deal with complex cases (multiple retargeting of the same signature in the
+ // same inheritance tree, etc., since they do not happen in the most common desugared library.
+ DexMethod desugarMethod =
+ appView
+ .options()
+ .desugaredLibraryConfiguration
+ .retargetMethod(emulatedDispatchMethod, appView);
+ assert desugarMethod != null; // This method is reached only for retarget core lib members.
+ DexMethod newMethod =
+ appView
+ .dexItemFactory()
+ .createMethod(dispatchHolder, desugarMethod.proto, emulatedDispatchMethod.name);
+ return DexEncodedMethod.toEmulateDispatchLibraryMethod(
+ emulatedDispatchMethod.holder,
+ newMethod,
+ desugarMethod,
+ itfMethod,
+ Collections.emptyList(),
+ appView);
+ }
+ }
+
+ private DexType dispatchInterfaceTypeFor(DexMethod method) {
+ return dispatchTypeFor(method, "dispatchInterface");
+ }
+
+ private DexType dispatchHolderTypeFor(DexMethod method) {
+ return dispatchTypeFor(method, "dispatchHolder");
+ }
+
+ private DexType dispatchTypeFor(DexMethod method, String suffix) {
+ String descriptor =
+ "L"
+ + appView
+ .options()
+ .desugaredLibraryConfiguration
+ .getSynthesizedLibraryClassesPackagePrefix()
+ + DESUGAR_LIB_RETARGET_CLASS_NAME_PREFIX
+ + '$'
+ + method.holder.getName()
+ + '$'
+ + method.name
+ + '$'
+ + suffix
+ + ';';
+ return appView.dexItemFactory().createType(descriptor);
+ }
+}