Combine Long.compare/Objects.requireNonNull with Java 8 rewriter
This also adds support for Byte/Boolean/Character/Short/Integer.compare.
Test: tools/test.py --dex_vm all --no-internal *BackportedMethodRewriterTest*
Change-Id: Ieef5383bdf87e51c16481c57b13be76dd7d6188a
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 801d6f8..be60042 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -10,7 +10,7 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.ir.desugar.Java8MethodRewriter;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring;
import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
import com.android.tools.r8.naming.NamingLens;
@@ -250,7 +250,7 @@
|| name.contains(OutlineOptions.CLASS_NAME)
|| name.contains(TwrCloseResourceRewriter.UTILITY_CLASS_NAME)
|| name.contains(NestBasedAccessDesugaring.NEST_CONSTRUCTOR_NAME)
- || name.contains(Java8MethodRewriter.UTILITY_CLASS_NAME_PREFIX);
+ || name.contains(BackportedMethodRewriter.UTILITY_CLASS_NAME_PREFIX);
}
public boolean isProgramType(DexDefinitionSupplier definitions) {
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 c6c1ba8..f0f1613 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
@@ -42,7 +42,7 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.desugar.CovariantReturnTypeAnnotationTransformer;
import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
-import com.android.tools.r8.ir.desugar.Java8MethodRewriter;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
import com.android.tools.r8.ir.desugar.LambdaRewriter;
import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaringRewriter;
import com.android.tools.r8.ir.desugar.StringConcatRewriter;
@@ -123,7 +123,7 @@
private final NestBasedAccessDesugaringRewriter nestBasedAccessDesugaringRewriter;
private final InterfaceMethodRewriter interfaceMethodRewriter;
private final TwrCloseResourceRewriter twrCloseResourceRewriter;
- private final Java8MethodRewriter java8MethodRewriter;
+ private final BackportedMethodRewriter backportedMethodRewriter;
private final LambdaMerger lambdaMerger;
private final ClassInliner classInliner;
private final ClassStaticizer classStaticizer;
@@ -180,9 +180,9 @@
(options.enableDesugaring && enableTwrCloseResourceDesugaring())
? new TwrCloseResourceRewriter(appView, this)
: null;
- this.java8MethodRewriter =
+ this.backportedMethodRewriter =
options.enableDesugaring
- ? new Java8MethodRewriter(appView, this)
+ ? new BackportedMethodRewriter(appView, this)
: null;
this.lambdaMerger = options.enableLambdaMerging ? new LambdaMerger(appView) : null;
this.covariantReturnTypeAnnotationTransformer =
@@ -344,8 +344,8 @@
private void synthesizeJava8UtilityClass(
Builder<?> builder, ExecutorService executorService) throws ExecutionException {
- if (java8MethodRewriter != null) {
- java8MethodRewriter.synthesizeUtilityClass(builder, executorService, options);
+ if (backportedMethodRewriter != null) {
+ backportedMethodRewriter.synthesizeUtilityClass(builder, executorService, options);
}
}
@@ -983,7 +983,6 @@
assert code.verifyTypes(appView);
codeRewriter.removeTrivialCheckCastAndInstanceOfInstructions(code);
- codeRewriter.rewriteLongCompareAndRequireNonNull(code, options);
codeRewriter.commonSubexpressionElimination(code);
codeRewriter.simplifyArrayConstruction(code);
codeRewriter.rewriteMoveResult(code);
@@ -1025,8 +1024,8 @@
if (options.enableDesugaring && enableTryWithResourcesDesugaring()) {
codeRewriter.rewriteThrowableAddAndGetSuppressed(code);
}
- if (java8MethodRewriter != null) {
- java8MethodRewriter.desugar(code);
+ if (backportedMethodRewriter != null) {
+ backportedMethodRewriter.desugar(code);
}
stringConcatRewriter.desugarStringConcats(method.method, code);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/Java8MethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
similarity index 85%
rename from src/main/java/com/android/tools/r8/ir/desugar/Java8MethodRewriter.java
rename to src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 69d0b48..ac911f2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/Java8MethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -27,7 +27,7 @@
import com.android.tools.r8.ir.code.InstructionIterator;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.ir.desugar.Java8MethodRewriter.RewritableMethods.MethodGenerator;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.RewritableMethods.MethodGenerator;
import com.android.tools.r8.ir.synthetic.TemplateMethodCode;
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -43,9 +43,9 @@
import java.util.concurrent.ExecutorService;
import java.util.function.BiFunction;
-public final class Java8MethodRewriter {
- public static final String UTILITY_CLASS_NAME_PREFIX = "$r8$java8methods$utility";
- private static final String UTILITY_CLASS_DESCRIPTOR_PREFIX = "L$r8$java8methods$utility";
+public final class BackportedMethodRewriter {
+ public static final String UTILITY_CLASS_NAME_PREFIX = "$r8$backportedMethods$utility";
+ private static final String UTILITY_CLASS_DESCRIPTOR_PREFIX = "L" + UTILITY_CLASS_NAME_PREFIX;
private final Set<DexType> holders = Sets.newConcurrentHashSet();
private final AppView<?> appView;
@@ -55,7 +55,7 @@
private Map<DexMethod, MethodGenerator> methodGenerators = new ConcurrentHashMap<>();
- public Java8MethodRewriter(AppView<?> appView, IRConverter converter) {
+ public BackportedMethodRewriter(AppView<?> appView, IRConverter converter) {
this.appView = appView;
this.converter = converter;
this.factory = appView.dexItemFactory();
@@ -96,7 +96,7 @@
return null;
}
- public static boolean hasJava8MethodRewritePrefix(DexType clazz) {
+ public static boolean hasRewrittenMethodPrefix(DexType clazz) {
return clazz.descriptor.toString().startsWith(UTILITY_CLASS_DESCRIPTOR_PREFIX);
}
@@ -173,9 +173,17 @@
return new ByteMethods(options, method, "hashCodeImpl");
}
+ public static ByteMethods compareCode(InternalOptions options, DexMethod method) {
+ return new ByteMethods(options, method, "compareImpl");
+ }
+
public static int hashCodeImpl(byte i) {
return Byte.valueOf(i).hashCode();
}
+
+ public static int compareImpl(byte a, byte b) {
+ return Byte.valueOf(a).compareTo(Byte.valueOf(b));
+ }
}
@@ -188,9 +196,17 @@
return new ShortMethods(options, method, "hashCodeImpl");
}
+ public static ShortMethods compareCode(InternalOptions options, DexMethod method) {
+ return new ShortMethods(options, method, "compareImpl");
+ }
+
public static int hashCodeImpl(short i) {
return Short.valueOf(i).hashCode();
}
+
+ public static int compareImpl(short a, short b) {
+ return Short.valueOf(a).compareTo(Short.valueOf(b));
+ }
}
private static final class IntegerMethods extends TemplateMethodCode {
@@ -202,6 +218,10 @@
return new IntegerMethods(options, method, "hashCodeImpl");
}
+ public static IntegerMethods compareCode(InternalOptions options, DexMethod method) {
+ return new IntegerMethods(options, method, "compareImpl");
+ }
+
public static IntegerMethods maxCode(InternalOptions options, DexMethod method) {
return new IntegerMethods(options, method, "maxImpl");
}
@@ -230,6 +250,10 @@
return Integer.valueOf(i).hashCode();
}
+ public static int compareImpl(int a, int b) {
+ return Integer.valueOf(a).compareTo(Integer.valueOf(b));
+ }
+
public static int maxImpl(int a, int b) {
return java.lang.Math.max(a, b);
}
@@ -364,6 +388,10 @@
return new BooleanMethods(options, method, "hashCodeImpl");
}
+ public static BooleanMethods compareCode(InternalOptions options, DexMethod method) {
+ return new BooleanMethods(options, method, "compareImpl");
+ }
+
public static BooleanMethods logicalAndCode(InternalOptions options, DexMethod method) {
return new BooleanMethods(options, method, "logicalAndImpl");
}
@@ -380,6 +408,10 @@
return Boolean.valueOf(b).hashCode();
}
+ public static int compareImpl(boolean a, boolean b) {
+ return Boolean.valueOf(a).compareTo(Boolean.valueOf(b));
+ }
+
public static boolean logicalAndImpl(boolean a, boolean b) {
return a && b;
}
@@ -402,6 +434,10 @@
return new LongMethods(options, method, "hashCodeImpl");
}
+ public static LongMethods compareCode(InternalOptions options, DexMethod method) {
+ return new LongMethods(options, method, "compareImpl");
+ }
+
public static LongMethods maxCode(InternalOptions options, DexMethod method) {
return new LongMethods(options, method, "maxImpl");
}
@@ -430,6 +466,10 @@
return Long.valueOf(i).hashCode();
}
+ public static int compareImpl(long a, long b) {
+ return Long.valueOf(a).compareTo(Long.valueOf(b));
+ }
+
public static long maxImpl(long a, long b) {
return java.lang.Math.max(a, b);
}
@@ -530,9 +570,34 @@
return new CharacterMethods(options, method, "hashCodeImpl");
}
+ public static CharacterMethods compareCode(InternalOptions options, DexMethod method) {
+ return new CharacterMethods(options, method, "compareImpl");
+ }
+
public static int hashCodeImpl(char i) {
return Character.valueOf(i).hashCode();
}
+
+ public static int compareImpl(char a, char b) {
+ return Character.valueOf(a).compareTo(Character.valueOf(b));
+ }
+ }
+
+ private static final class ObjectMethods extends TemplateMethodCode {
+ ObjectMethods(InternalOptions options, DexMethod method, String methodName) {
+ super(options, method, methodName, method.proto.toDescriptorString());
+ }
+
+ public static ObjectMethods requireNonNullCode(InternalOptions options, DexMethod method) {
+ // Rewrite calls to Objects.requireNonNull(Object) because Javac 9 started to use it for
+ // synthesized null checks.
+ return new ObjectMethods(options, method, "requireNonNullImpl");
+ }
+
+ public static Object requireNonNullImpl(Object a) {
+ a.getClass();
+ return a;
+ }
}
public static final class RewritableMethods {
@@ -541,6 +606,9 @@
new HashMap<>();
public RewritableMethods(DexItemFactory factory, InternalOptions options) {
+ if (!options.canUseJava7CompareAndObjectsOperations()) {
+ initializeJava7CompareOperations(factory);
+ }
if (!options.canUseJava8SignedOperations()) {
initializeJava8SignedOperations(factory);
}
@@ -553,6 +621,64 @@
return rewritable.isEmpty();
}
+ private void initializeJava7CompareOperations(DexItemFactory factory) {
+ // Byte
+ DexString clazz = factory.boxedByteDescriptor;
+ // int Byte.compare(byte a, byte b)
+ DexString method = factory.createString("compare");
+ DexProto proto = factory.createProto(factory.intType, factory.byteType, factory.byteType);
+ addOrGetMethod(clazz, method)
+ .put(proto, new MethodGenerator(ByteMethods::compareCode, clazz, method, proto));
+
+ // Short
+ clazz = factory.boxedShortDescriptor;
+ // int Short.compare(short a, short b)
+ method = factory.createString("compare");
+ proto = factory.createProto(factory.intType, factory.shortType, factory.shortType);
+ addOrGetMethod(clazz, method)
+ .put(proto, new MethodGenerator(ShortMethods::compareCode, clazz, method, proto));
+
+ // Integer
+ clazz = factory.boxedIntDescriptor;
+ // int Integer.compare(int a, int b)
+ method = factory.createString("compare");
+ proto = factory.createProto(factory.intType, factory.intType, factory.intType);
+ addOrGetMethod(clazz, method)
+ .put(proto, new MethodGenerator(IntegerMethods::compareCode, clazz, method, proto));
+
+ // Boolean
+ clazz = factory.boxedBooleanDescriptor;
+ // int Boolean.compare(boolean a, boolean b)
+ method = factory.createString("compare");
+ proto = factory.createProto(factory.intType, factory.booleanType, factory.booleanType);
+ addOrGetMethod(clazz, method)
+ .put(proto, new MethodGenerator(BooleanMethods::compareCode, clazz, method, proto));
+
+ // Long
+ clazz = factory.boxedLongDescriptor;
+ // int Long.compare(long a, long b)
+ method = factory.createString("compare");
+ proto = factory.createProto(factory.intType, factory.longType, factory.longType);
+ addOrGetMethod(clazz, method)
+ .put(proto, new MethodGenerator(LongMethods::compareCode, clazz, method, proto));
+
+ // Character
+ clazz = factory.boxedCharDescriptor;
+ // int Character.compare(char a, char b)
+ method = factory.createString("compare");
+ proto = factory.createProto(factory.intType, factory.charType, factory.charType);
+ addOrGetMethod(clazz, method)
+ .put(proto, new MethodGenerator(CharacterMethods::compareCode, clazz, method, proto));
+
+ // Object
+ clazz = factory.objectsDescriptor;
+ // Object Objects.requireNonNull(Object a)
+ method = factory.createString("requireNonNull");
+ proto = factory.createProto(factory.objectType, factory.objectType);
+ addOrGetMethod(clazz, method)
+ .put(proto, new MethodGenerator(ObjectMethods::requireNonNullCode, clazz, method, proto));
+ }
+
private void initializeJava8SignedOperations(DexItemFactory factory) {
// Byte
DexString clazz = factory.boxedByteDescriptor;
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 b949b72..ff03181 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
@@ -154,7 +154,7 @@
InvokeStatic invokeStatic = instruction.asInvokeStatic();
DexMethod method = invokeStatic.getInvokedMethod();
DexClass clazz = appInfo.definitionFor(method.holder);
- if (Java8MethodRewriter.hasJava8MethodRewritePrefix(method.holder)) {
+ if (BackportedMethodRewriter.hasRewrittenMethodPrefix(method.holder)) {
// We did not create this code yet, but it will not require rewriting.
continue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 87c28bf..48b81ae 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -43,8 +43,6 @@
import com.android.tools.r8.ir.code.Binop;
import com.android.tools.r8.ir.code.CatchHandlers;
import com.android.tools.r8.ir.code.CheckCast;
-import com.android.tools.r8.ir.code.Cmp;
-import com.android.tools.r8.ir.code.Cmp.Bias;
import com.android.tools.r8.ir.code.ConstInstruction;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.ConstString;
@@ -3509,39 +3507,6 @@
return true;
}
- public void rewriteLongCompareAndRequireNonNull(IRCode code, InternalOptions options) {
- if (options.canUseLongCompareAndObjectsNonNull()) {
- return;
- }
-
- InstructionIterator iterator = code.instructionIterator();
- while (iterator.hasNext()) {
- Instruction current = iterator.next();
- if (current.isInvokeMethod()) {
- DexMethod invokedMethod = current.asInvokeMethod().getInvokedMethod();
- if (invokedMethod == dexItemFactory.longMethods.compare) {
- // Rewrite calls to Long.compare for sdk versions that do not have that method.
- List<Value> inValues = current.inValues();
- assert inValues.size() == 2;
- iterator.replaceCurrentInstruction(
- new Cmp(NumericType.LONG, Bias.NONE, current.outValue(), inValues.get(0),
- inValues.get(1)));
- } else if (invokedMethod == dexItemFactory.objectsMethods.requireNonNull) {
- // Rewrite calls to Objects.requireNonNull(Object) because Javac 9 start to use it for
- // synthesized null checks.
- InvokeVirtual callToGetClass = new InvokeVirtual(dexItemFactory.objectMethods.getClass,
- null, current.inValues());
- if (current.outValue() != null) {
- current.outValue().replaceUsers(current.inValues().get(0));
- current.setOutValue(null);
- }
- iterator.replaceCurrentInstruction(callToGetClass);
- }
- }
- }
- assert code.isConsistentSSA();
- }
-
/**
* Remove moves that are not actually used by instructions in exiting paths. These moves can arise
* due to debug local info needing a particular value and the live-interval for it then moves it
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 7fafc4b..99eb7ea 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -719,7 +719,7 @@
return intermediate || hasMinApi(AndroidApiLevel.L);
}
- public boolean canUseLongCompareAndObjectsNonNull() {
+ public boolean canUseJava7CompareAndObjectsOperations() {
return isGeneratingClassFiles() || hasMinApi(AndroidApiLevel.K);
}
diff --git a/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java b/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
index 1cf61c0..edc1873 100644
--- a/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
+++ b/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
@@ -9,7 +9,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
-import com.android.tools.r8.ir.desugar.Java8MethodRewriter;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
import com.android.tools.r8.ir.desugar.LambdaRewriter;
import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
import java.util.List;
@@ -69,7 +69,7 @@
private static boolean assumeClassesAreEqual(DexProgramClass a) {
return LambdaRewriter.hasLambdaClassPrefix(a.type)
- || Java8MethodRewriter.hasJava8MethodRewritePrefix(a.type)
+ || BackportedMethodRewriter.hasRewrittenMethodPrefix(a.type)
|| InterfaceMethodRewriter.hasDispatchClassSuffix(a.type)
|| a.type.descriptor.toString().equals(TwrCloseResourceRewriter.UTILITY_CLASS_DESCRIPTOR);
}
diff --git a/src/test/examples/rewrite/LongCompare.java b/src/test/examples/rewrite/LongCompare.java
deleted file mode 100644
index 7c96c10..0000000
--- a/src/test/examples/rewrite/LongCompare.java
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (c) 2017, 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 rewrite;
-
-public class LongCompare {
-
- public static int simpleCompare(long l1, long l2) {
- try {
- return Long.compare(l1, l2);
- } catch (Throwable t) {
- System.out.println(t);
- }
- return 2;
- }
-
- public static long getValue1() {
- return 123456789L;
- }
-
- public static long getValue2() {
- return 0;
- }
-
- public static boolean complexCompare(long l1, long l2) {
- return Long.compare(getValue1(), l1) == 0 && Long.compare(l2, getValue2()) > 0;
- }
-
- public static void main(String[] args) {
- System.out.println(simpleCompare(123456789L, 987654321L));
- System.out.println(simpleCompare(Long.MAX_VALUE, 0L));
- System.out.println(simpleCompare(Long.MIN_VALUE, 0L));
- System.out.println(simpleCompare(Long.MAX_VALUE, Long.MAX_VALUE));
-
- System.out.println(complexCompare(123456789L, 1));
- System.out.println(complexCompare(123456789L, -1));
- System.out.println(complexCompare(1234567890L, 1));
- System.out.println(complexCompare(1234567890L, -1));
- }
-}
diff --git a/src/test/examples/rewrite/RequireNonNull.java b/src/test/examples/rewrite/RequireNonNull.java
deleted file mode 100644
index d8868bf..0000000
--- a/src/test/examples/rewrite/RequireNonNull.java
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (c) 2017, 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 rewrite;
-
-import java.util.Objects;
-
-public class RequireNonNull {
-
- public static void main(String[] args) {
- RequireNonNull o = new RequireNonNull();
- System.out.println(o.nonnullRemove().toString());
- System.out.println(o.nonnullRemove(o).toString());
- o.nonnullWithPhiInstruction(true, o);
- o.nonnullWithPhiInstruction(false, o);
- }
-
- private Object nonnullRemove() {
- return Objects.requireNonNull(this);
- }
-
- private Object nonnullRemove(Object o) {
- Objects.requireNonNull(o);
- return o;
- }
-
- private void nonnullWithPhiInstruction(boolean b, Object input) {
- Object o = null;
- if (b) {
- o = Objects.requireNonNull(input);
- }
- System.out.println(o);
- }
-
- @Override
- public String toString() {
- return "toString";
- }
-}
diff --git a/src/test/java/com/android/tools/r8/desugar/Java8MethodsTest.java b/src/test/java/com/android/tools/r8/desugar/BackportedMethodRewriterTest.java
similarity index 74%
rename from src/test/java/com/android/tools/r8/desugar/Java8MethodsTest.java
rename to src/test/java/com/android/tools/r8/desugar/BackportedMethodRewriterTest.java
index cb4c9b7..a130c0f 100644
--- a/src/test/java/com/android/tools/r8/desugar/Java8MethodsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/BackportedMethodRewriterTest.java
@@ -16,56 +16,58 @@
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.nio.file.Path;
import java.util.List;
+import java.util.Objects;
import org.junit.Before;
import org.junit.Test;
-public class Java8MethodsTest extends TestBase {
+public class BackportedMethodRewriterTest extends TestBase {
static String expectedOutput = "";
@Before
public void testJvm() throws Exception {
expectedOutput = testForJvm()
.addTestClasspath()
- .run(Java8Methods.class).getStdOut();
+ .run(TestMethods.class).getStdOut();
}
@Test
public void testD8() throws Exception {
testForD8()
- .addProgramClasses(Java8Methods.class)
- .run(Java8Methods.class)
+ .addProgramClasses(TestMethods.class)
+ .run(TestMethods.class)
.assertSuccessWithOutput(expectedOutput);
- assertDesugaring(AndroidApiLevel.O, 31);
- assertDesugaring(AndroidApiLevel.N, 25);
- assertDesugaring(AndroidApiLevel.M, 0);
+ assertDesugaring(AndroidApiLevel.O, 39);
+ assertDesugaring(AndroidApiLevel.N, 33);
+ assertDesugaring(AndroidApiLevel.K, 8);
+ assertDesugaring(AndroidApiLevel.J_MR2, 0);
}
- private void assertDesugaring(AndroidApiLevel apilevel, int expectedJavaLangInvokeStatics)
+ private void assertDesugaring(AndroidApiLevel apiLevel, int expectedJavaInvokeStatics)
throws Exception {
D8TestCompileResult runResult = testForD8()
- .addProgramClasses(Java8Methods.class)
- .setMinApi(apilevel)
+ .addProgramClasses(TestMethods.class)
+ .setMinApi(apiLevel)
.compile();
MethodSubject mainMethod = runResult.inspector()
- .clazz(Java8Methods.class)
+ .clazz(TestMethods.class)
.mainMethod();
assertThat(mainMethod, isPresent());
- List<InstructionSubject> javaLangInvokeStatics = mainMethod
+ List<InstructionSubject> javaInvokeStatics = mainMethod
.streamInstructions()
.filter(InstructionSubject::isInvokeStatic)
- .filter(is -> is.getMethod().holder.toDescriptorString().startsWith("Ljava/lang/"))
+ .filter(is -> is.getMethod().holder.toDescriptorString().startsWith("Ljava/"))
.collect(toList());
- int actualJavaLangInvokeStatics = javaLangInvokeStatics.size();
+ int actualJavaInvokeStatics = javaInvokeStatics.size();
assertEquals("Expected "
- + expectedJavaLangInvokeStatics
- + " invoke-static on java/lang/<Type> but found "
- + actualJavaLangInvokeStatics
+ + expectedJavaInvokeStatics
+ + " invoke-static on java/*/<Type> but found "
+ + actualJavaInvokeStatics
+ ": "
- + javaLangInvokeStatics, expectedJavaLangInvokeStatics, actualJavaLangInvokeStatics);
+ + javaInvokeStatics, expectedJavaInvokeStatics, actualJavaInvokeStatics);
}
@Test
@@ -122,16 +124,29 @@
}
}
- static class Java8Methods {
+ static class TestMethods {
+ // Defined as a static method on this class to avoid affecting invoke-static counts in main().
+ private static int signum(int value) {
+ return (int) Math.signum(value);
+ }
+
public static void main(String[] args) {
byte[] aBytes = new byte[]{42, 1, -1, Byte.MAX_VALUE, Byte.MIN_VALUE};
for (byte aByte : aBytes) {
System.out.println(Byte.hashCode(aByte));
+ for (byte bByte : aBytes) {
+ // Normalize comparison to [-1, 1] since the values differ across versions but signs match
+ System.out.println(signum(Byte.compare(aByte, bByte)));
+ }
}
short[] aShorts = new short[]{42, 1, -1, Short.MAX_VALUE, Short.MIN_VALUE};
for (short aShort : aShorts) {
System.out.println(Short.hashCode(aShort));
+ for (short bShort : aShorts) {
+ // Normalize comparison to [-1, 1] since the values differ across versions but signs match
+ System.out.println(signum(Short.compare(aShort, bShort)));
+ }
}
int[] aInts = new int[]{42, 1, -1, Integer.MAX_VALUE, Integer.MIN_VALUE};
@@ -139,6 +154,7 @@
for (int aInt : aInts) {
System.out.println(Integer.hashCode(aInt));
for (int bInt : bInts) {
+ System.out.println(Integer.compare(aInt, bInt));
System.out.println(Integer.max(aInt, bInt));
System.out.println(Integer.min(aInt, bInt));
System.out.println(Integer.sum(aInt, bInt));
@@ -183,6 +199,7 @@
for (boolean aBoolean : new boolean[]{true, false}) {
System.out.println(Boolean.hashCode(aBoolean));
for (boolean bBoolean : new boolean[]{true, false}) {
+ System.out.println(Boolean.compare(aBoolean, bBoolean));
System.out.println(Boolean.logicalAnd(aBoolean, bBoolean));
System.out.println(Boolean.logicalOr(aBoolean, bBoolean));
System.out.println(Boolean.logicalXor(aBoolean, bBoolean));
@@ -196,6 +213,7 @@
for (long aLong : aLongs) {
System.out.println(Long.hashCode(aLong));
for (long bLong : bLongs) {
+ System.out.println(Long.compare(aLong, bLong));
System.out.println(Long.max(aLong, bLong));
System.out.println(Long.min(aLong, bLong));
System.out.println(Long.sum(aLong, bLong));
@@ -208,6 +226,21 @@
char[] aChars = new char[]{'s', 'u', 'p', Character.MAX_VALUE, Character.MIN_VALUE};
for (char aChar : aChars) {
System.out.println(Character.hashCode(aChar));
+ for (char bChar : aChars) {
+ System.out.println(Character.compare(aChar, bChar));
+ }
+ }
+
+ // Use a runtime conditional so nullability analysis doesn't remove the requireNonNull call.
+ String nonNullString = args.length < Integer.MAX_VALUE ? "non-null string" : null;
+ System.out.println(Objects.requireNonNull(nonNullString));
+
+ try {
+ // Use a runtime conditional so nullability analysis doesn't remove the requireNonNull call.
+ String nullString = args.length == 0 ? null : "non-null string";
+ throw new AssertionError(Objects.requireNonNull(nullString));
+ } catch (NullPointerException expected) {
+ System.out.println("null");
}
}
}
diff --git a/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java b/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java
deleted file mode 100644
index 16b7922..0000000
--- a/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright (c) 2017, 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.rewrite.longcompare;
-
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.D8;
-import com.android.tools.r8.D8Command;
-import com.android.tools.r8.OutputMode;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
-import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.InstructionSubject;
-import com.android.tools.r8.utils.codeinspector.InvokeInstructionSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Arrays;
-import java.util.Iterator;
-import org.junit.Assert;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-
-/**
- * Tests checking that Long.compare() is always rewritten into long compare instruction.
- */
-public class LongCompare {
-
- @Rule
- public TemporaryFolder tmpOutputDir = ToolHelper.getTemporaryFolderForTest();
-
- void compileWithD8(Path intputPath, Path outputPath)
- throws IOException, CompilationFailedException {
- D8.run(
- D8Command.builder()
- .addProgramFiles(intputPath)
- .setOutput(outputPath, OutputMode.DexIndexed)
- .build());
- }
-
- void runTest(Path dexFile) {
- ArtCommandBuilder builder = new ArtCommandBuilder(ToolHelper.getDexVm());
- builder.appendClasspath(dexFile.toString());
- builder.setMainClass("rewrite.LongCompare");
- try {
- String output = ToolHelper.runArt(builder);
- Assert
- .assertEquals(StringUtils.lines("-1", "1", "-1", "0", "true", "false", "false", "false"),
- output);
- } catch (IOException e) {
- Assert.fail();
- }
- }
-
- @Test
- public void testLongCompareIsRewritten() throws Exception {
- final Path inputPath = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + "/rewrite.jar");
- Path outputPath = tmpOutputDir.newFolder().toPath();
-
- compileWithD8(inputPath, outputPath);
-
- Path dexPath = outputPath.resolve("classes.dex");
-
- CodeInspector codeInspector = new CodeInspector(dexPath);
- ClassSubject classSubject = codeInspector.clazz("rewrite.LongCompare");
- MethodSubject methodSubject = classSubject
- .method("int", "simpleCompare", Arrays.asList("long", "long"));
- // Check that exception handler is removed since it is no longer needed.
- Assert.assertFalse(methodSubject.getMethod().getCode().asDexCode().usesExceptionHandling());
- Iterator<InvokeInstructionSubject> iterator =
- methodSubject.iterateInstructions(InstructionSubject::isInvoke);
- Assert.assertFalse(iterator.hasNext());
-
- runTest(dexPath);
- }
-}
diff --git a/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java b/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java
deleted file mode 100644
index 278bc8b..0000000
--- a/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright (c) 2017, 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.rewrite.longcompare;
-
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.D8;
-import com.android.tools.r8.D8Command;
-import com.android.tools.r8.OutputMode;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.InstructionSubject;
-import com.android.tools.r8.utils.codeinspector.InvokeInstructionSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.concurrent.ExecutionException;
-import org.junit.Assert;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-
-public class RequireNonNullRewriteTest {
-
- @Rule
- public TemporaryFolder tmpOutputDir = ToolHelper.getTemporaryFolderForTest();
-
- void compileWithD8(Path intputPath, Path outputPath, CompilationMode mode)
- throws IOException, CompilationFailedException {
- D8.run(
- D8Command.builder()
- .setMode(mode)
- .addProgramFiles(intputPath)
- .setOutput(outputPath, OutputMode.DexIndexed)
- .build());
- }
-
- void runTest(Path jarFile, Path dexFile) {
- String mainClass = "rewrite.RequireNonNull";
- ArtCommandBuilder builder = new ArtCommandBuilder(ToolHelper.getDexVm());
- builder.appendClasspath(dexFile.toString());
- builder.setMainClass(mainClass);
- try {
- String output = ToolHelper.runArt(builder);
- ProcessResult javaResult = ToolHelper.runJava(jarFile, mainClass);
- Assert.assertEquals(javaResult.stdout, output);
- } catch (IOException e) {
- Assert.fail();
- }
- }
-
- @Test
- public void testDebugRequireNonNullIsRewritten() throws Exception {
- runTest(CompilationMode.DEBUG);
- }
-
- @Test
- public void testReleaseRequireNonNullIsRewritten() throws Exception {
- runTest(CompilationMode.RELEASE);
- }
-
- private void runTest(CompilationMode mode)
- throws IOException, ExecutionException, CompilationFailedException {
- final Path inputPath = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + "/rewrite.jar");
- Path outputPath = tmpOutputDir.newFolder().toPath();
-
- compileWithD8(inputPath, outputPath, mode);
-
- Path dexPath = outputPath.resolve("classes.dex");
-
- CodeInspector codeInspector = new CodeInspector(dexPath);
- ClassSubject classSubject = codeInspector.clazz("rewrite.RequireNonNull");
- MethodSubject methodSubject = classSubject
- .method("java.lang.Object", "nonnullRemove", Collections.emptyList());
-
- Iterator<InvokeInstructionSubject> iterator =
- methodSubject.iterateInstructions(InstructionSubject::isInvoke);
- Assert.assertTrue(iterator.hasNext());
- Assert.assertEquals("getClass", iterator.next().invokedMethod().name.toString());
- Assert.assertFalse(iterator.hasNext());
-
- runTest(inputPath, dexPath);
- }
-}