blob: ae3b95eef23926c42d453de4beb5514796e40c7b [file] [log] [blame]
// Copyright (c) 2018, 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.cf;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.ProgramConsumer;
import com.android.tools.r8.R8;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.R8Command.Builder;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.DescriptorUtils;
import java.nio.file.Path;
import java.util.Arrays;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
public class MethodHandleTestRunner {
static final Class<?> CLASS = MethodHandleTest.class;
private boolean ldc = false;
private boolean minify = false;
@Rule
public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
@Test
public void testMethodHandlesLookup() throws Exception {
// Run test with dynamic method lookups, i.e. using MethodHandles.lookup().find*()
ldc = false;
test();
}
@Test
public void testLdcMethodHandle() throws Exception {
// Run test with LDC methods, i.e. without java.lang.invoke.MethodHandles
ldc = true;
test();
}
@Test
public void testMinify() throws Exception {
// Run test with LDC methods, i.e. without java.lang.invoke.MethodHandles
ldc = true;
ProcessResult runInput = runInput();
assertEquals(0, runInput.exitCode);
Path outCf = temp.getRoot().toPath().resolve("cf.jar");
build(new ClassFileConsumer.ArchiveConsumer(outCf), true);
ProcessResult runCf =
ToolHelper.runJava(outCf, CLASS.getCanonicalName(), ldc ? "error" : "exception");
assertEquals(runInput.toString(), runCf.toString());
}
private final Class[] inputClasses = {
MethodHandleTest.class,
MethodHandleTest.C.class,
MethodHandleTest.I.class,
MethodHandleTest.Impl.class,
MethodHandleTest.D.class,
MethodHandleTest.E.class,
MethodHandleTest.F.class,
};
private void test() throws Exception {
ProcessResult runInput = runInput();
Path outCf = temp.getRoot().toPath().resolve("cf.jar");
build(new ClassFileConsumer.ArchiveConsumer(outCf), false);
Path outDex = temp.getRoot().toPath().resolve("dex.zip");
build(new DexIndexedConsumer.ArchiveConsumer(outDex), false);
ProcessResult runCf =
ToolHelper.runJava(outCf, CLASS.getCanonicalName(), ldc ? "error" : "exception");
assertEquals(runInput.toString(), runCf.toString());
// TODO(mathiasr): Once we include a P runtime, change this to "P and above".
if (ToolHelper.getDexVm() != DexVm.ART_DEFAULT) {
return;
}
ProcessResult runDex =
ToolHelper.runArtRaw(
outDex.toString(),
CLASS.getCanonicalName(),
cmd -> cmd.appendProgramArgument(ldc ? "pass" : "exception"));
// Only compare stdout and exitCode since dex2oat prints to stderr.
if (runInput.exitCode != runDex.exitCode) {
System.out.println(runDex.stderr);
}
assertEquals(runInput.stdout, runDex.stdout);
assertEquals(runInput.exitCode, runDex.exitCode);
}
private void build(ProgramConsumer programConsumer, boolean minify) throws Exception {
// MethodHandle.invoke() only supported from Android O
// ConstMethodHandle only supported from Android P
AndroidApiLevel apiLevel = AndroidApiLevel.P;
Builder cfBuilder =
R8Command.builder()
.setMode(CompilationMode.DEBUG)
.addLibraryFiles(ToolHelper.getAndroidJar(apiLevel))
.setProgramConsumer(programConsumer);
if (!(programConsumer instanceof ClassFileConsumer)) {
cfBuilder.setMinApiLevel(apiLevel.getLevel());
}
for (Class<?> c : inputClasses) {
byte[] classAsBytes = getClassAsBytes(c);
cfBuilder.addClassProgramData(classAsBytes, Origin.unknown());
}
if (minify) {
cfBuilder.addProguardConfiguration(
Arrays.asList(
"-keep public class com.android.tools.r8.cf.MethodHandleTest {",
" public static void main(...);",
"}"),
Origin.unknown());
}
R8.run(cfBuilder.build());
}
private ProcessResult runInput() throws Exception {
Path out = temp.getRoot().toPath().resolve("input.jar");
ClassFileConsumer.ArchiveConsumer archiveConsumer = new ClassFileConsumer.ArchiveConsumer(out);
for (Class<?> c : inputClasses) {
archiveConsumer.accept(
getClassAsBytes(c), DescriptorUtils.javaTypeToDescriptor(c.getName()), null);
}
archiveConsumer.finished(null);
ProcessResult runInput = ToolHelper.runJava(out, CLASS.getName(), ldc ? "error" : "exception");
if (runInput.exitCode != 0) {
System.out.println(runInput);
}
assertEquals(0, runInput.exitCode);
return runInput;
}
private byte[] getClassAsBytes(Class<?> clazz) throws Exception {
if (ldc) {
if (clazz == MethodHandleTest.D.class) {
return MethodHandleDump.dumpD();
} else if (clazz == MethodHandleTest.class) {
return MethodHandleDump.transform(ToolHelper.getClassAsBytes(clazz));
}
}
return ToolHelper.getClassAsBytes(clazz);
}
}