| // 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; | |
| import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage; | |
| import static com.android.tools.r8.TestBase.Backend.DEX; | |
| import static com.android.tools.r8.TestBase.testForD8; | |
| import static com.android.tools.r8.utils.CfUtils.extractClassDescriptor; | |
| import static org.hamcrest.CoreMatchers.not; | |
| import static org.hamcrest.MatcherAssert.assertThat; | |
| import static org.junit.Assert.assertEquals; | |
| import static org.junit.Assert.assertTrue; | |
| import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer; | |
| import com.android.tools.r8.TestBase.Backend; | |
| import com.android.tools.r8.TestRuntime.DexRuntime; | |
| import com.android.tools.r8.ToolHelper.ArtCommandBuilder; | |
| import com.android.tools.r8.ToolHelper.DexVm; | |
| import com.android.tools.r8.ToolHelper.ProcessResult; | |
| import com.android.tools.r8.benchmarks.BenchmarkResults; | |
| import com.android.tools.r8.debug.DebugTestConfig; | |
| import com.android.tools.r8.errors.Unimplemented; | |
| import com.android.tools.r8.errors.Unreachable; | |
| import com.android.tools.r8.origin.Origin; | |
| import com.android.tools.r8.utils.AndroidApiLevel; | |
| import com.android.tools.r8.utils.AndroidApp; | |
| import com.android.tools.r8.utils.DescriptorUtils; | |
| import com.android.tools.r8.utils.FileUtils; | |
| import com.android.tools.r8.utils.InternalOptions; | |
| import com.android.tools.r8.utils.ThrowingConsumer; | |
| import com.android.tools.r8.utils.TriFunction; | |
| import com.android.tools.r8.utils.codeinspector.ClassSubject; | |
| import com.android.tools.r8.utils.codeinspector.CodeInspector; | |
| import com.android.tools.r8.utils.codeinspector.Matchers; | |
| import com.google.common.collect.ImmutableList; | |
| import com.google.common.collect.ImmutableSet; | |
| import com.google.common.collect.ObjectArrays; | |
| import java.io.File; | |
| import java.io.IOException; | |
| import java.io.PrintStream; | |
| import java.io.UncheckedIOException; | |
| import java.nio.file.Path; | |
| import java.util.ArrayList; | |
| import java.util.Arrays; | |
| import java.util.Collection; | |
| import java.util.Collections; | |
| import java.util.List; | |
| import java.util.Set; | |
| import java.util.concurrent.ExecutionException; | |
| import java.util.function.BiConsumer; | |
| import java.util.function.Consumer; | |
| import java.util.function.Function; | |
| import java.util.stream.Collectors; | |
| import org.hamcrest.Matcher; | |
| import org.hamcrest.MatcherAssert; | |
| public abstract class TestCompileResult< | |
| CR extends TestCompileResult<CR, RR>, RR extends TestRunResult<RR>> | |
| extends TestBaseResult<CR, RR> { | |
| public final AndroidApp app; | |
| public final int minApiLevel; | |
| private final OutputMode outputMode; | |
| final List<Path> additionalRunClassPath = new ArrayList<>(); | |
| final List<Path> additionalBootClasspath = new ArrayList<>(); | |
| final List<String> vmArguments = new ArrayList<>(); | |
| private boolean withArt6Plus64BitsLib = false; | |
| private boolean withArtFrameworks = true; | |
| private LibraryDesugaringTestConfiguration libraryDesugaringTestConfiguration; | |
| TestCompileResult(TestState state, AndroidApp app, int minApiLevel, OutputMode outputMode) { | |
| super(state); | |
| this.app = app; | |
| this.minApiLevel = minApiLevel; | |
| this.outputMode = outputMode; | |
| this.libraryDesugaringTestConfiguration = LibraryDesugaringTestConfiguration.DISABLED; | |
| } | |
| TestCompileResult( | |
| TestState state, | |
| AndroidApp app, | |
| int minApiLevel, | |
| OutputMode outputMode, | |
| LibraryDesugaringTestConfiguration libraryDesugaringTestConfiguration) { | |
| super(state); | |
| this.app = app; | |
| this.minApiLevel = minApiLevel; | |
| this.outputMode = outputMode; | |
| this.libraryDesugaringTestConfiguration = libraryDesugaringTestConfiguration; | |
| } | |
| public CR addVmArguments(Collection<String> arguments) { | |
| vmArguments.addAll(arguments); | |
| return self(); | |
| } | |
| public CR addVmArguments(String... arguments) { | |
| return addVmArguments(Arrays.asList(arguments)); | |
| } | |
| public CR noVerify() { | |
| return addVmArguments("-noverify"); | |
| } | |
| public CR applyIf(boolean condition, ThrowableConsumer<CR> thenConsumer) { | |
| return applyIf(condition, thenConsumer, result -> {}); | |
| } | |
| public CR applyIf( | |
| boolean condition, ThrowableConsumer<CR> thenConsumer, ThrowableConsumer<CR> elseConsumer) { | |
| if (condition) { | |
| thenConsumer.acceptWithRuntimeException(self()); | |
| } else { | |
| elseConsumer.acceptWithRuntimeException(self()); | |
| } | |
| return self(); | |
| } | |
| public final CR withArt6Plus64BitsLib() { | |
| withArt6Plus64BitsLib = true; | |
| return self(); | |
| } | |
| public final CR withArtFrameworks() { | |
| withArtFrameworks = true; | |
| return self(); | |
| } | |
| public final AndroidApp getApp() { | |
| return app; | |
| } | |
| public final Backend getBackend() { | |
| if (outputMode == OutputMode.ClassFile) { | |
| return Backend.CF; | |
| } | |
| assert outputMode == OutputMode.DexIndexed | |
| || outputMode == OutputMode.DexFilePerClass | |
| || outputMode == OutputMode.DexFilePerClassFile; | |
| return Backend.DEX; | |
| } | |
| public abstract TestDiagnosticMessages getDiagnosticMessages(); | |
| public CR inspectDiagnosticMessages(Consumer<TestDiagnosticMessages> consumer) { | |
| consumer.accept(getDiagnosticMessages()); | |
| return self(); | |
| } | |
| public abstract Set<String> getMainDexClasses(); | |
| public final CR inspectMainDexClasses(Consumer<Set<String>> consumer) { | |
| consumer.accept(getMainDexClasses()); | |
| return self(); | |
| } | |
| public final CR inspectMainDexClasses(BiConsumer<CodeInspector, Set<String>> consumer) | |
| throws IOException { | |
| consumer.accept(inspector(), getMainDexClasses()); | |
| return self(); | |
| } | |
| public abstract String getStdout(); | |
| public abstract String getStderr(); | |
| public OutputMode getOutputMode() { | |
| return outputMode; | |
| } | |
| protected abstract RR createRunResult(TestRuntime runtime, ProcessResult result); | |
| @Deprecated | |
| public RR run(Class<?> mainClass) throws ExecutionException, IOException { | |
| return run(mainClass.getTypeName()); | |
| } | |
| @Deprecated | |
| public RR run(String mainClass) throws IOException { | |
| assert !libraryDesugaringTestConfiguration.isEnabled(); | |
| ClassSubject mainClassSubject = inspector().clazz(mainClass); | |
| MatcherAssert.assertThat(mainClassSubject, Matchers.isPresent()); | |
| switch (getBackend()) { | |
| case DEX: | |
| return runArt( | |
| new DexRuntime(ToolHelper.getDexVm()), | |
| mainClassSubject.getFinalName()); | |
| case CF: | |
| return runJava( | |
| TestRuntime.getDefaultJavaRuntime(), | |
| mainClassSubject.getFinalName()); | |
| default: | |
| throw new Unreachable(); | |
| } | |
| } | |
| public RR run(TestRuntime runtime, Class<?> mainClass) throws ExecutionException, IOException { | |
| return run(runtime, mainClass.getTypeName()); | |
| } | |
| public RR run(TestRuntime runtime, Class<?> mainClass, String... args) | |
| throws ExecutionException, IOException { | |
| return run(runtime, mainClass.getTypeName(), args); | |
| } | |
| public RR run(TestRuntime runtime, String mainClass) throws ExecutionException, IOException { | |
| return run(runtime, mainClass, new String[] {}); | |
| } | |
| public RR run(TestRuntime runtime, String mainClass, String... args) throws IOException { | |
| assert getBackend() == runtime.getBackend(); | |
| ClassSubject mainClassSubject = inspector().clazz(mainClass); | |
| if (!mainClassSubject.isPresent()) { | |
| for (Path classpathFile : additionalRunClassPath) { | |
| mainClassSubject = new CodeInspector(classpathFile).clazz(mainClass); | |
| if (mainClassSubject.isPresent()) { | |
| break; | |
| } | |
| } | |
| } | |
| assertThat( | |
| "Did you forget a keep rule for the main method?", mainClassSubject, Matchers.isPresent()); | |
| if (runtime.isDex()) { | |
| return runArt(runtime, mainClassSubject.getFinalName(), args); | |
| } | |
| assert runtime.isCf(); | |
| return runJava( | |
| runtime, | |
| ObjectArrays.concat(mainClassSubject.getFinalName(), args)); | |
| } | |
| public RR runWithJaCoCo(Path output, TestRuntime runtime, String mainClass, String... args) | |
| throws IOException, ExecutionException { | |
| setSystemProperty("jacoco-agent.destfile", output.toString()); | |
| setSystemProperty("jacoco-agent.dumponexit", "true"); | |
| setSystemProperty("jacoco-agent.output", "file"); | |
| return run(runtime, mainClass, args); | |
| } | |
| public CR addRunClasspathFiles(Path... classpath) { | |
| return addRunClasspathFiles(Arrays.asList(classpath)); | |
| } | |
| public CR addRunClasspathFiles(List<Path> classpath) { | |
| additionalRunClassPath.addAll(classpath); | |
| return self(); | |
| } | |
| public CR addRunClasspathClasses(Class<?>... classpath) throws Exception { | |
| return addRunClasspathClasses(Arrays.asList(classpath)); | |
| } | |
| public CR addRunClasspathClasses(List<Class<?>> classpath) throws Exception { | |
| if (getBackend() == Backend.DEX) { | |
| return addRunClasspathFiles( | |
| testForD8(state.getTempFolder()) | |
| .addProgramClasses(classpath) | |
| .setMinApi(minApiLevel) | |
| .compile() | |
| .writeToZip()); | |
| } | |
| assert getBackend() == Backend.CF; | |
| try { | |
| Path path = state.getNewTempFolder().resolve("runtime-classes.jar"); | |
| ArchiveConsumer consumer = new ArchiveConsumer(path); | |
| for (Class clazz : classpath) { | |
| consumer.accept( | |
| ByteDataView.of(ToolHelper.getClassAsBytes(clazz)), | |
| DescriptorUtils.javaTypeToDescriptor(clazz.getTypeName()), | |
| null); | |
| } | |
| consumer.finished(null); | |
| additionalRunClassPath.addAll(Collections.singletonList(path)); | |
| return self(); | |
| } catch (IOException e) { | |
| throw new RuntimeException(e); | |
| } | |
| } | |
| public CR addRunClasspathClassFileData(byte[]... classes) throws Exception { | |
| return addRunClasspathClassFileData(Arrays.asList(classes)); | |
| } | |
| public CR addRunClasspathClassFileData(Collection<byte[]> classes) throws Exception { | |
| if (getBackend() == Backend.DEX) { | |
| additionalRunClassPath.add( | |
| testForD8(state.getTempFolder()) | |
| .addProgramClassFileData(classes) | |
| .setMinApi(minApiLevel) | |
| .compile() | |
| .writeToZip()); | |
| return self(); | |
| } | |
| assert getBackend() == Backend.CF; | |
| try { | |
| AndroidApp.Builder appBuilder = AndroidApp.builder(); | |
| for (byte[] clazz : classes) { | |
| appBuilder.addClassProgramData( | |
| clazz, Origin.unknown(), ImmutableSet.of(extractClassDescriptor(clazz))); | |
| } | |
| Path path = state.getNewTempFolder().resolve("runtime-classes.jar"); | |
| appBuilder.build().writeToZipForTesting(path, OutputMode.ClassFile); | |
| additionalRunClassPath.add(path); | |
| return self(); | |
| } catch (IOException e) { | |
| throw new RuntimeException(e); | |
| } | |
| } | |
| public CR addBootClasspathClasses(Class<?>... classes) throws Exception { | |
| return addBootClasspathClasses(Arrays.asList(classes)); | |
| } | |
| public CR addBootClasspathClasses(List<Class<?>> classes) throws Exception { | |
| if (getBackend() == Backend.DEX) { | |
| additionalBootClasspath.add( | |
| testForD8(state.getTempFolder()) | |
| .addProgramClasses(classes) | |
| .setMinApi(minApiLevel) | |
| .compile() | |
| .writeToZip()); | |
| return self(); | |
| } | |
| assert getBackend() == Backend.CF; | |
| try { | |
| Path path = state.getNewTempFolder().resolve("runtime-classes.jar"); | |
| ArchiveConsumer consumer = new ArchiveConsumer(path); | |
| for (Class<?> clazz : classes) { | |
| consumer.accept( | |
| ByteDataView.of(ToolHelper.getClassAsBytes(clazz)), | |
| DescriptorUtils.javaTypeToDescriptor(clazz.getTypeName()), | |
| null); | |
| } | |
| consumer.finished(null); | |
| additionalBootClasspath.add(path); | |
| return self(); | |
| } catch (IOException e) { | |
| throw new RuntimeException(e); | |
| } | |
| } | |
| public CR addBootClasspathFiles(Path... files) { | |
| return addBootClasspathFiles(Arrays.asList(files)); | |
| } | |
| public CR addBootClasspathFiles(Collection<Path> files) { | |
| additionalBootClasspath.addAll(files); | |
| return self(); | |
| } | |
| public CR addDesugaredCoreLibraryRunClassPath( | |
| Function<AndroidApiLevel, Path> classPathSupplier, AndroidApiLevel minAPILevel) { | |
| addRunClasspathFiles(classPathSupplier.apply(minAPILevel)); | |
| return self(); | |
| } | |
| public CR addDesugaredCoreLibraryRunClassPath( | |
| TriFunction<AndroidApiLevel, String, Boolean, Path> classPathSupplier, | |
| AndroidApiLevel minAPILevel, | |
| String keepRules, | |
| boolean shrink) { | |
| addRunClasspathFiles(classPathSupplier.apply(minAPILevel, keepRules, shrink)); | |
| return self(); | |
| } | |
| public CR enableRuntimeAssertions() { | |
| assert getBackend() == Backend.CF; | |
| if (!vmArguments.contains("-ea")) { | |
| vmArguments.add("-ea"); | |
| } | |
| return self(); | |
| } | |
| public CR enableJVMPreview() { | |
| assert getBackend() == Backend.CF; | |
| if (!vmArguments.contains("--enable-preview")) { | |
| vmArguments.add("--enable-preview"); | |
| } | |
| return self(); | |
| } | |
| public CR enableRuntimeAssertions(boolean enable) { | |
| if (getBackend() == Backend.CF) { | |
| if (enable) { | |
| enableRuntimeAssertions(); | |
| } | |
| } else { | |
| // Assertions cannot be enabled on dex VMs. | |
| assert !enable; | |
| } | |
| if (enable) { | |
| if (!this.vmArguments.contains("-ea")) { | |
| this.vmArguments.add("-ea"); | |
| } | |
| } | |
| return self(); | |
| } | |
| public CR setSystemProperty(String name, String value) { | |
| vmArguments.add("-D" + name + "=" + value); | |
| return self(); | |
| } | |
| public CR writeSingleDexOutputToFile(Path file) throws IOException { | |
| assertTrue(getApp().getClassProgramResourcesForTesting().isEmpty()); | |
| try { | |
| List<ProgramResource> dexProgramSources = getApp().getDexProgramResourcesForTesting(); | |
| assertEquals(1, dexProgramSources.size()); | |
| FileUtils.writeToFile(file, null, dexProgramSources.get(0).getBytes()); | |
| return self(); | |
| } catch (ResourceException e) { | |
| throw new IOException(e); | |
| } | |
| } | |
| public Path writeToZip() throws IOException { | |
| Path file = state.getNewTempFolder().resolve("out.zip"); | |
| writeToZip(file); | |
| return file; | |
| } | |
| public CR writeToZip(Consumer<Path> fn) throws IOException { | |
| fn.accept(writeToZip()); | |
| return self(); | |
| } | |
| public CR writeToZip(Path file) throws IOException { | |
| app.writeToZipForTesting(file, getOutputMode()); | |
| return self(); | |
| } | |
| public Path writeToDirectory() throws IOException { | |
| Path directory = state.getNewTempFolder(); | |
| writeToDirectory(directory); | |
| return directory; | |
| } | |
| public CR writeToDirectory(Path directory) throws IOException { | |
| app.writeToDirectory(directory, getOutputMode()); | |
| return self(); | |
| } | |
| public CodeInspector inspector() throws IOException { | |
| return new CodeInspector(app); | |
| } | |
| public CodeInspector inspector(Consumer<InternalOptions> debugOptionsConsumer) | |
| throws IOException { | |
| return new CodeInspector(app, debugOptionsConsumer); | |
| } | |
| public <E extends Throwable> CR inspect(ThrowingConsumer<CodeInspector, E> consumer) | |
| throws IOException, E { | |
| consumer.accept(inspector()); | |
| return self(); | |
| } | |
| public <E extends Throwable> CR inspectIf( | |
| boolean condition, ThrowingConsumer<CodeInspector, E> consumer) throws IOException, E { | |
| if (condition) { | |
| consumer.accept(inspector()); | |
| } | |
| return self(); | |
| } | |
| @SuppressWarnings("unchecked") | |
| public <E extends Throwable> CR inspectMultiDex(ThrowingConsumer<CodeInspector, E>... consumers) | |
| throws E { | |
| return inspectMultiDex(null, consumers); | |
| } | |
| @SafeVarargs | |
| public final <E extends Throwable> CR inspectMultiDex( | |
| Path mappingFile, ThrowingConsumer<CodeInspector, E>... consumers) throws E { | |
| try { | |
| Path out = state.getNewTempFolder(); | |
| getApp().writeToDirectory(out, OutputMode.DexIndexed); | |
| consumers[0].accept(new CodeInspector(out.resolve("classes.dex"), mappingFile)); | |
| for (int i = 1; i < consumers.length; i++) { | |
| Path dex = out.resolve("classes" + (i + 1) + ".dex"); | |
| CodeInspector inspector = | |
| dex.toFile().exists() ? new CodeInspector(dex, mappingFile) : CodeInspector.empty(); | |
| consumers[i].accept(inspector); | |
| } | |
| } catch (IOException e) { | |
| throw new UncheckedIOException(e); | |
| } | |
| return self(); | |
| } | |
| public <E extends Throwable> CR inspectWithOptions( | |
| ThrowingConsumer<CodeInspector, E> consumer, Consumer<InternalOptions> debugOptionsConsumer) | |
| throws IOException, E { | |
| consumer.accept(inspector(debugOptionsConsumer)); | |
| return self(); | |
| } | |
| public CR assertNoMessages() { | |
| getDiagnosticMessages().assertNoMessages(); | |
| return self(); | |
| } | |
| public CR assertOnlyInfos() { | |
| getDiagnosticMessages().assertOnlyInfos(); | |
| return self(); | |
| } | |
| public CR assertOnlyWarnings() { | |
| getDiagnosticMessages().assertOnlyWarnings(); | |
| return self(); | |
| } | |
| public CR assertOnlyErrors() { | |
| getDiagnosticMessages().assertOnlyErrors(); | |
| return self(); | |
| } | |
| public CR assertDiagnosticThatMatches(Matcher<Diagnostic> matcher) { | |
| getDiagnosticMessages().assertDiagnosticThatMatches(matcher); | |
| return self(); | |
| } | |
| public CR assertInfoThatMatches(Matcher<Diagnostic> matcher) { | |
| getDiagnosticMessages().assertInfoThatMatches(matcher); | |
| return self(); | |
| } | |
| public CR assertInfoMessageThatMatches(Matcher<String> matcher) { | |
| getDiagnosticMessages().assertInfoThatMatches(diagnosticMessage(matcher)); | |
| return self(); | |
| } | |
| public CR assertAllInfosMatch(Matcher<Diagnostic> matcher) { | |
| getDiagnosticMessages().assertNoInfosMatch(not(matcher)); | |
| return self(); | |
| } | |
| public CR assertAllInfoMessagesMatch(Matcher<String> matcher) { | |
| return assertNoInfoMessageThatMatches(not(matcher)); | |
| } | |
| public CR assertNoInfoThatMatches(Matcher<Diagnostic> matcher) { | |
| getDiagnosticMessages().assertNoInfosMatch(matcher); | |
| return self(); | |
| } | |
| public CR assertNoInfoMessageThatMatches(Matcher<String> matcher) { | |
| getDiagnosticMessages().assertNoInfosMatch(diagnosticMessage(matcher)); | |
| return self(); | |
| } | |
| public CR assertAtLeastOneInfoMessage() { | |
| assertTrue(getDiagnosticMessages().getInfos().size() >= 1); | |
| return self(); | |
| } | |
| public CR assertInfosCount(int count) { | |
| getDiagnosticMessages().assertInfosCount(count); | |
| return self(); | |
| } | |
| public CR assertNoInfoMessages() { | |
| getDiagnosticMessages().assertNoInfos(); | |
| return self(); | |
| } | |
| public CR assertWarningMessageThatMatches(Matcher<String> matcher) { | |
| getDiagnosticMessages().assertWarningMessageThatMatches(matcher); | |
| return self(); | |
| } | |
| public CR assertWarningThatMatches(Matcher<Diagnostic> matcher) { | |
| getDiagnosticMessages().assertWarningThatMatches(matcher); | |
| return self(); | |
| } | |
| public CR assertAllWarningMessagesMatch(Matcher<String> matcher) { | |
| getDiagnosticMessages().assertHasWarnings().assertAllWarningsMatch(diagnosticMessage(matcher)); | |
| return self(); | |
| } | |
| public CR assertNoWarningMessages() { | |
| getDiagnosticMessages().assertNoWarnings(); | |
| return self(); | |
| } | |
| public CR assertNoWarningMessageThatMatches(Matcher<String> matcher) { | |
| getDiagnosticMessages().assertNoWarningsMatch(diagnosticMessage(matcher)); | |
| return self(); | |
| } | |
| public CR assertErrorMessageThatMatches(Matcher<String> matcher) { | |
| getDiagnosticMessages().assertErrorMessageThatMatches(matcher); | |
| return self(); | |
| } | |
| public CR assertNoErrorMessages() { | |
| getDiagnosticMessages().assertNoErrors(); | |
| return self(); | |
| } | |
| public CR assertNoStdout() { | |
| assertEquals("", getStdout()); | |
| return self(); | |
| } | |
| public CR assertStdoutThatMatches(Matcher<String> matcher) { | |
| assertThat(getStdout(), matcher); | |
| return self(); | |
| } | |
| public CR assertNoStderr() { | |
| assertEquals("", getStderr()); | |
| return self(); | |
| } | |
| public CR assertStderrThatMatches(Matcher<String> matcher) { | |
| assertThat(getStderr(), matcher); | |
| return self(); | |
| } | |
| public CR disassemble(PrintStream ps) throws IOException { | |
| ToolHelper.disassemble(app, ps); | |
| return self(); | |
| } | |
| public CR disassemble() throws IOException, ExecutionException { | |
| return disassemble(System.out); | |
| } | |
| @Deprecated | |
| public DebugTestConfig debugConfig() { | |
| return debugConfig( | |
| getBackend().isCf() | |
| ? TestRuntime.getDefaultCfRuntime() | |
| : new DexRuntime(ToolHelper.getDexVm())); | |
| } | |
| public DebugTestConfig debugConfig(TestRuntime runtime) { | |
| assert runtime.getBackend() == getBackend(); | |
| // Rethrow exceptions since debug config is usually used in a delayed wrapper which | |
| // does not declare exceptions. | |
| try { | |
| Path out = state.getNewTempFolder().resolve("out.zip"); | |
| app.writeToZipForTesting(out, getOutputMode()); | |
| return DebugTestConfig.create(runtime, out); | |
| } catch (IOException e) { | |
| throw new RuntimeException(e); | |
| } | |
| } | |
| private RR runJava(TestRuntime runtime, String... arguments) throws IOException { | |
| assert runtime != null; | |
| Path out = state.getNewTempFolder().resolve("out.zip"); | |
| app.writeToZipForTesting(out, OutputMode.ClassFile); | |
| List<Path> classPath = | |
| ImmutableList.<Path>builder().addAll(additionalRunClassPath).add(out).build(); | |
| ProcessResult result = | |
| ToolHelper.runJava( | |
| runtime.asCf(), vmArguments, additionalBootClasspath, classPath, arguments); | |
| return createRunResult(runtime, result); | |
| } | |
| RR runArt(TestRuntime runtime, String mainClass, String... arguments) throws IOException { | |
| DexVm vm = runtime.asDex().getVm(); | |
| // TODO(b/127785410): Always assume a non-null runtime. | |
| Path out = state.getNewTempFolder().resolve("out.zip"); | |
| app.writeToZipForTesting(out, OutputMode.DexIndexed); | |
| List<String> classPath = | |
| ImmutableList.<String>builder() | |
| .addAll( | |
| additionalRunClassPath.stream().map(Path::toString).collect(Collectors.toList())) | |
| .add(out.toString()) | |
| .build(); | |
| Consumer<ArtCommandBuilder> commandConsumer = | |
| withArt6Plus64BitsLib && vm.getVersion().isNewerThanOrEqual(DexVm.Version.V6_0_1) | |
| ? builder -> builder.appendArtOption("--64") | |
| : builder -> {}; | |
| commandConsumer = | |
| commandConsumer.andThen( | |
| builder -> { | |
| if (!additionalBootClasspath.isEmpty()) { | |
| DexVm dexVm = runtime.asDex().getVm(); | |
| if (dexVm.isNewerThan(DexVm.ART_4_4_4_HOST)) { | |
| builder.appendArtOption("-Ximage:/system/non/existent/image.art"); | |
| builder.appendArtOption("-Xnoimage-dex2oat"); | |
| } | |
| try { | |
| for (String s : ToolHelper.getBootLibs(dexVm)) { | |
| builder.appendBootClasspath(new File(s).getCanonicalPath()); | |
| } | |
| } catch (Exception e) { | |
| throw new RuntimeException(); | |
| } | |
| additionalBootClasspath.forEach( | |
| path -> builder.appendBootClasspath(path.toString())); | |
| } | |
| for (String vmArgument : vmArguments) { | |
| builder.appendArtOption(vmArgument); | |
| } | |
| }); | |
| ProcessResult result = | |
| ToolHelper.runArtRaw( | |
| classPath, mainClass, commandConsumer, vm, withArtFrameworks, arguments); | |
| return createRunResult(runtime, result); | |
| } | |
| public Dex2OatTestRunResult runDex2Oat(TestRuntime runtime) throws IOException { | |
| assert getBackend() == DEX; | |
| DexVm vm = runtime.asDex().getVm(); | |
| Path tmp = state.getNewTempFolder(); | |
| Path jarFile = tmp.resolve("out.jar"); | |
| Path oatFile = tmp.resolve("out.oat"); | |
| app.writeToZipForTesting(jarFile, OutputMode.DexIndexed); | |
| return new Dex2OatTestRunResult( | |
| app, oatFile, runtime, ToolHelper.runDex2OatRaw(jarFile, oatFile, vm), state); | |
| } | |
| public CR benchmarkCodeSize(BenchmarkResults results) throws IOException, ResourceException { | |
| results.addCodeSizeResult(app.applicationSize()); | |
| return self(); | |
| } | |
| public CR benchmarkInstructionCodeSize(BenchmarkResults results) | |
| throws IOException, ResourceException { | |
| throw new Unimplemented(); | |
| } | |
| public CR benchmarkDexSegmentsCodeSize(BenchmarkResults results) | |
| throws IOException, ResourceException { | |
| throw new Unimplemented(); | |
| } | |
| public CR benchmarkDex2OatCodeSize(BenchmarkResults results) | |
| throws IOException, ResourceException { | |
| throw new Unimplemented(); | |
| } | |
| } |