Run TreeShakingTest on the CF backend
* Implement hasNonTrivialClassInitializer() for JarCode+CfCode
* Make SourceFileRewriter a no-op for CfCode
* Implement DexEncodedMethod.toEmptyThrowingMethodCf() for CF backend
- Requires an implementation of CfCode.buildIR() to handle this
specific pattern
Change-Id: I78d4965c53c3317d0e6235b672b2dee00d075f04
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index a14811a..61cd5a7 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -5,17 +5,28 @@
import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.cf.code.CfConstNull;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfLabel;
+import com.android.tools.r8.cf.code.CfPosition;
+import com.android.tools.r8.cf.code.CfReturnVoid;
+import com.android.tools.r8.cf.code.CfThrow;
import com.android.tools.r8.cf.code.CfTryCatch;
import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.Throw;
+import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.code.ValueNumberGenerator;
+import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.InternalOptions;
import java.util.Collections;
+import java.util.LinkedList;
import java.util.List;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
@@ -155,8 +166,35 @@
}
@Override
+ public boolean isEmptyVoidMethod() {
+ for (CfInstruction insn : instructions) {
+ if (!(insn instanceof CfReturnVoid)
+ && !(insn instanceof CfLabel)
+ && !(insn instanceof CfPosition)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
public IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options)
throws ApiLevelException {
+ if (instructions.size() == 2
+ && instructions.get(0) instanceof CfConstNull
+ && instructions.get(1) instanceof CfThrow) {
+ BasicBlock block = new BasicBlock();
+ block.setNumber(1);
+ Value nullValue = new Value(0, ValueType.OBJECT, null);
+ block.add(new ConstNumber(nullValue, 0L));
+ block.add(new Throw(nullValue));
+ block.close(null);
+ for (Instruction insn : block.getInstructions()) {
+ insn.setPosition(Position.none());
+ }
+ LinkedList<BasicBlock> blocks = new LinkedList<>(Collections.singleton(block));
+ return new IRCode(options, encodedMethod, blocks, null, false);
+ }
throw new Unimplemented("Converting Java class-file bytecode to IR not yet supported");
}
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
index 35a6381..52217a3 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -81,4 +81,6 @@
void collectMixedSectionItems(MixedSectionCollection collection) {
throw new Unreachable();
}
+
+ public abstract boolean isEmptyVoidMethod();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 58053e2..e9b9255 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -339,9 +339,7 @@
return superType == null;
}
DexEncodedMethod clinit = getClassInitializer();
- return clinit != null
- && clinit.getCode() != null
- && clinit.getCode().asDexCode().isEmptyVoidMethod();
+ return clinit != null && clinit.getCode() != null && clinit.getCode().isEmptyVoidMethod();
}
public boolean hasNonTrivialClassInitializer() {
@@ -353,11 +351,7 @@
if (clinit == null || clinit.getCode() == null) {
return false;
}
- if (clinit.getCode().isDexCode()) {
- return !clinit.getCode().asDexCode().isEmptyVoidMethod();
- }
- // For non-dex code we don't try to check the code.
- return true;
+ return !clinit.getCode().isEmptyVoidMethod();
}
public boolean hasDefaultInitializer() {
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index 7d2ac23..50117c7 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -157,6 +157,7 @@
return false;
}
+ @Override
public boolean isEmptyVoidMethod() {
return instructions.length == 1 && instructions[0] instanceof ReturnVoid;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 0a357bf..e18bf8a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -10,6 +10,9 @@
import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_NOT_INLINING_CANDIDATE;
import com.android.tools.r8.ApiLevelException;
+import com.android.tools.r8.cf.code.CfConstNull;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfThrow;
import com.android.tools.r8.code.Const;
import com.android.tools.r8.code.ConstString;
import com.android.tools.r8.code.ConstStringJumbo;
@@ -40,6 +43,7 @@
import com.android.tools.r8.naming.MemberNaming.Signature;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.InternalOptions;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
@@ -365,12 +369,28 @@
outRegisters, instructions, new DexCode.Try[0], new DexCode.TryHandler[0], null, null);
}
- public DexEncodedMethod toEmptyThrowingMethod() {
- Instruction insn[] = {new Const(0, 0), new Throw(0)};
- DexCode code = generateCodeFromTemplate(1, 0, insn);
- assert !accessFlags.isAbstract();
+ public DexEncodedMethod toEmptyThrowingMethodDex() {
+ assert !accessFlags.isAbstract() && !accessFlags.isNative();
Builder builder = builder(this);
- builder.setCode(code);
+ Instruction insn[] = {new Const(0, 0), new Throw(0)};
+ DexCode emptyThrowingCode = generateCodeFromTemplate(1, 0, insn);
+ builder.setCode(emptyThrowingCode);
+ return builder.build();
+ }
+
+ public DexEncodedMethod toEmptyThrowingMethodCf() {
+ assert !accessFlags.isAbstract() && !accessFlags.isNative();
+ Builder builder = builder(this);
+ CfInstruction insn[] = {new CfConstNull(), new CfThrow()};
+ CfCode emptyThrowingCode =
+ new CfCode(
+ method,
+ 1,
+ method.proto.parameters.size() + 1,
+ Arrays.asList(insn),
+ Collections.emptyList(),
+ Collections.emptyList());
+ builder.setCode(emptyThrowingCode);
return builder.build();
}
diff --git a/src/main/java/com/android/tools/r8/graph/JarCode.java b/src/main/java/com/android/tools/r8/graph/JarCode.java
index aa1faaf..8c1a6c5 100644
--- a/src/main/java/com/android/tools/r8/graph/JarCode.java
+++ b/src/main/java/com/android/tools/r8/graph/JarCode.java
@@ -18,12 +18,16 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.JSRInlinerAdapter;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.LabelNode;
+import org.objectweb.asm.tree.LineNumberNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.util.Textifier;
import org.objectweb.asm.util.TraceMethodVisitor;
@@ -98,6 +102,19 @@
}
@Override
+ public boolean isEmptyVoidMethod() {
+ for (Iterator<AbstractInsnNode> it = getNode().instructions.iterator(); it.hasNext(); ) {
+ AbstractInsnNode insn = it.next();
+ if (insn.getType() != Opcodes.RETURN
+ && !(insn instanceof LabelNode)
+ && !(insn instanceof LineNumberNode)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
public IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options)
throws ApiLevelException {
triggerDelayedParsingIfNeccessary();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 41e4afb..dba6183 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -996,6 +996,11 @@
}
@Override
+ public boolean isEmptyVoidMethod() {
+ return false;
+ }
+
+ @Override
public IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options)
throws ApiLevelException {
OutlineSourceCode source = new OutlineSourceCode(outline);
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java
index e90b135..53bc57e 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java
@@ -32,6 +32,11 @@
}
@Override
+ public boolean isEmptyVoidMethod() {
+ return false;
+ }
+
+ @Override
public final IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options)
throws ApiLevelException {
return new IRBuilder(encodedMethod, sourceCode, options).build();
diff --git a/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java b/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
index 3736008..1dd6e31 100644
--- a/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
+++ b/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
@@ -50,16 +50,20 @@
if (code == null) {
return;
}
- assert code.isDexCode();
- DexDebugInfo dexDebugInfo = code.asDexCode().getDebugInfo();
- if (dexDebugInfo == null) {
- return;
+ if (code.isDexCode()) {
+ DexDebugInfo dexDebugInfo = code.asDexCode().getDebugInfo();
+ if (dexDebugInfo == null) {
+ return;
+ }
+ // Thanks to a single global source file, we can safely remove DBG_SET_FILE entirely.
+ dexDebugInfo.events =
+ Arrays.stream(dexDebugInfo.events)
+ .filter(dexDebugEvent -> !(dexDebugEvent instanceof SetFile))
+ .toArray(DexDebugEvent[]::new);
+ } else {
+ assert code.isCfCode();
+ // CF has nothing equivalent to SetFile, so there is nothing to remove.
}
- // Thanks to a single global source file, we can safely remove DBG_SET_FILE entirely.
- dexDebugInfo.events =
- Arrays.stream(dexDebugInfo.events)
- .filter(dexDebugEvent -> !(dexDebugEvent instanceof SetFile))
- .toArray(DexDebugEvent[]::new);
});
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index 27a6405..962247e 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -151,9 +151,12 @@
// this can only happen as the result of an invalid invoke. They will not actually be
// called at runtime but we have to keep them as non-abstract (see above) to produce the
// same failure mode.
- reachableMethods.add(allowAbstract
- ? method.toAbstractMethod()
- : method.toEmptyThrowingMethod());
+ reachableMethods.add(
+ allowAbstract
+ ? method.toAbstractMethod()
+ : (options.isGeneratingClassFiles()
+ ? method.toEmptyThrowingMethodCf()
+ : method.toEmptyThrowingMethodDex()));
} else {
if (Log.ENABLED) {
Log.debug(getClass(), "Removing method %s.", method.method);
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
index fe1f1eb..ae2a432 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -10,6 +10,8 @@
import com.android.tools.r8.TestBase.MinifyMode;
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.errors.Unreachable;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -57,8 +59,7 @@
public class TreeShakingTest {
private static final Path ANDROID_JAR = ToolHelper.getDefaultAndroidJar();
- private static final List<Path> JAR_LIBRARIES = ImmutableList
- .of(ANDROID_JAR, Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + "shakinglib.jar"));
+ private final List<Path> JAR_LIBRARIES;
private static final String EMPTY_FLAGS = "src/test/proguard/valid/empty.flags";
private static final Set<String> IGNORED_FLAGS = ImmutableSet.of(
"examples/minification:conflict-mapping.txt",
@@ -66,33 +67,35 @@
);
private static final Set<String> IGNORED = ImmutableSet.of(
// there's no point in running those without obfuscation
- "examples/shaking1:keep-rules-repackaging.txt:DEX:NONE",
- "examples/shaking1:keep-rules-repackaging.txt:JAR:NONE",
- "examples/shaking16:keep-rules-1.txt:DEX:NONE",
- "examples/shaking16:keep-rules-1.txt:JAR:NONE",
- "examples/shaking16:keep-rules-2.txt:DEX:NONE",
- "examples/shaking16:keep-rules-2.txt:JAR:NONE",
- "examples/shaking15:keep-rules.txt:DEX:NONE",
- "examples/shaking15:keep-rules.txt:JAR:NONE",
- "examples/minifygeneric:keep-rules.txt:DEX:NONE",
- "examples/minifygeneric:keep-rules.txt:JAR:NONE",
- "examples/minifygenericwithinner:keep-rules.txt:DEX:NONE",
- "examples/minifygenericwithinner:keep-rules.txt:JAR:NONE",
+ "examples/shaking1:keep-rules-repackaging.txt:*:NONE",
+ "examples/shaking16:keep-rules-1.txt:*:NONE",
+ "examples/shaking16:keep-rules-2.txt:*:NONE",
+ "examples/shaking15:keep-rules.txt:*:NONE",
+ "examples/minifygeneric:keep-rules.txt:*:NONE",
+ "examples/minifygenericwithinner:keep-rules.txt:*:NONE",
// No prebuild DEX files for AndroidN
- "examplesAndroidN/shaking:keep-rules.txt:DEX:NONE",
- "examplesAndroidN/shaking:keep-rules.txt:DEX:JAVA",
- "examplesAndroidN/shaking:keep-rules.txt:DEX:AGGRESSIVE"
+ "examplesAndroidN/shaking:keep-rules.txt:DEX:DEX:*",
+ // TODO(b/75997473): No inlining in R8/CF yet
+ "examples/inlining:keep-rules-discard.txt:JAR:CF:*",
+ "examples/inlining:keep-rules.txt:JAR:CF:*"
);
private static Set<String> SKIPPED = Collections.emptySet();
private final MinifyMode minify;
+ private Path proguardMap;
+ private Path out;
private enum Frontend {
DEX, JAR
-
}
- private final Frontend kind;
+
+ private enum Backend {
+ DEX, CF
+ }
+
+ private final Frontend frontend;
+ private final Backend backend;
private final String originalDex;
private final String programFile;
private final String mainClass;
@@ -105,13 +108,14 @@
@Rule
public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
- public TreeShakingTest(String test, Frontend kind, String mainClass, List<String> keepRulesFiles,
- MinifyMode minify, Consumer<DexInspector> inspection,
+ public TreeShakingTest(String test, Frontend frontend, Backend backend, String mainClass,
+ List<String> keepRulesFiles, MinifyMode minify, Consumer<DexInspector> inspection,
BiConsumer<String, String> outputComparator,
BiConsumer<DexInspector, DexInspector> dexComparator) {
- this.kind = kind;
+ this.frontend = frontend;
+ this.backend = backend;
originalDex = ToolHelper.TESTS_BUILD_DIR + test + "/classes.dex";
- if (kind == Frontend.DEX) {
+ if (frontend == Frontend.DEX) {
this.programFile = originalDex;
} else {
this.programFile = ToolHelper.TESTS_BUILD_DIR + test + ".jar";
@@ -122,12 +126,23 @@
this.minify = minify;
this.outputComparator = outputComparator;
this.dexComparator = dexComparator;
+ if (backend == Backend.CF) {
+ JAR_LIBRARIES =
+ ImmutableList.of(
+ Paths.get(ToolHelper.JAVA_8_RUNTIME),
+ Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + "shakinglib.jar"));
+ } else {
+ JAR_LIBRARIES =
+ ImmutableList.of(
+ ANDROID_JAR, Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + "shakinglib.jar"));
+ }
}
@Before
public void generateTreeShakedVersion() throws Exception {
+ out = temp.getRoot().toPath().resolve("out.zip");
+ proguardMap = temp.getRoot().toPath().resolve(DEFAULT_PROGUARD_MAP_FILE);
// Generate R8 processed version without library option.
- Path out = temp.getRoot().toPath();
boolean inline = programFile.contains("inlining");
R8Command.Builder builder =
@@ -135,15 +150,24 @@
R8Command.builder(),
pgConfig -> {
pgConfig.setPrintMapping(true);
- pgConfig.setPrintMappingFile(out.resolve(ToolHelper.DEFAULT_PROGUARD_MAP_FILE));
+ pgConfig.setPrintMappingFile(proguardMap);
pgConfig.setOverloadAggressively(minify == MinifyMode.AGGRESSIVE);
if (!minify.isMinify()) {
pgConfig.disableObfuscation();
}
})
- .setOutput(out, OutputMode.DexIndexed)
.addProguardConfigurationFiles(ListUtils.map(keepRulesFiles, Paths::get))
.addLibraryFiles(JAR_LIBRARIES);
+ switch (backend) {
+ case CF:
+ builder.setOutput(out, OutputMode.ClassFile);
+ break;
+ case DEX:
+ builder.setOutput(out, OutputMode.DexIndexed);
+ break;
+ default:
+ throw new Unreachable();
+ }
ToolHelper.getAppBuilder(builder).addProgramFiles(Paths.get(programFile));
ToolHelper.runR8(builder.build(), options -> options.enableInlining = inline);
}
@@ -364,6 +388,10 @@
ClassSubject mainClass = inspector.clazz("shaking13.Shaking");
MethodSubject testMethod = mainClass.method("void", "fieldTest", Collections.emptyList());
Assert.assertTrue(testMethod.isPresent());
+ if (testMethod.getMethod().getCode().isJarCode()) {
+ // TODO(mathiasr): Implement iterateInstructions() for JarCode/CfCode
+ return;
+ }
Iterator<FieldAccessInstructionSubject> iterator =
testMethod.iterateInstructions(InstructionSubject::isFieldAccess);
Assert.assertTrue(iterator.hasNext() && iterator.next().holder().is("shakinglib.LibraryClass"));
@@ -617,7 +645,7 @@
clazz.field(signature.type, signature.name).isPresent());
}
- @Parameters(name = "dex: {0} frontend: {1} keep: {3} minify: {4}")
+ @Parameters(name = "dex:{0} mode:{1}-{2} keep:{4} minify:{5}")
public static Collection<Object[]> data() {
List<String> tests = Arrays
.asList(
@@ -837,21 +865,38 @@
return;
}
for (MinifyMode mode : MinifyMode.values()) {
- addTestCase(testCases, test, Frontend.JAR, mainClass, keepName, keepList, mode, inspection,
- outputComparator, dexComparator);
- addTestCase(testCases, test, Frontend.DEX, mainClass, keepName, keepList, mode, inspection,
- outputComparator, dexComparator);
+ addTestCase(testCases, test, Frontend.JAR, Backend.CF, mainClass, keepName, keepList, mode,
+ inspection, outputComparator, dexComparator);
+ addTestCase(testCases, test, Frontend.JAR, Backend.DEX, mainClass, keepName, keepList, mode,
+ inspection, outputComparator, dexComparator);
+ addTestCase(testCases, test, Frontend.DEX, Backend.DEX, mainClass, keepName, keepList, mode,
+ inspection, outputComparator, dexComparator);
}
}
- private static void addTestCase(List<Object[]> testCases, String test, Frontend kind,
- String mainClass, String keepName, List<String> keepList, MinifyMode minify,
+ private static void addTestCase(List<Object[]> testCases, String test, Frontend frontend,
+ Backend backend, String mainClass, String keepName, List<String> keepList, MinifyMode minify,
Consumer<DexInspector> inspection, BiConsumer<String, String> outputComparator,
BiConsumer<DexInspector, DexInspector> dexComparator) {
+ String full = test + ":" + keepName + ":" + frontend + ":" + backend + ":" + minify;
+ String anyKind = test + ":" + keepName + ":*:" + minify;
+ String anyMinify = test + ":" + keepName + ":" + frontend + ":" + backend + ":*";
if (!IGNORED_FLAGS.contains(test + ":" + keepName)
- && !IGNORED.contains(test + ":" + keepName + ":" + kind + ":" + minify)) {
- testCases.add(new Object[]{
- test, kind, mainClass, keepList, minify, inspection, outputComparator, dexComparator});
+ && !IGNORED.contains(full)
+ && !IGNORED.contains(anyKind)
+ && !IGNORED.contains(anyMinify)) {
+ testCases.add(
+ new Object[] {
+ test,
+ frontend,
+ backend,
+ mainClass,
+ keepList,
+ minify,
+ inspection,
+ outputComparator,
+ dexComparator
+ });
}
}
@@ -888,11 +933,32 @@
@Test
public void treeShakingTest() throws IOException, InterruptedException, ExecutionException {
+ if (backend == Backend.CF) {
+ Path shakinglib = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "shakinglib.jar");
+ ProcessResult resultInput =
+ ToolHelper.runJava(Arrays.asList(Paths.get(programFile), shakinglib), mainClass);
+ Assert.assertEquals(0, resultInput.exitCode);
+ ProcessResult resultOutput =
+ ToolHelper.runJava(Arrays.asList(out, shakinglib), mainClass);
+ if (outputComparator != null) {
+ outputComparator.accept(resultInput.stdout, resultOutput.stdout);
+ } else {
+ Assert.assertEquals(resultInput.toString(), resultOutput.toString());
+ }
+ if (inspection != null) {
+ DexInspector inspector =
+ new DexInspector(
+ out,
+ minify.isMinify()
+ ? proguardMap.toString()
+ : null);
+ inspection.accept(inspector);
+ }
+ return;
+ }
if (!ToolHelper.artSupported()) {
return;
}
- String out = temp.getRoot().getCanonicalPath();
- Path generated = Paths.get(out, "classes.dex");
Consumer<ArtCommandBuilder> extraArtArgs = builder -> {
builder.appendClasspath(ToolHelper.EXAMPLES_BUILD_DIR + "shakinglib/classes.dex");
};
@@ -902,19 +968,18 @@
String output1 = ToolHelper.runArtNoVerificationErrors(
Collections.singletonList(originalDex), mainClass, extraArtArgs, null);
String output2 = ToolHelper.runArtNoVerificationErrors(
- Collections.singletonList(generated.toString()), mainClass, extraArtArgs, null);
+ Collections.singletonList(out.toString()), mainClass, extraArtArgs, null);
outputComparator.accept(output1, output2);
} else {
ToolHelper.checkArtOutputIdentical(Collections.singletonList(originalDex),
- Collections.singletonList(generated.toString()), mainClass,
+ Collections.singletonList(out.toString()), mainClass,
extraArtArgs, null);
}
if (dexComparator != null) {
DexInspector ref = new DexInspector(Paths.get(originalDex));
- DexInspector inspector = new DexInspector(generated,
- minify.isMinify() ? temp.getRoot().toPath().resolve(DEFAULT_PROGUARD_MAP_FILE)
- .toString()
+ DexInspector inspector = new DexInspector(out,
+ minify.isMinify() ? proguardMap.toString()
: null);
dexComparator.accept(ref, inspector);
}
@@ -922,12 +987,12 @@
Assert.assertNull(outputComparator);
Assert.assertNull(dexComparator);
ToolHelper.runArtNoVerificationErrors(
- Collections.singletonList(generated.toString()), mainClass, extraArtArgs, null);
+ Collections.singletonList(out.toString()), mainClass, extraArtArgs, null);
}
if (inspection != null) {
- DexInspector inspector = new DexInspector(generated,
- minify.isMinify() ? temp.getRoot().toPath().resolve(DEFAULT_PROGUARD_MAP_FILE).toString()
+ DexInspector inspector = new DexInspector(out,
+ minify.isMinify() ? proguardMap.toString()
: null);
inspection.accept(inspector);
}