Desugared library: virtual retargetting
Bug: 142846107
Change-Id: I679fb5d34eb775f288c83991e1a1aa598214fd3b
diff --git a/src/library_desugar/desugar_jdk_libs.json b/src/library_desugar/desugar_jdk_libs.json
index d54bf57..c0ba84f 100644
--- a/src/library_desugar/desugar_jdk_libs.json
+++ b/src/library_desugar/desugar_jdk_libs.json
@@ -1,6 +1,6 @@
{
"configuration_format_version": 3,
- "version": "0.10.0",
+ "version": "0.10.1",
"required_compilation_api_level": 26,
"synthesized_library_classes_package_prefix": "j$.",
"library_flags": [
@@ -16,6 +16,10 @@
"java.lang.Integer8": "java.lang.Integer",
"java.lang.Long8": "java.lang.Long",
"java.lang.Math8": "java.lang.Math"
+ },
+ "retarget_lib_member": {
+ "java.util.Date#toInstant": "java.util.DesugarDate",
+ "java.util.GregorianCalendar#toZonedDateTime": "java.util.DesugarGregorianCalendar"
}
},
{
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 62fc1a8..97da896 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -913,24 +913,22 @@
return builder.build();
}
- public DexEncodedMethod toEmulateInterfaceLibraryMethod(
+ public static DexEncodedMethod toEmulateDispatchLibraryMethod(
+ DexType interfaceType,
DexMethod newMethod,
DexMethod companionMethod,
DexMethod libraryMethod,
List<Pair<DexType, DexMethod>> extraDispatchCases,
AppView<?> appView) {
- assert isDefaultMethod() || isStatic();
- DexEncodedMethod.Builder builder = DexEncodedMethod.builder(this);
- builder.setMethod(newMethod);
- builder.accessFlags.setSynthetic();
- builder.accessFlags.setStatic();
- builder.accessFlags.unsetPrivate();
- builder.accessFlags.setPublic();
- builder.setCode(
+ MethodAccessFlags accessFlags =
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_SYNTHETIC | Constants.ACC_STATIC | Constants.ACC_PUBLIC, false);
+ CfCode code =
new EmulateInterfaceSyntheticCfCodeProvider(
- this.method.holder, companionMethod, libraryMethod, extraDispatchCases, appView)
- .generateCfCode());
- return builder.build();
+ interfaceType, companionMethod, libraryMethod, extraDispatchCases, appView)
+ .generateCfCode();
+ return new DexEncodedMethod(
+ newMethod, accessFlags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), code);
}
public DexEncodedMethod toStaticForwardingBridge(DexClass holder, DexMethod newMethod) {
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 41995ce..734077e 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
@@ -15,6 +15,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;
@@ -27,6 +28,7 @@
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.Invoke;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.conversion.IRConverter;
@@ -38,16 +40,19 @@
import com.android.tools.r8.ir.desugar.backports.LongMethodRewrites;
import com.android.tools.r8.ir.desugar.backports.NumericMethodRewrites;
import com.android.tools.r8.ir.desugar.backports.ObjectsMethodRewrites;
+import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
+import com.android.tools.r8.ir.synthetic.SynthesizedCode;
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.InternalOptions;
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.Collection;
import java.util.Collections;
import java.util.IdentityHashMap;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -114,21 +119,31 @@
if (provider == null) {
continue;
}
+ }
- // Since we are rewriting a virtual method into a static invoke in this case, the look-up
- // logic gets confused. Final methods rewritten in such a way are always or invokes from a
- // library class are rewritten into the static invoke, which is correct. However,
- // overrides of the programmer are currently disabled. We still rewrite everything to make
- // basic cases work.
- // TODO(b/142846107): Support overrides of retarget virtual methods by uncommenting the
- // following and implementing doSomethingSmart().
-
- // DexClass receiverType = appView.definitionFor(invoke.getInvokedMethod().holder);
- // if (!(dexEncodedMethod.isFinal()
- // || (receiverType != null && receiverType.isLibraryClass()))) {
- // doSomethingSmart();
- // 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()) {
+ DexEncodedMethod dexEncodedMethod =
+ appView
+ .appInfo()
+ .lookupSuperTarget(invoke.getInvokedMethod(), code.method.method.holder);
+ if (!dexEncodedMethod.isFinal()) { // Final methods can be rewritten as a normal invoke.
+ Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
+ appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember();
+ Map<DexType, DexType> typeMap = retargetCoreLibMember.get(dexEncodedMethod.method.name);
+ if (typeMap != null && typeMap.containsKey(dexEncodedMethod.method.holder)) {
+ DexMethod retargetMethod =
+ factory.createMethod(
+ typeMap.get(dexEncodedMethod.method.holder),
+ factory.prependTypeToProto(
+ dexEncodedMethod.method.holder, dexEncodedMethod.method.proto),
+ dexEncodedMethod.method.name);
+ iterator.replaceCurrentInstruction(
+ new InvokeStatic(retargetMethod, invoke.outValue(), invoke.arguments()));
+ }
+ continue;
+ }
}
provider.rewriteInvoke(invoke, iterator, code, appView);
@@ -181,6 +196,11 @@
public void synthesizeUtilityClasses(Builder<?> builder, ExecutorService executorService)
throws ExecutionException {
+ if (appView.options().isDesugaredLibraryCompilation()) {
+ synthesizeEmulatedDispatchMethods(builder);
+ } else {
+ addInterfacesAndForwardingMethods(executorService);
+ }
if (holders.isEmpty()) {
return;
}
@@ -248,6 +268,210 @@
}
}
+ 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()) {
+ 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 = ClassProcessor.retargetMethod(appView, target);
+ // New method will have the same name, proto, and also all the flags of the
+ // default method, including bridge flag.
+ DexMethod newMethod =
+ appView.dexItemFactory().createMethod(clazz.type, target.proto, target.name);
+ DexEncodedMethod dexEncodedMethod = appView.definitionFor(target);
+ MethodAccessFlags newFlags = dexEncodedMethod.accessFlags.copy();
+ newFlags.setSynthetic();
+ ForwardMethodSourceCode.Builder forwardSourceCodeBuilder =
+ ForwardMethodSourceCode.builder(newMethod);
+ forwardSourceCodeBuilder
+ .setReceiver(clazz.type)
+ .setTarget(forwardMethod)
+ .setInvokeType(Invoke.Type.STATIC)
+ .setIsInterface(false);
+ return new DexEncodedMethod(
+ newMethod,
+ newFlags,
+ dexEncodedMethod.annotations,
+ dexEncodedMethod.parameterAnnotationsList,
+ new SynthesizedCode(forwardSourceCodeBuilder::build));
+ }
+
+ 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);
+ DexProgramClass dispatchInterface =
+ new DexProgramClass(
+ interfaceType,
+ null,
+ new SynthesizedOrigin("desugared library interface dispatch", getClass()),
+ itfAccessFlags,
+ factory.objectType,
+ DexTypeList.empty(),
+ null,
+ null,
+ Collections.emptyList(),
+ null,
+ Collections.emptyList(),
+ DexAnnotationSet.empty(),
+ DexEncodedField.EMPTY_ARRAY,
+ DexEncodedField.EMPTY_ARRAY,
+ DexEncodedMethod.EMPTY_ARRAY,
+ new DexEncodedMethod[] {itfMethod},
+ factory.getSkipNameValidationForTesting(),
+ getChecksumSupplier(itfMethod));
+ appView.appInfo().addSynthesizedClass(dispatchInterface);
+ builder.addSynthesizedClass(dispatchInterface, false);
+ // Dispatch holder.
+ DexType holderType = dispatchHolderTypeFor(appView, emulatedDispatchMethod);
+ DexEncodedMethod dispatchMethod =
+ generateHolderDispatchMethod(emulatedDispatchMethod, holderType, itfMethod.method);
+ DexProgramClass dispatchHolder =
+ new DexProgramClass(
+ holderType,
+ null,
+ new SynthesizedOrigin("desugared library dispatch holder class", getClass()),
+ holderAccessFlags,
+ factory.objectType,
+ DexTypeList.empty(),
+ null,
+ null,
+ Collections.emptyList(),
+ null,
+ Collections.emptyList(),
+ DexAnnotationSet.empty(),
+ DexEncodedField.EMPTY_ARRAY,
+ DexEncodedField.EMPTY_ARRAY,
+ new DexEncodedMethod[] {dispatchMethod},
+ DexEncodedMethod.EMPTY_ARRAY,
+ factory.getSkipNameValidationForTesting(),
+ getChecksumSupplier(dispatchMethod));
+ appView.appInfo().addSynthesizedClass(dispatchHolder);
+ builder.addSynthesizedClass(dispatchHolder, false);
+ }
+ }
+
+ 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);
+ }
+
+ 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.
+ DexItemFactory factory = appView.dexItemFactory();
+ DexProto newProto =
+ factory.prependTypeToProto(emulatedDispatchMethod.holder, emulatedDispatchMethod.proto);
+ DexMethod newMethod =
+ factory.createMethod(dispatchHolder, newProto, emulatedDispatchMethod.name);
+ DexType desugarType =
+ appView
+ .options()
+ .desugaredLibraryConfiguration
+ .getRetargetCoreLibMember()
+ .get(emulatedDispatchMethod.name)
+ .get(emulatedDispatchMethod.holder);
+ DexMethod desugarMethod =
+ factory.createMethod(desugarType, newProto, 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;
@@ -255,6 +479,31 @@
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 desugaredLibPrefix =
+ appView.options().desugaredLibraryConfiguration.getSynthesizedLibraryClassesPackagePrefix();
+ String descriptor =
+ "L"
+ + desugaredLibPrefix
+ + 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;
@@ -285,6 +534,8 @@
// 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) {
DexItemFactory factory = options.itemFactory;
@@ -330,6 +581,10 @@
return false;
}
+ public Set<DexMethod> getEmulatedDispatchMethods() {
+ return emulatedDispatchMethods;
+ }
+
boolean isEmpty() {
return rewritable.isEmpty();
}
@@ -1232,9 +1487,14 @@
if (!encodedMethod.isStatic()) {
virtualRewrites.putIfAbsent(encodedMethod.method.name, new ArrayList<>());
virtualRewrites.get(encodedMethod.method.name).add(encodedMethod.method);
- if (isEmulatedInterfaceDispatch(appView, encodedMethod)) {
+ 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;
@@ -1248,37 +1508,6 @@
}
}
- private boolean isEmulatedInterfaceDispatch(AppView<?> appView, DexEncodedMethod method) {
- // Answers true if this method is already managed through emulated interface dispatch.
- Map<DexType, DexType> emulateLibraryInterface =
- appView.options().desugaredLibraryConfiguration.getEmulateLibraryInterface();
- if (emulateLibraryInterface.isEmpty()) {
- return false;
- }
- DexMethod methodToFind = method.method;
-
- // Look-up all superclass and interfaces, if an emulated interface is found, and it implements
- // the method, answers true.
- LinkedList<DexType> workList = new LinkedList<>();
- workList.add(methodToFind.holder);
- while (!workList.isEmpty()) {
- DexType dexType = workList.removeFirst();
- DexClass dexClass = appView.definitionFor(dexType);
- assert dexClass != null; // It is a library class, or we are doing L8 compilation.
- if (dexClass.isInterface() && emulateLibraryInterface.containsKey(dexType)) {
- DexEncodedMethod dexEncodedMethod = dexClass.lookupMethod(methodToFind);
- if (dexEncodedMethod != null) {
- return true;
- }
- }
- Collections.addAll(workList, dexClass.interfaces.values);
- if (dexClass.superType != appView.dexItemFactory().objectType) {
- workList.add(dexClass.superType);
- }
- }
- return false;
- }
-
private List<DexEncodedMethod> findDexEncodedMethodsWithName(
DexString methodName, DexClass clazz) {
List<DexEncodedMethod> found = new ArrayList<>();
@@ -1291,6 +1520,17 @@
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) {
MethodProvider replaced = rewritable.put(generator.method, generator);
assert replaced == null;
@@ -1457,8 +1697,8 @@
}
// Specific subclass to transform virtual methods into static desugared methods.
- // To be correct, the method has to be on a final class, and be implemented directly
- // on the class (no overrides).
+ // To be correct, the method has to be on a final class or be a final method, and to be
+ // implemented directly on the class (no overrides).
private static class StatifyingMethodGenerator extends MethodGenerator {
private final DexType receiverType;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index 3168dde..c2f5ae9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -391,7 +391,7 @@
DexMethod forwardMethod =
targetHolder.isInterface()
? rewriter.defaultAsMethodOfCompanionClass(method)
- : retargetMethod(method);
+ : retargetMethod(appView, method);
// New method will have the same name, proto, and also all the flags of the
// default method, including bridge flag.
DexMethod newMethod = dexItemFactory.createMethod(clazz.type, method.proto, method.name);
@@ -415,16 +415,18 @@
addSyntheticMethod(clazz.asProgramClass(), newEncodedMethod);
}
- private DexMethod retargetMethod(DexMethod method) {
+ static DexMethod retargetMethod(AppView<?> appView, DexMethod method) {
Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember();
Map<DexType, DexType> typeMap = retargetCoreLibMember.get(method.name);
assert typeMap != null;
assert typeMap.get(method.holder) != null;
- return dexItemFactory.createMethod(
- typeMap.get(method.holder),
- dexItemFactory.prependTypeToProto(method.holder, method.proto),
- method.name);
+ return appView
+ .dexItemFactory()
+ .createMethod(
+ typeMap.get(method.holder),
+ appView.dexItemFactory().prependTypeToProto(method.holder, method.proto),
+ method.name);
}
// Topological order traversal and its helpers.
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 1c11d78..fd954e5 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
@@ -114,6 +114,10 @@
: "";
}
+ public String getSynthesizedLibraryClassesPackagePrefix() {
+ return synthesizedLibraryClassesPackagePrefix;
+ }
+
public Map<String, String> getRewritePrefix() {
return rewritePrefix;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index ff7576a..679f927 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -610,7 +610,8 @@
}
}
emulationMethods.add(
- method.toEmulateInterfaceLibraryMethod(
+ DexEncodedMethod.toEmulateDispatchLibraryMethod(
+ method.method.holder,
emulateInterfaceLibraryMethod(method.method, method.method.holder, factory),
companionMethod,
libraryMethod,
@@ -1012,6 +1013,37 @@
return true;
}
+ public static boolean isEmulatedInterfaceDispatch(AppView<?> appView, DexEncodedMethod method) {
+ // Answers true if this method is already managed through emulated interface dispatch.
+ Map<DexType, DexType> emulateLibraryInterface =
+ appView.options().desugaredLibraryConfiguration.getEmulateLibraryInterface();
+ if (emulateLibraryInterface.isEmpty()) {
+ return false;
+ }
+ DexMethod methodToFind = method.method;
+
+ // Look-up all superclass and interfaces, if an emulated interface is found, and it implements
+ // the method, answers true.
+ LinkedList<DexType> workList = new LinkedList<>();
+ workList.add(methodToFind.holder);
+ while (!workList.isEmpty()) {
+ DexType dexType = workList.removeFirst();
+ DexClass dexClass = appView.definitionFor(dexType);
+ assert dexClass != null; // It is a library class, or we are doing L8 compilation.
+ if (dexClass.isInterface() && emulateLibraryInterface.containsKey(dexType)) {
+ DexEncodedMethod dexEncodedMethod = dexClass.lookupMethod(methodToFind);
+ if (dexEncodedMethod != null) {
+ return true;
+ }
+ }
+ Collections.addAll(workList, dexClass.interfaces.values);
+ if (dexClass.superType != appView.dexItemFactory().objectType) {
+ workList.add(dexClass.superType);
+ }
+ }
+ return false;
+ }
+
public void warnMissingInterface(
DexClass classToDesugar, DexClass implementing, DexType missing) {
// We use contains() on non hashed collection, but we know it's a 8 cases collection.
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
index eebea28..04a7258 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
@@ -5,11 +5,12 @@
package com.android.tools.r8.desugar.desugaredlibrary;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.BooleanUtils;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.GregorianCalendar;
+import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.IntUnaryOperator;
import org.junit.Test;
@@ -21,26 +22,33 @@
public class RetargetOverrideTest extends DesugaredLibraryTestBase {
private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
}
- public RetargetOverrideTest(TestParameters parameters) {
+ public RetargetOverrideTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
this.parameters = parameters;
}
@Test
public void testRetargetOverrideD8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
String stdout =
testForD8()
.addInnerClasses(RetargetOverrideTest.class)
- .enableCoreLibraryDesugaring(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.setMinApi(parameters.getApiLevel())
.compile()
.addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibrary, parameters.getApiLevel())
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
.run(parameters.getRuntime(), Executor.class)
.assertSuccess()
.getStdOut();
@@ -49,15 +57,19 @@
@Test
public void testRetargetOverrideR8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
String stdout =
testForR8(Backend.DEX)
.addKeepMainRule(Executor.class)
.addInnerClasses(RetargetOverrideTest.class)
- .enableCoreLibraryDesugaring(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.setMinApi(parameters.getApiLevel())
.compile()
.addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibrary, parameters.getApiLevel())
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
.run(parameters.getRuntime(), Executor.class)
.assertSuccess()
.getStdOut();
@@ -67,22 +79,18 @@
static class Executor {
public static void main(String[] args) {
- java.sql.Date date = new java.sql.Date(123456789);
- // The following one is not working on JVMs, but works on Android...
- System.out.println(date.toInstant());
- System.out.println("1970-01-02T10:17:36.789Z");
+ directTypes();
+ polyTypes();
+ baseTypes();
+ }
- GregorianCalendar gregCal = new GregorianCalendar(1990, 2, 22);
- System.out.println(gregCal.toInstant());
+ public static void directTypes() {
+ MyCalendarOverride myCal = new MyCalendarOverride(1990, 2, 22);
+ System.out.println(myCal.toZonedDateTime());
+ System.out.println("1990-11-22T00:00Z[GMT]");
+ System.out.println(myCal.toInstant());
System.out.println("1990-03-22T00:00:00Z");
- // TODO(b/142846107): Enable overrides of retarget core members.
- // MyCalendarOverride myCal = new MyCalendarOverride(1990, 2, 22);
- // System.out.println(myCal.toZonedDateTime());
- // System.out.println("1990-11-22T00:00Z[GMT]");
- // System.out.println(myCal.toInstant());
- // System.out.println("1990-03-22T00:00:00Z");
-
MyCalendarNoOverride myCalN = new MyCalendarNoOverride(1990, 2, 22);
System.out.println(myCalN.toZonedDateTime());
System.out.println("1990-03-22T00:00Z[GMT]");
@@ -93,10 +101,13 @@
System.out.println(myCalN.superToInstant());
System.out.println("1990-03-22T00:00:00Z");
- // TODO(b/142846107): Enable overrides of retarget core members.
- // MyDateOverride myDate = new MyDateOverride(123456789);
- // System.out.println(myDate.toInstant());
- // System.out.println("1970-01-02T10:17:45.789Z");
+ MyDateDoubleOverride myDateCast2 = new MyDateDoubleOverride(123456789);
+ System.out.println(myDateCast2.toInstant());
+ System.out.println("1970-01-02T10:17:48.789Z");
+
+ MyDateOverride myDate = new MyDateOverride(123456789);
+ System.out.println(myDate.toInstant());
+ System.out.println("1970-01-02T10:17:45.789Z");
MyDateNoOverride myDateN = new MyDateNoOverride(123456789);
System.out.println(myDateN.toInstant());
@@ -112,21 +123,58 @@
System.out.println(myAtomicInteger.updateAndGet(x -> x + 100));
System.out.println("145");
}
+
+ public static void polyTypes() {
+ Date myDateCast = new MyDateOverride(123456789);
+ System.out.println(myDateCast.toInstant());
+ System.out.println("1970-01-02T10:17:45.789Z");
+
+ Date myDateCast2 = new MyDateDoubleOverride(123456789);
+ System.out.println(myDateCast2.toInstant());
+ System.out.println("1970-01-02T10:17:48.789Z");
+
+ Date myDateN = new MyDateNoOverride(123456789);
+ System.out.println(myDateN.toInstant());
+ System.out.println("1970-01-02T10:17:36.789Z");
+
+ GregorianCalendar myCalCast = new MyCalendarOverride(1990, 2, 22);
+ System.out.println(myCalCast.toZonedDateTime());
+ System.out.println("1990-11-22T00:00Z[GMT]");
+ System.out.println(myCalCast.toInstant());
+ System.out.println("1990-03-22T00:00:00Z");
+
+ GregorianCalendar myCalN = new MyCalendarNoOverride(1990, 2, 22);
+ System.out.println(myCalN.toZonedDateTime());
+ System.out.println("1990-03-22T00:00Z[GMT]");
+ System.out.println(myCalN.toInstant());
+ System.out.println("1990-03-22T00:00:00Z");
+ }
+
+ public static void baseTypes() {
+ java.sql.Date date = new java.sql.Date(123456789);
+ // The following one is not working on JVMs, but works on Android...
+ System.out.println(date.toInstant());
+ System.out.println("1970-01-02T10:17:36.789Z");
+
+ GregorianCalendar gregCal = new GregorianCalendar(1990, 2, 22);
+ System.out.println(gregCal.toInstant());
+ System.out.println("1990-03-22T00:00:00Z");
+ }
}
- // static class MyCalendarOverride extends GregorianCalendar {
- //
- // public MyCalendarOverride(int year, int month, int dayOfMonth) {
- // super(year, month, dayOfMonth);
- // }
- //
- // // Cannot override toInstant (final).
- //
- // @Override
- // public ZonedDateTime toZonedDateTime() {
- // return super.toZonedDateTime().withMonth(11);
- // }
- // }
+ static class MyCalendarOverride extends GregorianCalendar {
+
+ public MyCalendarOverride(int year, int month, int dayOfMonth) {
+ super(year, month, dayOfMonth);
+ }
+
+ // Cannot override toInstant (final).
+
+ @Override
+ public ZonedDateTime toZonedDateTime() {
+ return super.toZonedDateTime().withMonth(11);
+ }
+ }
static class MyCalendarNoOverride extends GregorianCalendar {
public MyCalendarNoOverride(int year, int month, int dayOfMonth) {
@@ -142,17 +190,29 @@
}
}
- // static class MyDateOverride extends Date {
- //
- // public MyDateOverride(long date) {
- // super(date);
- // }
- //
- // @Override
- // public Instant toInstant() {
- // return super.toInstant().plusSeconds(9);
- // }
- // }
+ static class MyDateOverride extends Date {
+
+ public MyDateOverride(long date) {
+ super(date);
+ }
+
+ @Override
+ public Instant toInstant() {
+ return super.toInstant().plusSeconds(9);
+ }
+ }
+
+ static class MyDateDoubleOverride extends MyDateOverride {
+
+ public MyDateDoubleOverride(long date) {
+ super(date);
+ }
+
+ @Override
+ public Instant toInstant() {
+ return super.toInstant().plusSeconds(3);
+ }
+ }
static class MyDateNoOverride extends Date {