| // 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; | |
| import static org.junit.Assert.assertFalse; | |
| import static org.junit.Assert.assertTrue; | |
| import com.android.tools.r8.ToolHelper.ArtCommandBuilder; | |
| 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.AndroidApp; | |
| import com.android.tools.r8.utils.InternalOptions; | |
| import com.android.tools.r8.utils.QuadConsumer; | |
| import java.io.IOException; | |
| import java.util.Arrays; | |
| import java.util.Collections; | |
| import java.util.List; | |
| import java.util.function.Consumer; | |
| import org.junit.Assert; | |
| public class AsmTestBase extends TestBase { | |
| protected void ensureSameOutput(String main, AndroidApiLevel apiLevel, byte[]... classes) | |
| throws Exception { | |
| ensureSameOutput(main, apiLevel, Collections.emptyList(), classes); | |
| } | |
| protected void ensureSameOutput(String main, AndroidApiLevel apiLevel, | |
| List<String> args, byte[]... classes) throws Exception { | |
| AndroidApp app = buildAndroidApp(classes); | |
| Consumer<InternalOptions> setMinApiLevel = o -> o.setMinApiLevel(apiLevel); | |
| ProcessResult javaResult = runOnJavaRaw(main, Arrays.asList(classes), args); | |
| Consumer<ArtCommandBuilder> cmdBuilder = builder -> { | |
| for (String arg : args) { | |
| builder.appendProgramArgument(arg); | |
| } | |
| }; | |
| ProcessResult d8Result = runOnArtRaw( | |
| compileWithD8(app, setMinApiLevel), main, cmdBuilder, null); | |
| ProcessResult r8Result = runOnArtRaw( | |
| compileWithR8(app, setMinApiLevel), main, cmdBuilder, null); | |
| ProcessResult r8ShakenResult = runOnArtRaw( | |
| compileWithR8(app, keepMainProguardConfiguration(main) + "-dontobfuscate\n", | |
| setMinApiLevel), main, cmdBuilder, null); | |
| Assert.assertEquals(javaResult.stdout, d8Result.stdout); | |
| Assert.assertEquals(javaResult.stdout, r8Result.stdout); | |
| Assert.assertEquals(javaResult.stdout, r8ShakenResult.stdout); | |
| } | |
| protected void ensureCustomCheck( | |
| QuadConsumer<ProcessResult, ProcessResult, ProcessResult, ProcessResult> checker, | |
| String main, | |
| AndroidApiLevel apiLevel, | |
| List<String> args, | |
| byte[]... classes) | |
| throws Exception { | |
| AndroidApp app = buildAndroidApp(classes); | |
| Consumer<InternalOptions> setMinApiLevel = o -> o.setMinApiLevel(apiLevel); | |
| ProcessResult javaResult = runOnJavaRaw(main, Arrays.asList(classes), args); | |
| Consumer<ArtCommandBuilder> cmdBuilder = | |
| builder -> { | |
| for (String arg : args) { | |
| builder.appendProgramArgument(arg); | |
| } | |
| }; | |
| ProcessResult d8Result = | |
| runOnArtRaw(compileWithD8(app, setMinApiLevel), main, cmdBuilder, null); | |
| ProcessResult r8Result = | |
| runOnArtRaw(compileWithR8(app, setMinApiLevel), main, cmdBuilder, null); | |
| ProcessResult r8ShakenResult = | |
| runOnArtRaw( | |
| compileWithR8( | |
| app, keepMainProguardConfiguration(main) + "-dontobfuscate\n", setMinApiLevel), | |
| main, | |
| cmdBuilder, | |
| null); | |
| checker.accept(javaResult, d8Result, r8Result, r8ShakenResult); | |
| } | |
| protected void ensureSameOutput(String main, byte[]... classes) throws Exception { | |
| AndroidApp app = buildAndroidApp(classes); | |
| ensureSameOutput(main, app, false, classes); | |
| } | |
| protected void ensureSameOutputJavaNoVerify(String main, byte[]... classes) throws Exception { | |
| AndroidApp app = buildAndroidApp(classes); | |
| ensureSameOutput(main, app, true, classes); | |
| } | |
| private void ensureSameOutput( | |
| String main, AndroidApp app, boolean useJavaNoVerify, byte[]... classes) | |
| throws IOException, CompilationFailedException { | |
| ProcessResult javaResult = | |
| useJavaNoVerify ? runOnJavaRawNoVerify(main, classes) : runOnJavaRaw(main, classes); | |
| ProcessResult d8Result = runOnArtRaw(compileWithD8(app), main); | |
| ProcessResult r8NonShakenResult = | |
| runOnArtRaw(compileWithR8(app, "-dontshrink\n-dontobfuscate\n"), main); | |
| ProcessResult r8ShakenResult = | |
| runOnArtRaw( | |
| compileWithR8(app, keepMainProguardConfiguration(main) + "-dontobfuscate\n"), main); | |
| Assert.assertEquals(javaResult.stdout, d8Result.stdout); | |
| Assert.assertEquals(javaResult.stdout, r8NonShakenResult.stdout); | |
| Assert.assertEquals(javaResult.stdout, r8ShakenResult.stdout); | |
| Assert.assertEquals(0, javaResult.exitCode); | |
| Assert.assertEquals(0, d8Result.exitCode); | |
| Assert.assertEquals(0, r8NonShakenResult.exitCode); | |
| Assert.assertEquals(0, r8ShakenResult.exitCode); | |
| } | |
| protected void ensureR8FailsWithCompilationError(String main, byte[]... classes) | |
| throws Exception { | |
| // TODO(zerny): Port this to use diagnostics handler. | |
| AndroidApp app = buildAndroidApp(classes); | |
| CompilationFailedException r8Error = null; | |
| CompilationFailedException r8ShakenError = null; | |
| try { | |
| runOnArtRaw(compileWithR8(app, "-dontshrink\n-dontobfuscate\n"), main); | |
| } catch (CompilationFailedException e) { | |
| r8Error = e; | |
| } | |
| try { | |
| runOnArtRaw(compileWithR8(app, keepMainProguardConfiguration(main) + "-dontobfuscate\n"), | |
| main); | |
| } catch (CompilationFailedException e) { | |
| r8ShakenError = e; | |
| } | |
| Assert.assertNotNull(r8Error); | |
| Assert.assertNotNull(r8ShakenError); | |
| } | |
| protected void ensureSameOutputAfterMerging(String main, byte[]... classes) | |
| throws IOException, CompilationFailedException { | |
| AndroidApp app = buildAndroidApp(classes); | |
| // Compile to dex files with D8. | |
| AndroidApp dexApp = compileWithD8(app); | |
| // Perform dex merging with D8 to read the dex files. | |
| AndroidApp mergedApp = compileWithD8(dexApp); | |
| ensureSameOutput(main, mergedApp, false, classes); | |
| } | |
| protected static AndroidApp readClassesAndAsmDump( | |
| List<Class<?>> classes, List<byte[]> asmClasses) { | |
| AndroidApp.Builder builder = AndroidApp.builder(); | |
| for (Class clazz : classes) { | |
| builder.addProgramFiles(ToolHelper.getClassFileForTestClass(clazz)); | |
| } | |
| for (byte[] clazz : asmClasses) { | |
| builder.addClassProgramData(clazz, Origin.unknown()); | |
| } | |
| return builder.build(); | |
| } | |
| private void ensureExceptionThrown(ProcessResult result, Class<? extends Throwable> exception) { | |
| assertFalse(result.stdout, result.exitCode == 0); | |
| assertTrue(result.stderr, result.stderr.contains(exception.getCanonicalName())); | |
| } | |
| @FunctionalInterface | |
| protected interface AsmDump { | |
| byte[] dump() throws Exception; | |
| } | |
| protected static byte[] getBytesFromAsmClass(AsmDump asmDump) { | |
| try { | |
| return asmDump.dump(); | |
| } catch (Exception e) { | |
| throw new ClassFormatError(e.toString()); | |
| } | |
| } | |
| } |