Revert "Split BackporterMethodRewriter and RetargetLibraryMember"
This reverts commit 6b15b7762f894b911474790f9d4989d90f6cb217.
Reason for revert: failing tests
Change-Id: I1fcd541768a3c4d628107192ca5d2362b73bd94c
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index b843181..100b5aa 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.DesugaredLibraryRetargeter;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
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()) {
- DesugaredLibraryRetargeter.checkForAssumedLibraryTypes(appView);
+ BackportedMethodRewriter.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 01c5298..4b8c6d4 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -16,7 +16,6 @@
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;
@@ -45,7 +44,8 @@
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,12 +145,11 @@
}
@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);
}
}
@@ -273,7 +272,6 @@
|| 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);
}
@@ -300,17 +298,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 + "'");
@@ -330,11 +328,8 @@
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);
}
@@ -369,10 +364,8 @@
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);
}
@@ -383,8 +376,7 @@
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 4c820ff..4ac8995 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,7 +45,6 @@
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;
@@ -144,7 +143,6 @@
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;
@@ -219,16 +217,13 @@
.collect(Collectors.toList());
if (options.isDesugaredLibraryCompilation()) {
// Specific L8 Settings.
- // DesugaredLibraryRetargeter is needed for retarget core library members and backports.
+ // BackportedMethodRewriter 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.desugaredLibraryRetargeter =
- options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()
- ? null
- : new DesugaredLibraryRetargeter(appView);
+ this.backportedMethodRewriter = new BackportedMethodRewriter(appView, this);
this.interfaceMethodRewriter =
options.desugaredLibraryConfiguration.getEmulateLibraryInterface().isEmpty()
? null
@@ -236,7 +231,6 @@
this.lambdaRewriter = new LambdaRewriter(appView);
this.desugaredLibraryAPIConverter =
new DesugaredLibraryAPIConverter(appView, Mode.GENERATE_CALLBACKS_AND_WRAPPERS);
- this.backportedMethodRewriter = null;
this.twrCloseResourceRewriter = null;
this.lambdaMerger = null;
this.covariantReturnTypeAnnotationTransformer = null;
@@ -274,10 +268,6 @@
? 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())
@@ -460,16 +450,7 @@
private void synthesizeJava8UtilityClass(
Builder<?> builder, ExecutorService executorService) throws ExecutionException {
- if (backportedMethodRewriter != null) {
- backportedMethodRewriter.synthesizeUtilityClasses(builder, executorService);
- }
- }
-
- private void synthesizeRetargetClass(Builder<?> builder, ExecutorService executorService)
- throws ExecutionException {
- if (desugaredLibraryRetargeter != null) {
- desugaredLibraryRetargeter.synthesizeRetargetClasses(builder, executorService, this);
- }
+ backportedMethodRewriter.synthesizeUtilityClasses(builder, executorService);
}
private void synthesizeEnumUnboxingUtilityClass(
@@ -501,8 +482,6 @@
desugarInterfaceMethods(builder, ExcludeDexResources, executor);
synthesizeTwrCloseResourceUtilityClass(builder, executor);
synthesizeJava8UtilityClass(builder, executor);
- synthesizeRetargetClass(builder, executor);
-
processCovariantReturnTypeAnnotations(builder);
generateDesugaredLibraryAPIWrappers(builder, executor);
@@ -768,7 +747,6 @@
printPhase("Utility classes synthesis");
synthesizeTwrCloseResourceUtilityClass(builder, executorService);
synthesizeJava8UtilityClass(builder, executorService);
- synthesizeRetargetClass(builder, executorService);
handleSynthesizedClassMapping(builder);
synthesizeEnumUnboxingUtilityClass(builder, executorService);
@@ -1392,20 +1370,9 @@
codeRewriter.rewriteThrowableAddAndGetSuppressed(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("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 c56524e..50b068c 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,6 +21,7 @@
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;
@@ -30,6 +31,7 @@
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;
@@ -52,10 +54,13 @@
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;
@@ -117,9 +122,6 @@
BackportedMethodRewriter.RewritableMethods rewritableMethods =
new BackportedMethodRewriter.RewritableMethods(options, appView);
rewritableMethods.visit(methods::add);
- DesugaredLibraryRetargeter desugaredLibraryRetargeter =
- new DesugaredLibraryRetargeter(appView);
- desugaredLibraryRetargeter.visit(methods::add);
return methods;
} catch (ExecutionException e) {
throw unwrapExecutionException(e);
@@ -144,10 +146,49 @@
&& !invoke.isInvokeStatic()) {
continue;
}
-
MethodProvider provider = getMethodProviderOrNull(invoke.getInvokedMethod());
if (provider == null) {
- continue;
+ 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;
+ }
}
provider.rewriteInvoke(invoke, iterator, code, appView, affectedValues);
@@ -181,6 +222,11 @@
if (!enabled) {
return;
}
+ if (appView.options().isDesugaredLibraryCompilation()) {
+ synthesizeEmulatedDispatchMethods(builder);
+ } else {
+ addInterfacesAndForwardingMethods(executorService);
+ }
if (holders.isEmpty()) {
return;
}
@@ -197,9 +243,8 @@
}
}
- 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
@@ -217,12 +262,7 @@
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));
@@ -233,27 +273,140 @@
method.holder,
dexEncodedMethod,
"java8 methods utility class",
- addToMainDexList,
- appView);
+ addToMainDexList);
// The following may add elements to methodsProviders.
converter.optimizeSynthesizedClass(utilityClass, executorService);
}
}
- static DexProgramClass synthesizeClassWithUniqueMethod(
+ 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(
Builder<?> builder,
ClassAccessFlags accessFlags,
DexType type,
DexEncodedMethod uniqueMethod,
String origin,
- boolean addToMainDexList,
- AppView<?> appView) {
- DexItemFactory factory = appView.dexItemFactory();
+ boolean addToMainDexList) {
DexProgramClass newClass =
new DexProgramClass(
type,
null,
- new SynthesizedOrigin(origin, BackportedMethodRewriter.class),
+ new SynthesizedOrigin(origin, getClass()),
accessFlags,
factory.objectType,
DexTypeList.empty(),
@@ -272,64 +425,186 @@
? DexEncodedMethod.EMPTY_ARRAY
: new DexEncodedMethod[] {uniqueMethod},
factory.getSkipNameValidationForTesting(),
- getChecksumSupplier(uniqueMethod, appView));
+ getChecksumSupplier(uniqueMethod));
appView.appInfo().addSynthesizedClass(newClass);
builder.addSynthesizedClass(newClass, addToMainDexList);
return newClass;
}
- private static ChecksumSupplier getChecksumSupplier(DexEncodedMethod method, AppView<?> appView) {
+ 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) {
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()) {
- return;
- }
- 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);
+ 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);
}
- // 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 (!options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()) {
+ initializeRetargetCoreLibraryMembers(appView);
}
- 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 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;
+ }
+
+ public Set<DexMethod> getEmulatedDispatchMethods() {
+ return emulatedDispatchMethods;
}
boolean isEmpty() {
@@ -394,9 +669,8 @@
// 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));
@@ -969,9 +1243,8 @@
// 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"));
@@ -1006,13 +1279,19 @@
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");
@@ -1106,7 +1385,8 @@
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;
@@ -1126,7 +1406,8 @@
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;
@@ -1273,24 +1554,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++) {
@@ -1299,20 +1580,24 @@
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];
@@ -1329,14 +1614,17 @@
// 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[] {
@@ -1357,17 +1645,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++) {
@@ -1388,7 +1676,76 @@
DexProto proto = factory.createProto(factory.streamType, factory.objectType);
DexMethod method = factory.createMethod(streamType, proto, name);
addProvider(
- new MethodGenerator(method, BackportedMethods::StreamMethods_ofNullable, "ofNullable"));
+ 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);
+ }
}
private void addProvider(MethodProvider generator) {
@@ -1423,6 +1780,52 @@
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 baa6317..bdb4cd9 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,6 +110,12 @@
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
deleted file mode 100644
index 3949767..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
+++ /dev/null
@@ -1,441 +0,0 @@
-// 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);
- }
-}