Backport android.util.SparseArray#set
For Android S android.util.SparseArray#set was added as an alias for
android.util.SparseArray#put.
Bug: 185547135
Change-Id: I6b3558921a14fc3e0ac7766db8c6b6e614d0c236
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index ceb543f..646fca7 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -536,6 +536,8 @@
public final DexType androidUtilPropertyType =
createStaticallyKnownType("Landroid/util/Property;");
public final DexType androidViewViewType = createStaticallyKnownType("Landroid/view/View;");
+ public final DexType androidUtilSparseArrayType =
+ createStaticallyKnownType("Landroid/util/SparseArray;");
public final StringBuildingMethods stringBuilderMethods =
new StringBuildingMethods(stringBuilderType);
@@ -581,6 +583,8 @@
public final AndroidSystemOsConstantsMembers androidSystemOsConstantsMembers =
new AndroidSystemOsConstantsMembers();
public final AndroidViewViewMembers androidViewViewMembers = new AndroidViewViewMembers();
+ public final AndroidUtilSparseArrayMembers androidUtilSparseArrayMembers =
+ new AndroidUtilSparseArrayMembers();
// java.**
public final JavaIoFileMembers javaIoFileMembers = new JavaIoFileMembers();
@@ -959,6 +963,13 @@
}
}
+ public class AndroidUtilSparseArrayMembers extends LibraryMembers {
+ public final DexMethod put =
+ createMethod(androidUtilSparseArrayType, createProto(voidType, intType, objectType), "put");
+ public final DexMethod set =
+ createMethod(androidUtilSparseArrayType, createProto(voidType, intType, objectType), "set");
+ }
+
public class BooleanMembers extends LibraryMembers {
public final DexField FALSE = createField(boxedBooleanType, boxedBooleanType, "FALSE");
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 7bb5741..832fd8c 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
@@ -31,6 +31,7 @@
import com.android.tools.r8.ir.desugar.backports.NumericMethodRewrites;
import com.android.tools.r8.ir.desugar.backports.ObjectsMethodRewrites;
import com.android.tools.r8.ir.desugar.backports.OptionalMethodRewrites;
+import com.android.tools.r8.ir.desugar.backports.SparseArrayMethodRewrites;
import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
@@ -1055,6 +1056,13 @@
new MethodGenerator(
method, BackportedMethods::MathMethods_floorModLongInt, "floorModLongInt"));
}
+
+ // android.util.SparseArray
+
+ // void android.util.SparseArray.set(int, Object))
+ addProvider(
+ new InvokeRewriter(
+ factory.androidUtilSparseArrayMembers.set, SparseArrayMethodRewrites.rewriteSet()));
}
private void initializeJava9MethodProviders(DexItemFactory factory) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/SparseArrayMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/SparseArrayMethodRewrites.java
new file mode 100644
index 0000000..e1858e2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/SparseArrayMethodRewrites.java
@@ -0,0 +1,20 @@
+// 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.ir.desugar.backports;
+
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
+import org.objectweb.asm.Opcodes;
+
+public final class SparseArrayMethodRewrites {
+
+ private SparseArrayMethodRewrites() {}
+
+ public static MethodInvokeRewriter rewriteSet() {
+ // Rewrite android/util/SparseArray#set to android/util/SparseArray#put
+ return (invoke, factory) ->
+ new CfInvoke(Opcodes.INVOKEVIRTUAL, factory.androidUtilSparseArrayMembers.put, false);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
index 646dbf9..e4dd6d9 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
import it.unimi.dsi.fastutil.ints.Int2IntAVLTreeMap;
import it.unimi.dsi.fastutil.ints.Int2IntSortedMap;
import java.io.IOException;
@@ -29,25 +30,71 @@
abstract class AbstractBackportTest extends TestBase {
protected final TestParameters parameters;
- private final Class<?> targetClass;
- private final Class<?> testClass;
+ private final ClassInfo targetClass;
+ private final ClassInfo testClass;
private final Path testJar;
private final String testClassName;
private final Int2IntSortedMap invokeStaticCounts = new Int2IntAVLTreeMap();
private final Set<String> ignoredInvokes = new HashSet<>();
+ private static class ClassInfo {
+ private final Class<?> clazz;
+ private final List<byte[]> classFileData;
+
+ private ClassInfo(Class<?> clazz) {
+ this.clazz = clazz;
+ this.classFileData = null;
+ }
+
+ private ClassInfo(byte[] classFileData) {
+ this.clazz = null;
+ this.classFileData = ImmutableList.of(classFileData);
+ }
+
+ private ClassInfo(List<byte[]> classFileData) {
+ this.clazz = null;
+ this.classFileData = classFileData;
+ }
+
+ String getName() {
+ return clazz != null ? clazz.getName() : extractClassName(classFileData.get(0));
+ }
+
+ TestBuilder<?, ?> addAsProgramClass(TestBuilder<?, ?> builder) throws IOException {
+ if (clazz != null) {
+ return builder.addProgramClassesAndInnerClasses(clazz);
+ } else {
+ return builder.addProgramClassFileData(classFileData);
+ }
+ }
+ }
+
AbstractBackportTest(TestParameters parameters, Class<?> targetClass,
Class<?> testClass) {
- this(parameters, targetClass, testClass, null, null);
+ this(parameters, new ClassInfo(targetClass), new ClassInfo(testClass), null, null);
+ }
+
+ AbstractBackportTest(
+ TestParameters parameters, byte[] targetClassFileData, List<byte[]> testClassFileData) {
+ this(
+ parameters,
+ new ClassInfo(targetClassFileData),
+ new ClassInfo(testClassFileData),
+ null,
+ null);
}
AbstractBackportTest(TestParameters parameters, Class<?> targetClass,
Path testJar, String testClassName) {
- this(parameters, targetClass, null, testJar, testClassName);
+ this(parameters, new ClassInfo(targetClass), null, testJar, testClassName);
}
- private AbstractBackportTest(TestParameters parameters, Class<?> targetClass,
- Class<?> testClass, Path testJar, String testClassName) {
+ private AbstractBackportTest(
+ TestParameters parameters,
+ ClassInfo targetClass,
+ ClassInfo testClass,
+ Path testJar,
+ String testClassName) {
this.parameters = parameters;
this.targetClass = targetClass;
this.testClass = testClass;
@@ -83,7 +130,7 @@
private void configureProgram(TestBuilder<?, ?> builder) throws IOException {
builder.addProgramClasses(MiniAssert.class, IgnoreInvokes.class);
if (testClass != null) {
- builder.addProgramClassesAndInnerClasses(testClass);
+ testClass.addAsProgramClass(builder);
} else {
builder.addProgramFiles(testJar);
}
@@ -188,5 +235,9 @@
"Expected <" + expected + "> to be same instance as <" + actual + '>');
}
}
+
+ static void fail(String message) {
+ throw new AssertionError("Failed: " + message);
+ }
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/SparseArrayBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/SparseArrayBackportTest.java
new file mode 100644
index 0000000..61cf95d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/SparseArrayBackportTest.java
@@ -0,0 +1,78 @@
+// 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.backports;
+
+import com.android.tools.r8.TestParameters;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class SparseArrayBackportTest extends AbstractBackportTest {
+
+ private static final String SPARSE_ARRAY_DESCRIPTOR = "Landroid/util/SparseArray;";
+
+ @Parameters(name = "{0}")
+ public static Iterable<?> data() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public SparseArrayBackportTest(TestParameters parameters) throws IOException {
+ super(
+ parameters,
+ SparseArrayBackportTest.getSparseArray(),
+ ImmutableList.of(
+ SparseArrayBackportTest.getTestRunner(), SparseArrayBackportTest.getSparseArray()));
+
+ // The constructor is used by the test and put has been available since API 1 and is the
+ // method set is rewritten to.
+ ignoreInvokes("<init>");
+ ignoreInvokes("put");
+ }
+
+ private static byte[] getSparseArray() throws IOException {
+ return transformer(SparseArray.class).setClassDescriptor(SPARSE_ARRAY_DESCRIPTOR).transform();
+ }
+
+ private static byte[] getTestRunner() throws IOException {
+ return transformer(TestRunner.class)
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(SparseArray.class), SPARSE_ARRAY_DESCRIPTOR)
+ .transform();
+ }
+
+ public static class SparseArray {
+ public void set(int index, Object value) {
+ TestRunner.doFail("set should not be called");
+ }
+
+ public void put(int index, Object value) {
+ TestRunner.doAssertEquals(42, index);
+ TestRunner.doAssertEquals("Forty two", value);
+ }
+ }
+
+ public static class TestRunner extends MiniAssert {
+
+ public static void main(String[] args) {
+ new SparseArray().set(42, "Forty two");
+ }
+
+ // Forwards to MiniAssert to avoid having to make it public.
+ public static void doAssertEquals(int expected, int actual) {
+ MiniAssert.assertEquals(expected, actual);
+ }
+
+ public static void doAssertEquals(Object expected, Object actual) {
+ MiniAssert.assertEquals(expected, actual);
+ }
+
+ public static void doFail(String message) {
+ MiniAssert.fail(message);
+ }
+ }
+}