Desugared library: rewrite super invoke
- Make sure super invoke are rewritten correctly.
- Make sure the spliterator case is tested entirely.
- Fix dispatch on library overrides in general.
Subclasses of lib classes had sometimes invalid emulated dispatch.
Change-Id: I024d2187c83f0dc4cf9bf238ec50820a50c0545a
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 d52f02e..511b971 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
@@ -47,6 +47,7 @@
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;
@@ -1232,6 +1233,10 @@
if (!encodedMethod.isStatic()) {
virtualRewrites.putIfAbsent(encodedMethod.method.name, new ArrayList<>());
virtualRewrites.get(encodedMethod.method.name).add(encodedMethod.method);
+ if (isEmulatedInterfaceDispatch(appView, encodedMethod)) {
+ // In this case interface method rewriter takes care of it.
+ continue;
+ }
}
DexProto proto = encodedMethod.method.proto;
DexMethod method = appView.dexItemFactory().createMethod(inType, proto, methodName);
@@ -1244,6 +1249,37 @@
}
}
+ 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<>();
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 54ac5c0..ae14474 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
@@ -49,6 +49,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.IdentityHashMap;
+import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
@@ -254,27 +255,6 @@
// exception but we can not report it as error since it can also be the intended
// behavior.
warnMissingType(encodedMethod.method, invokedMethod.holder);
- } else if (clazz.isInterface() && clazz.isLibraryClass() && isInDesugaredLibrary(clazz)) {
- // Here we try to avoid doing the expensive look-up on all invokes.
- boolean rewritten = false;
- if (emulatedMethods.contains(invokedMethod.name)) {
- DexType dexType = nearestEmulatedInterfaceImplementingWithCache(invokedMethod);
- if (dexType != null) {
- rewriteCurrentInstructionToEmulatedInterfaceCall(
- dexType, invokedMethod, invokeSuper, instructions);
- rewritten = true;
- }
- }
- if (!rewritten) {
- DexMethod amendedMethod =
- amendDefaultMethod(
- appInfo.definitionFor(encodedMethod.method.holder), invokedMethod);
- instructions.replaceCurrentInstruction(
- new InvokeStatic(
- defaultAsMethodOfCompanionClass(amendedMethod),
- invokeSuper.outValue(),
- invokeSuper.arguments()));
- }
} else if (clazz.isInterface() && !clazz.isLibraryClass()) {
// NOTE: we intentionally don't desugar super calls into interface methods
// coming from android.jar since it is only possible in case v24+ version
@@ -291,6 +271,54 @@
instructions.replaceCurrentInstruction(
new InvokeStatic(defaultAsMethodOfCompanionClass(amendedMethod),
invokeSuper.outValue(), invokeSuper.arguments()));
+ } else {
+ DexType dexType = nearestEmulatedInterfaceOrNull(invokedMethod);
+ if (dexType != null) {
+ // That invoke super may not resolve since the super method may not be present
+ // since it's in the emulated interface. We need to force resolution. If it resolves
+ // to a library method, then it needs to be rewritten.
+ // If it resolves to a program overrides, the invoke-super can remain.
+ DexEncodedMethod dexEncodedMethod =
+ appView
+ .appInfo()
+ .lookupSuperTarget(invokeSuper.getInvokedMethod(), code.method.method.holder);
+ if (dexEncodedMethod != null) {
+ DexClass dexClass = appView.definitionFor(dexEncodedMethod.method.holder);
+ if (dexClass != null && dexClass.isLibraryClass()) {
+ // Rewriting is required because the super invoke resolves into a missing
+ // method (method is on desugared library). Find out if it needs to be
+ // retarget or if it just calls a companion class method and rewrite.
+ Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
+ options.desugaredLibraryConfiguration.getRetargetCoreLibMember();
+ Map<DexType, DexType> typeMap =
+ retargetCoreLibMember.get(dexEncodedMethod.method.name);
+ if (typeMap == null || !typeMap.containsKey(dexEncodedMethod.method.holder)) {
+ DexMethod originalCompanionMethod =
+ instanceAsMethodOfCompanionClass(
+ dexEncodedMethod.method, DEFAULT_METHOD_PREFIX, factory);
+ DexMethod companionMethod =
+ factory.createMethod(
+ getCompanionClassType(dexType),
+ factory.protoWithDifferentFirstParameter(
+ originalCompanionMethod.proto, dexType),
+ originalCompanionMethod.name);
+ instructions.replaceCurrentInstruction(
+ new InvokeStatic(
+ companionMethod, invokeSuper.outValue(), invokeSuper.arguments()));
+ } else {
+ DexMethod retargetMethod =
+ factory.createMethod(
+ typeMap.get(dexEncodedMethod.method.holder),
+ factory.prependTypeToProto(
+ dexEncodedMethod.method.holder, dexEncodedMethod.method.proto),
+ dexEncodedMethod.method.name);
+ instructions.replaceCurrentInstruction(
+ new InvokeStatic(
+ retargetMethod, invokeSuper.outValue(), invokeSuper.arguments()));
+ }
+ }
+ }
+ }
}
continue;
}
@@ -345,39 +373,42 @@
if (instruction.isInvokeVirtual() || instruction.isInvokeInterface()) {
InvokeMethod invokeMethod = instruction.asInvokeMethod();
DexMethod invokedMethod = invokeMethod.getInvokedMethod();
- // Here we try to avoid doing the expensive look-up on all invokes.
- if (!emulatedMethods.contains(invokedMethod.name)) {
- continue;
+ DexType dexType = nearestEmulatedInterfaceOrNull(invokedMethod);
+ if (dexType != null) {
+ rewriteCurrentInstructionToEmulatedInterfaceCall(
+ dexType, invokedMethod, invokeMethod, instructions);
}
- DexClass dexClass = appView.definitionFor(invokedMethod.holder);
- // We cannot rewrite the invoke we do not know what the class is.
- if (dexClass == null) {
- continue;
- }
- // TODO(b/120884788): Make sure program class are looked up before library class for
- // CoreLib compilation or look again into all desugared library emulation.
- // Outside of core libraries, only library classes are rewritten. In core libraries,
- // some classes are present both as program and library class, and definitionFor
- // answers the program class so this is not true.
- if (!appView.options().isDesugaredLibraryCompilation() && !dexClass.isLibraryClass()) {
- continue;
- }
- // We always rewrite interfaces, but classes are rewritten only if they are not already
- // desugared (CoreLibrary classes efficient implementation).
- if (!dexClass.isInterface() && isInDesugaredLibrary(dexClass)) {
- continue;
- }
- DexType dexType = nearestEmulatedInterfaceImplementingWithCache(invokedMethod);
- if (dexType == null) {
- continue;
- }
- rewriteCurrentInstructionToEmulatedInterfaceCall(
- dexType, invokedMethod, invokeMethod, instructions);
}
}
}
}
+ private DexType nearestEmulatedInterfaceOrNull(DexMethod invokedMethod) {
+ // Here we try to avoid doing the expensive look-up on all invokes.
+ if (!emulatedMethods.contains(invokedMethod.name)) {
+ return null;
+ }
+ DexClass dexClass = appView.definitionFor(invokedMethod.holder);
+ // We cannot rewrite the invoke we do not know what the class is.
+ if (dexClass == null) {
+ return null;
+ }
+ // TODO(b/120884788): Make sure program class are looked up before library class for
+ // CoreLib compilation or look again into all desugared library emulation.
+ // Outside of core libraries, only library classes are rewritten. In core libraries,
+ // some classes are present both as program and library class, and definitionFor
+ // answers the program class so this is not true.
+ if (!appView.options().isDesugaredLibraryCompilation() && !dexClass.isLibraryClass()) {
+ return null;
+ }
+ // We always rewrite interfaces, but classes are rewritten only if they are not already
+ // desugared (CoreLibrary classes efficient implementation).
+ if (!dexClass.isInterface() && isInDesugaredLibrary(dexClass)) {
+ return null;
+ }
+ return nearestEmulatedInterfaceImplementingWithCache(invokedMethod);
+ }
+
private void rewriteCurrentInstructionToEmulatedInterfaceCall(
DexType emulatedItf,
DexMethod invokedMethod,
@@ -933,7 +964,7 @@
private void duplicateEmulatedInterfaces() {
// All classes implementing an emulated interface now implements the interface and the
- // emulated one.
+ // emulated one, as well as hidden overrides, for correct emulated dispatch.
for (DexClass clazz : appView.appInfo().classes()) {
List<DexType> extraInterfaces = new ArrayList<>();
for (DexType type : clazz.interfaces.values) {
@@ -941,6 +972,17 @@
extraInterfaces.add(emulatedInterfaces.get(type));
}
}
+ if (!appView.options().isDesugaredLibraryCompilation()) {
+ DexClass superClazz = appView.definitionFor(clazz.superType);
+ if (superClazz != null && superClazz.isLibraryClass()) {
+ List<DexType> itfs = emulatedInterfacesOf(superClazz);
+ for (DexType itf : itfs) {
+ extraInterfaces.add(emulatedInterfaces.get(itf));
+ }
+ }
+ // Remove duplicates.
+ extraInterfaces = new ArrayList<>(new LinkedHashSet<>(extraInterfaces));
+ }
if (!extraInterfaces.isEmpty()) {
DexType[] newInterfaces =
Arrays.copyOf(
@@ -953,6 +995,32 @@
}
}
+ private List<DexType> emulatedInterfacesOf(DexClass superClazz) {
+ if (superClazz.type == factory.objectType) {
+ return Collections.emptyList();
+ }
+ ArrayList<DexType> itfs = new ArrayList<>();
+ LinkedList<DexType> workList = new LinkedList<>();
+ workList.add(superClazz.type);
+ while (!workList.isEmpty()) {
+ DexType dexType = workList.removeFirst();
+ DexClass dexClass = appView.definitionFor(dexType);
+ if (dexClass != null) {
+ if (dexClass.superType != factory.objectType) {
+ workList.add(dexClass.superType);
+ }
+ for (DexType itf : dexClass.interfaces.values) {
+ if (emulatedInterfaces.containsKey(itf)) {
+ itfs.add(itf);
+ } else {
+ workList.add(itf);
+ }
+ }
+ }
+ }
+ return itfs;
+ }
+
/**
* Move static and default interface methods to companion classes, add missing methods to forward
* to moved default methods implementation.
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionSuperCallsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionSuperCallsTest.java
new file mode 100644
index 0000000..5b5b420
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionSuperCallsTest.java
@@ -0,0 +1,155 @@
+// Copyright (c) 2019, 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.desugar.desugaredlibrary;
+
+import com.android.tools.r8.D8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class CustomCollectionSuperCallsTest extends CoreLibDesugarTestBase {
+
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+
+ @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ }
+
+ public CustomCollectionSuperCallsTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testCustomCollectionSuperCallsD8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ D8TestRunResult d8TestRunResult =
+ testForD8()
+ .addInnerClasses(CustomCollectionSuperCallsTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccess();
+ assertLines2By2Correct(d8TestRunResult.getStdOut());
+ }
+
+ static class Executor {
+
+ // ArrayList spliterator is Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED.
+
+ public static void main(String[] args) {
+ rawTypes();
+ inheritedTypes();
+ }
+
+ public static void rawTypes() {
+ Spliterator<String> stringSpliterator;
+
+ stringSpliterator = new MyArrayListOverride().superSpliterator();
+ System.out.println(stringSpliterator.hasCharacteristics(Spliterator.ORDERED));
+ System.out.println(true);
+ System.out.println(stringSpliterator.hasCharacteristics(Spliterator.IMMUTABLE));
+ System.out.println(false);
+
+ stringSpliterator = new MyArrayListOverrideSubclass().superSpliterator();
+ System.out.println(stringSpliterator.hasCharacteristics(Spliterator.ORDERED));
+ System.out.println(false);
+ System.out.println(stringSpliterator.hasCharacteristics(Spliterator.IMMUTABLE));
+ System.out.println(true);
+
+ stringSpliterator = new MyArrayListNoOverride().superSpliterator();
+ System.out.println(stringSpliterator.hasCharacteristics(Spliterator.ORDERED));
+ System.out.println(true);
+ System.out.println(stringSpliterator.hasCharacteristics(Spliterator.IMMUTABLE));
+ System.out.println(false);
+
+ stringSpliterator = new MyArrayListSubclassNoOverride().superSpliterator();
+ System.out.println(stringSpliterator.hasCharacteristics(Spliterator.ORDERED));
+ System.out.println(true);
+ System.out.println(stringSpliterator.hasCharacteristics(Spliterator.IMMUTABLE));
+ System.out.println(false);
+ }
+
+ public static void inheritedTypes() {
+ Spliterator<String> stringSpliterator;
+
+ stringSpliterator =
+ ((MyArrayListOverride) new MyArrayListOverrideSubclass()).superSpliterator();
+ System.out.println(stringSpliterator.hasCharacteristics(Spliterator.ORDERED));
+ System.out.println(false);
+ System.out.println(stringSpliterator.hasCharacteristics(Spliterator.IMMUTABLE));
+ System.out.println(true);
+
+ stringSpliterator =
+ ((MyArrayListNoOverride) new MyArrayListSubclassNoOverride()).superSpliterator();
+ System.out.println(stringSpliterator.hasCharacteristics(Spliterator.ORDERED));
+ System.out.println(true);
+ System.out.println(stringSpliterator.hasCharacteristics(Spliterator.IMMUTABLE));
+ System.out.println(false);
+ }
+ }
+
+ static class MyArrayListOverride extends ArrayList<String> {
+
+ @Override
+ public Spliterator<String> spliterator() {
+ return Spliterators.spliterator(this, Spliterator.IMMUTABLE);
+ }
+
+ public Spliterator<String> superSpliterator() {
+ return super.spliterator();
+ }
+ }
+
+ static class MyArrayListOverrideSubclass extends MyArrayListOverride {
+
+ @Override
+ public Spliterator<String> superSpliterator() {
+ return super.spliterator();
+ }
+
+ // Unused, but prove the super invoke won't resolve into it.
+ @Override
+ public Spliterator<String> spliterator() {
+ return Spliterators.spliterator(this, Spliterator.IMMUTABLE | Spliterator.ORDERED);
+ }
+ }
+
+ static class MyArrayListNoOverride extends ArrayList<String> {
+
+ public Spliterator<String> superSpliterator() {
+ return super.spliterator();
+ }
+ }
+
+ static class MyArrayListSubclassNoOverride extends MyArrayListNoOverride {
+ public Spliterator<String> superSpliterator() {
+ return super.spliterator();
+ }
+
+ // Unused, but prove the super invoke won't resolve into it.
+ @Override
+ public Spliterator<String> spliterator() {
+ return Spliterators.spliterator(this, Spliterator.IMMUTABLE | Spliterator.ORDERED);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LinkedHashSetTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LinkedHashSetTest.java
index afae3b0..e9f221d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LinkedHashSetTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LinkedHashSetTest.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import java.util.LinkedHashSet;
+import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators;
import org.junit.Test;
@@ -43,15 +44,22 @@
assertLines2By2Correct(stdOut);
}
+ @SuppressWarnings("WeakerAccess")
static class Executor {
- @SuppressWarnings("RedundantOperationOnEmptyContainer")
- public static void main(String[] args) {
- Spliterator<String> spliterator;
+ public static void main(String[] args) {
// Spliterator of Set is only distinct.
// Spliterator of LinkedHashSet is distinct and ordered.
// Spliterator of CustomLinkedHashSetOverride is distinct, ordered and immutable.
// If an incorrect method is found, characteristics are incorrect.
+ rawTypes();
+ linkedHashSetType();
+ setType();
+ }
+
+ @SuppressWarnings("RedundantOperationOnEmptyContainer")
+ public static void rawTypes() {
+ Spliterator<String> spliterator;
spliterator = new LinkedHashSet<String>().spliterator();
System.out.println(spliterator.hasCharacteristics(Spliterator.DISTINCT));
@@ -92,6 +100,70 @@
System.out.println("true");
System.out.println(spliterator.hasCharacteristics(Spliterator.IMMUTABLE));
System.out.println("true");
+
+ spliterator = new SubclassNoOverride<String>().superSpliterator();
+ System.out.println(spliterator.hasCharacteristics(Spliterator.DISTINCT));
+ System.out.println("true");
+ System.out.println(spliterator.hasCharacteristics(Spliterator.ORDERED));
+ System.out.println("true");
+ System.out.println(spliterator.hasCharacteristics(Spliterator.IMMUTABLE));
+ System.out.println("false");
+ }
+
+ @SuppressWarnings("RedundantOperationOnEmptyContainer")
+ public static void linkedHashSetType() {
+ System.out.println("-");
+ System.out.println("-");
+ Spliterator<String> spliterator;
+
+ spliterator =
+ ((LinkedHashSet<String>) new CustomLinkedHashSetOverride<String>()).spliterator();
+ System.out.println(spliterator.hasCharacteristics(Spliterator.DISTINCT));
+ System.out.println("true");
+ System.out.println(spliterator.hasCharacteristics(Spliterator.ORDERED));
+ System.out.println("true");
+ System.out.println(spliterator.hasCharacteristics(Spliterator.IMMUTABLE));
+ System.out.println("true");
+
+ spliterator =
+ ((LinkedHashSet<String>) new CustomLinkedHashSetNoOverride<String>()).spliterator();
+ System.out.println(spliterator.hasCharacteristics(Spliterator.DISTINCT));
+ System.out.println("true");
+ System.out.println(spliterator.hasCharacteristics(Spliterator.ORDERED));
+ System.out.println("true");
+ System.out.println(spliterator.hasCharacteristics(Spliterator.IMMUTABLE));
+ System.out.println("false");
+ }
+
+ @SuppressWarnings("RedundantOperationOnEmptyContainer")
+ public static void setType() {
+ System.out.println("-");
+ System.out.println("-");
+ Spliterator<String> spliterator;
+
+ spliterator = ((Set<String>) new LinkedHashSet<String>()).spliterator();
+ System.out.println(spliterator.hasCharacteristics(Spliterator.DISTINCT));
+ System.out.println("true");
+ System.out.println(spliterator.hasCharacteristics(Spliterator.ORDERED));
+ System.out.println("true");
+ System.out.println(spliterator.hasCharacteristics(Spliterator.IMMUTABLE));
+ System.out.println("false");
+
+ spliterator = ((Set<String>) new CustomLinkedHashSetOverride<String>()).spliterator();
+ System.out.println(spliterator.hasCharacteristics(Spliterator.DISTINCT));
+ System.out.println("true");
+ System.out.println(spliterator.hasCharacteristics(Spliterator.ORDERED));
+ System.out.println("true");
+ System.out.println(spliterator.hasCharacteristics(Spliterator.IMMUTABLE));
+ System.out.println("true");
+
+ spliterator = ((Set<String>) new CustomLinkedHashSetNoOverride<String>()).spliterator();
+ System.out.println(spliterator.hasCharacteristics(Spliterator.DISTINCT));
+ System.out.println("true");
+ System.out.println(spliterator.hasCharacteristics(Spliterator.ORDERED));
+ System.out.println("true");
+ System.out.println(spliterator.hasCharacteristics(Spliterator.IMMUTABLE));
+ System.out.println("false");
}
}
@@ -109,6 +181,7 @@
}
static class SubclassOverride<E> extends CustomLinkedHashSetOverride<E> {
+
@Override
public Spliterator<E> superSpliterator() {
return super.spliterator();
@@ -117,4 +190,11 @@
@SuppressWarnings("WeakerAccess")
static class CustomLinkedHashSetNoOverride<E> extends LinkedHashSet<E> {}
+
+ static class SubclassNoOverride<E> extends CustomLinkedHashSetNoOverride<E> {
+
+ public Spliterator<E> superSpliterator() {
+ return super.spliterator();
+ }
+ }
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
index f2889ca..59db52d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
@@ -159,7 +159,7 @@
assertInvokeStaticMatching(invokes, 2, "Collection$-EL;->stream");
assertInvokeInterfaceMatching(invokes, 3, "Set;->iterator");
assertInvokeStaticMatching(invokes, 4, "Collection$-EL;->stream");
- assertInvokeStaticMatching(invokes, 5, "DesugarLinkedHashSet;->spliterator");
+ assertInvokeStaticMatching(invokes, 5, "Set$-EL;->spliterator");
assertInvokeInterfaceMatching(invokes, 9, "Iterator;->remove");
assertInvokeStaticMatching(invokes, 10, "DesugarArrays;->spliterator");
assertInvokeStaticMatching(invokes, 11, "DesugarArrays;->spliterator");
@@ -213,9 +213,6 @@
" j$.util.Spliterator spliterator(java.lang.Object[], int, int);",
"}",
"-keep class j$.util.stream.IntStream",
- "-keep class j$.util.DesugarLinkedHashSet {",
- " j$.util.Spliterator spliterator(java.util.LinkedHashSet);",
- "}",
"-keep class j$.util.stream.Stream",
"-keep class j$.util.Spliterator",
"-keep class j$.util.function.ToIntFunction { *; }");
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 bc6054a..b20a716 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
@@ -6,9 +6,12 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import java.time.Instant;
+import java.time.ZonedDateTime;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.IntUnaryOperator;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -65,6 +68,7 @@
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");
@@ -82,8 +86,12 @@
MyCalendarNoOverride myCalN = new MyCalendarNoOverride(1990, 2, 22);
System.out.println(myCalN.toZonedDateTime());
System.out.println("1990-03-22T00:00Z[GMT]");
+ System.out.println(myCalN.superToZonedDateTime());
+ System.out.println("1990-03-22T00:00Z[GMT]");
System.out.println(myCalN.toInstant());
System.out.println("1990-03-22T00:00:00Z");
+ 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);
@@ -93,16 +101,16 @@
MyDateNoOverride myDateN = new MyDateNoOverride(123456789);
System.out.println(myDateN.toInstant());
System.out.println("1970-01-02T10:17:36.789Z");
+ System.out.println(myDateN.superToInstant());
+ System.out.println("1970-01-02T10:17:36.789Z");
MyAtomicInteger myAtomicInteger = new MyAtomicInteger(42);
System.out.println(myAtomicInteger.getAndUpdate(x -> x + 1));
System.out.println("42");
- System.out.println(myAtomicInteger.getAndUpdate(x -> x + 5));
+ System.out.println(myAtomicInteger.superGetAndUpdate(x -> x + 2));
System.out.println("43");
System.out.println(myAtomicInteger.updateAndGet(x -> x + 100));
- System.out.println("148");
- System.out.println(myAtomicInteger.updateAndGet(x -> x + 500));
- System.out.println("648");
+ System.out.println("145");
}
}
@@ -124,6 +132,14 @@
public MyCalendarNoOverride(int year, int month, int dayOfMonth) {
super(year, month, dayOfMonth);
}
+
+ public Instant superToInstant() {
+ return super.toInstant();
+ }
+
+ public ZonedDateTime superToZonedDateTime() {
+ return super.toZonedDateTime();
+ }
}
// static class MyDateOverride extends Date {
@@ -143,6 +159,10 @@
public MyDateNoOverride(long date) {
super(date);
}
+
+ public Instant superToInstant() {
+ return super.toInstant();
+ }
}
static class MyAtomicInteger extends AtomicInteger {
@@ -150,5 +170,9 @@
public MyAtomicInteger(int initialValue) {
super(initialValue);
}
+
+ public int superGetAndUpdate(IntUnaryOperator op) {
+ return super.getAndUpdate(op);
+ }
}
}