1// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file 2// for details. All rights reserved. Use of this source code is governed by a 3// BSD-style license that can be found in the LICENSE file. 4package com.android.tools.r8.utils; 5 6import static junit.framework.TestCase.assertFalse; 7import static junit.framework.TestCase.assertTrue; 8 9import com.android.tools.r8.CompilationException; 10import com.android.tools.r8.R8Command; 11import com.android.tools.r8.ToolHelper; 12import com.android.tools.r8.graph.DexEncodedMethod; 13import com.android.tools.r8.shaking.ProguardRuleParserException; 14import com.android.tools.r8.utils.DexInspector.ClassSubject; 15import com.android.tools.r8.utils.DexInspector.MethodSubject; 16import java.io.IOException; 17import java.nio.file.Files; 18import java.nio.file.Path; 19import java.nio.file.Paths; 20import java.util.Arrays; 21import java.util.Collection; 22import java.util.Collections; 23import java.util.concurrent.ExecutionException; 24import org.junit.Before; 25import org.junit.Rule; 26import org.junit.Test; 27import org.junit.rules.ExpectedException; 28import org.junit.rules.TemporaryFolder; 29import org.junit.runner.RunWith; 30import org.junit.runners.Parameterized; 31import org.junit.runners.Parameterized.Parameters; 32 33@RunWith(Parameterized.class) 34public class R8InliningTest { 35 36 private static final String JAR_EXTENSION = ".jar"; 37 private static final String DEFAULT_DEX_FILENAME = "classes.dex"; 38 private static final String DEFAULT_MAP_FILENAME = "proguard.map"; 39 40 @Parameters(name = "{0}") 41 public static Collection<Object[]> data() { 42 return Arrays.asList(new Object[][]{{"Inlining"}}); 43 } 44 45 @Rule 46 public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest(); 47 private final String name; 48 private final String keepRulesFile; 49 50 public R8InliningTest(String name) { 51 this.name = name.toLowerCase(); 52 this.keepRulesFile = ToolHelper.EXAMPLES_DIR + this.name + "/keep-rules.txt"; 53 } 54 55 private Path getInputFile() { 56 return Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, name + JAR_EXTENSION); 57 } 58 59 private Path getOriginalDexFile() { 60 return Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, name, DEFAULT_DEX_FILENAME); 61 } 62 63 private Path getGeneratedDexFile() throws IOException { 64 return Paths.get(temp.getRoot().getCanonicalPath(), DEFAULT_DEX_FILENAME); 65 } 66 67 private String getGeneratedProguardMap() throws IOException { 68 Path mapFile = Paths.get(temp.getRoot().getCanonicalPath(), DEFAULT_MAP_FILENAME); 69 if (Files.exists(mapFile)) { 70 return mapFile.toAbsolutePath().toString(); 71 } 72 return null; 73 } 74 75 @Rule 76 public ExpectedException thrown = ExpectedException.none(); 77 78 @Before 79 public void generateR8Version() 80 throws IOException, ProguardRuleParserException, ExecutionException, CompilationException { 81 Path out = temp.getRoot().toPath(); 82 R8Command command = 83 R8Command.builder() 84 .addProgramFiles(getInputFile()) 85 .setOutputPath(out) 86 .addProguardConfigurationFiles(Paths.get(keepRulesFile)) 87 .build(); 88 ToolHelper.runR8(command); 89 ToolHelper.runArtNoVerificationErrors(out + "/classes.dex", "inlining.Inlining"); 90 } 91 92 private void checkAbsent(ClassSubject clazz, String name) { 93 assertTrue(clazz.isPresent()); 94 MethodSubject method = clazz.method("boolean", name, Collections.emptyList()); 95 assertFalse(method.isPresent()); 96 } 97 98 private void dump(DexEncodedMethod method) { 99 System.out.println(method); 100 System.out.println(method.codeToString()); 101 } 102 103 private void dump(Path path, String title) throws Throwable { 104 System.out.println(title + ":"); 105 DexInspector inspector = new DexInspector(path.toAbsolutePath()); 106 inspector.clazz("inlining.Inlining").forAllMethods(m -> dump(m.getMethod())); 107 System.out.println(title + " size: " + Files.size(path)); 108 } 109 110 @Test 111 public void checkNoInvokes() throws Throwable { 112 DexInspector inspector = new DexInspector(getGeneratedDexFile().toAbsolutePath(), 113 getGeneratedProguardMap()); 114 ClassSubject clazz = inspector.clazz("inlining.Inlining"); 115 // Simple constant inlining. 116 checkAbsent(clazz, "longExpression"); 117 checkAbsent(clazz, "intExpression"); 118 checkAbsent(clazz, "doubleExpression"); 119 checkAbsent(clazz, "floatExpression"); 120 // Simple return argument inlining. 121 checkAbsent(clazz, "longArgumentExpression"); 122 checkAbsent(clazz, "intArgumentExpression"); 123 checkAbsent(clazz, "doubleArgumentExpression"); 124 checkAbsent(clazz, "floatArgumentExpression"); 125 } 126 127 @Test 128 public void processedFileIsSmaller() throws Throwable { 129 long original = Files.size(getOriginalDexFile()); 130 long generated = Files.size(getGeneratedDexFile()); 131 final boolean ALWAYS_DUMP = false; // Used for debugging. 132 if (ALWAYS_DUMP || generated > original) { 133 dump(getOriginalDexFile(), "Original"); 134 dump(getGeneratedDexFile(), "Generated"); 135 } 136 assertTrue("Inlining failed to reduce size", original > generated); 137 } 138} 139