Refactor SmaliTestBase to go through the normal R8 entry points.
Bug:
Change-Id: Id3e8114ffc2a562790a7043ade5de05be187f449
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index a9f3329..18096f4 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -62,7 +62,6 @@
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
public class R8 {
@@ -106,51 +105,6 @@
}
}
- static DexApplication optimize(
- DexApplication application,
- AppInfoWithSubtyping appInfo,
- InternalOptions options)
- throws ApiLevelException, ExecutionException, IOException {
- return new R8(options).optimize(application, appInfo);
- }
-
- private DexApplication optimize(DexApplication application, AppInfoWithSubtyping appInfo)
- throws IOException, ApiLevelException, ExecutionException {
- return optimize(application, appInfo, GraphLense.getIdentityLense(),
- Executors.newSingleThreadExecutor());
- }
-
- private DexApplication optimize(
- DexApplication application,
- AppInfoWithSubtyping appInfo,
- GraphLense graphLense,
- ExecutorService executorService)
- throws IOException, ApiLevelException, ExecutionException {
- final CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
-
- timing.begin("Create IR");
- try {
- IRConverter converter = new IRConverter(
- appInfo, options, timing, printer, graphLense);
- application = converter.optimize(application, executorService);
- } finally {
- timing.end();
- }
-
- if (options.printCfg) {
- if (options.printCfgFile == null || options.printCfgFile.isEmpty()) {
- System.out.print(printer.toString());
- } else {
- try (OutputStreamWriter writer = new OutputStreamWriter(
- new FileOutputStream(options.printCfgFile),
- StandardCharsets.UTF_8)) {
- writer.write(printer.toString());
- }
- }
- }
- return application;
- }
-
private Set<DexType> filterMissingClasses(Set<DexType> missingClasses,
ProguardClassFilter dontWarnPatterns) {
Set<DexType> result = new HashSet<>(missingClasses);
@@ -278,7 +232,26 @@
graphLense = new BridgeMethodAnalysis(graphLense, appInfo.withSubtyping()).run();
- application = optimize(application, appInfo, graphLense, executorService);
+ timing.begin("Create IR");
+ CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
+ try {
+ IRConverter converter = new IRConverter(appInfo, options, timing, printer, graphLense);
+ application = converter.optimize(application, executorService);
+ } finally {
+ timing.end();
+ }
+
+ if (options.printCfg) {
+ if (options.printCfgFile == null || options.printCfgFile.isEmpty()) {
+ System.out.print(printer.toString());
+ } else {
+ try (OutputStreamWriter writer = new OutputStreamWriter(
+ new FileOutputStream(options.printCfgFile),
+ StandardCharsets.UTF_8)) {
+ writer.write(printer.toString());
+ }
+ }
+ }
// Overwrite SourceFile if specified. This step should be done after IR conversion.
timing.begin("Rename SourceFile");
diff --git a/src/main/java/com/android/tools/r8/graph/SmaliWriter.java b/src/main/java/com/android/tools/r8/graph/SmaliWriter.java
index 44ac9c8..7f7ec36 100644
--- a/src/main/java/com/android/tools/r8/graph/SmaliWriter.java
+++ b/src/main/java/com/android/tools/r8/graph/SmaliWriter.java
@@ -1,11 +1,15 @@
package com.android.tools.r8.graph;
+import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
+import java.util.concurrent.ExecutionException;
public class SmaliWriter extends DexByteCodeWriter {
@@ -15,12 +19,15 @@
}
/** Return smali source for the application code. */
- public static String smali(DexApplication application, InternalOptions options) {
- SmaliWriter writer = new SmaliWriter(application, options);
+ public static String smali(AndroidApp application, InternalOptions options) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
try (PrintStream ps = new PrintStream(os)) {
+ DexApplication dexApplication = new ApplicationReader(application, options,
+ new Timing("SmaliWriter"))
+ .read();
+ SmaliWriter writer = new SmaliWriter(dexApplication, options);
writer.write(ps);
- } catch (IOException e) {
+ } catch (IOException | ExecutionException e) {
throw new CompilationError("Failed to generate smali sting", e);
}
return new String(os.toByteArray(), StandardCharsets.UTF_8);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 00787ca..a9735c4 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -39,6 +39,7 @@
import com.android.tools.r8.utils.CfgPrinter;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
@@ -417,7 +418,7 @@
DexType result;
int count = 0;
do {
- String name = options.outline.className + (count == 0 ? "" : Integer.toString(count));
+ String name = OutlineOptions.CLASS_NAME + (count == 0 ? "" : Integer.toString(count));
count++;
result = appInfo.dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(name));
} while (appInfo.definitionFor(result) != null);
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 67e58cb..9709cf0 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
@@ -48,6 +48,7 @@
import com.android.tools.r8.ir.conversion.SourceCode;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.StringUtils.BraceType;
import com.google.common.collect.Sets;
@@ -1062,7 +1063,7 @@
MethodAccessFlags methodAccess =
MethodAccessFlags.fromSharedAccessFlags(
Constants.ACC_PUBLIC | Constants.ACC_STATIC, false);
- DexString methodName = dexItemFactory.createString(options.outline.methodPrefix + count);
+ DexString methodName = dexItemFactory.createString(OutlineOptions.METHOD_PREFIX + count);
DexMethod method = outline.buildMethod(type, methodName);
direct[count] = new DexEncodedMethod(method, methodAccess, DexAnnotationSet.empty(),
DexAnnotationSetRefList.empty(), new OutlineCode(outline));
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNaming.java b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
index c494c08..36ea1b3 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
@@ -17,6 +17,7 @@
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import org.objectweb.asm.Type;
@@ -291,6 +292,12 @@
this.parameters = parameters;
}
+ public MethodSignature(String name, String type, Collection<String> parameters) {
+ super(name);
+ this.type = type;
+ this.parameters = parameters.toArray(new String[parameters.size()]);
+ }
+
public static MethodSignature fromDexMethod(DexMethod method) {
String[] paramNames = new String[method.getArity()];
DexType[] values = method.proto.parameters.values;
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index 67aa288..ddac51f 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -608,7 +608,7 @@
* Set dead-code data.
*/
public Builder setDeadCode(byte[] content) {
- deadCode = content == null ? null : Resource.fromBytes(null, content);
+ deadCode = content == null ? null : Resource.fromBytes(Origin.unknown(), content);
return this;
}
@@ -631,7 +631,7 @@
* Set proguard-map data.
*/
public Builder setProguardMapData(byte[] content) {
- proguardMap = content == null ? null : Resource.fromBytes(null, content);
+ proguardMap = content == null ? null : Resource.fromBytes(Origin.unknown(), content);
return this;
}
@@ -639,7 +639,7 @@
* Set proguard-seeds data.
*/
public Builder setProguardSeedsData(byte[] content) {
- proguardSeeds = content == null ? null : Resource.fromBytes(null, content);
+ proguardSeeds = content == null ? null : Resource.fromBytes(Origin.unknown(), content);
return this;
}
@@ -685,7 +685,7 @@
* Set the main-dex list output data.
*/
public Builder setMainDexListOutputData(byte[] content) {
- mainDexListOutput = content == null ? null : Resource.fromBytes(null, content);
+ mainDexListOutput = content == null ? null : Resource.fromBytes(Origin.unknown(), content);
return this;
}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index d7006af..62dac5f 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -267,9 +267,10 @@
public static class OutlineOptions {
+ public static final String CLASS_NAME = "r8.GeneratedOutlineSupport";
+ public static final String METHOD_PREFIX = "outline";
+
public boolean enabled = true;
- public static final String className = "r8.GeneratedOutlineSupport";
- public String methodPrefix = "outline";
public int minSize = 3;
public int maxSize = 99;
public int threshold = 20;
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 2128663..60bcc5b 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -5,18 +5,20 @@
package com.android.tools.r8;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.SmaliWriter;
import com.android.tools.r8.shaking.FilteredClassPath;
import com.android.tools.r8.shaking.ProguardRuleParserException;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OutputMode;
-import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.io.ByteStreams;
@@ -368,8 +370,34 @@
*/
protected void disassemble(AndroidApp app) throws Exception {
InternalOptions options = new InternalOptions();
- DexApplication dexApplication = new ApplicationReader(app, options, new Timing("XX")).read();
- System.out.println(SmaliWriter.smali(dexApplication, options));
+ System.out.println(SmaliWriter.smali(app, options));
+ }
+
+ protected DexEncodedMethod getMethod(
+ DexInspector inspector,
+ String className,
+ String returnType,
+ String methodName,
+ List<String> parameters) {
+ ClassSubject clazz = inspector.clazz(className);
+ assertTrue(clazz.isPresent());
+ MethodSubject method = clazz.method(returnType, methodName, parameters);
+ assertTrue(method.isPresent());
+ return method.getMethod();
+ }
+
+ protected DexEncodedMethod getMethod(
+ AndroidApp application,
+ String className,
+ String returnType,
+ String methodName,
+ List<String> parameters) {
+ try {
+ DexInspector inspector = new DexInspector(application);
+ return getMethod(inspector, className, returnType, methodName, parameters);
+ } catch (Exception e) {
+ return null;
+ }
}
public enum MinifyMode {
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 2c4bc98..34230de 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.DeviceRunner.DeviceRunnerConfigurationException;
import com.android.tools.r8.ToolHelper.DexVm.Kind;
import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.shaking.FilteredClassPath;
@@ -697,14 +696,6 @@
return compatSink.build();
}
- public static DexApplication optimizeWithR8(
- DexApplication application,
- InternalOptions options)
- throws CompilationException, ExecutionException, IOException {
- application = application.toDirect();
- return R8.optimize(application, new AppInfoWithSubtyping(application), options);
- }
-
public static AndroidApp runD8(AndroidApp app) throws CompilationException, IOException {
return runD8(app, null);
}
diff --git a/src/test/java/com/android/tools/r8/bisect/BisectTest.java b/src/test/java/com/android/tools/r8/bisect/BisectTest.java
index ba49e64..fc6e4c1 100644
--- a/src/test/java/com/android/tools/r8/bisect/BisectTest.java
+++ b/src/test/java/com/android/tools/r8/bisect/BisectTest.java
@@ -10,8 +10,8 @@
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.smali.SmaliTestBase.MethodSignature;
-import com.android.tools.r8.smali.SmaliTestBase.SmaliBuilder;
+import com.android.tools.r8.smali.SmaliBuilder;
+import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.InternalOptions;
diff --git a/src/test/java/com/android/tools/r8/debug/SmaliDebugTest.java b/src/test/java/com/android/tools/r8/debug/SmaliDebugTest.java
index 66652bd..7a911be 100644
--- a/src/test/java/com/android/tools/r8/debug/SmaliDebugTest.java
+++ b/src/test/java/com/android/tools/r8/debug/SmaliDebugTest.java
@@ -12,15 +12,13 @@
import com.android.tools.r8.debuginfo.DebugInfoInspector;
import com.android.tools.r8.graph.DexDebugEntry;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.smali.SmaliTestBase.SmaliBuilder;
+import com.android.tools.r8.smali.SmaliBuilder;
import com.android.tools.r8.utils.AndroidApp;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import org.apache.harmony.jpda.tests.framework.jdwp.Value;
-import org.junit.Assume;
-import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
diff --git a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
index b344c8c..8d93b31 100644
--- a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
@@ -10,9 +10,10 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.smali.SmaliBuilder;
import com.android.tools.r8.smali.SmaliTestBase;
-import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
import com.google.common.collect.ImmutableList;
import org.junit.Test;
@@ -62,10 +63,11 @@
" goto :return"
);
- DexApplication application = buildApplication(builder);
- AppInfo appInfo = new AppInfo(application);
- DexEncodedMethod method =
- getMethod(application, DEFAULT_CLASS_NAME, "int", "x", ImmutableList.of());
+ AndroidApp application = buildApplication(builder);
+ AppInfo appInfo = getAppInfo(application);
+ DexInspector inspector = new DexInspector(appInfo.app);
+ DexEncodedMethod method = getMethod(inspector, DEFAULT_CLASS_NAME, "int", "x",
+ ImmutableList.of());
assertNull(appInfo.lookupVirtualTarget(method.method.holder, method.method));
assertNull(appInfo.lookupDirectTarget(method.method));
assertNotNull(appInfo.lookupStaticTarget(method.method));
@@ -74,12 +76,12 @@
// Dalvik rejects at verification time instead of producing the
// expected IncompatibleClassChangeError.
try {
- runArt(application, new InternalOptions());
+ runArt(application);
} catch (AssertionError e) {
assert e.toString().contains("VerifyError");
}
} else {
- assertEquals("OK", runArt(application, new InternalOptions()));
+ assertEquals("OK", runArt(application));
}
}
@@ -131,20 +133,21 @@
" goto :return"
);
- DexApplication application = buildApplication(builder);
- AppInfo appInfo = new AppInfo(application);
+ AndroidApp application = buildApplication(builder);
+ AppInfo appInfo = getAppInfo(application);
+ DexInspector inspector = new DexInspector(appInfo.app);
DexMethod methodXOnTestSuper =
- getMethod(application, "TestSuper", "int", "x", ImmutableList.of()).method;
+ getMethod(inspector, "TestSuper", "int", "x", ImmutableList.of()).method;
DexMethod methodYOnTest =
- getMethod(application, "Test", "int", "y", ImmutableList.of()).method;
+ getMethod(inspector, "Test", "int", "y", ImmutableList.of()).method;
DexType classTestSuper = methodXOnTestSuper.getHolder();
DexType classTest = methodYOnTest.getHolder();
DexProto methodXProto = methodXOnTestSuper.proto;
DexString methodXName = methodXOnTestSuper.name;
DexMethod methodXOnTest =
- application.dexItemFactory.createMethod(classTest, methodXProto, methodXName);
+ appInfo.dexItemFactory.createMethod(classTest, methodXProto, methodXName);
assertNull(appInfo.lookupVirtualTarget(classTestSuper, methodXOnTestSuper));
assertNull(appInfo.lookupVirtualTarget(classTest, methodXOnTestSuper));
@@ -156,7 +159,7 @@
assertNotNull(appInfo.lookupStaticTarget(methodXOnTestSuper));
assertNotNull(appInfo.lookupStaticTarget(methodXOnTest));
- assertEquals("OK", runArt(application, new InternalOptions()));
+ assertEquals("OK", runArt(application));
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java b/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java
index ad9a26a..2d73d1f 100644
--- a/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java
+++ b/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java
@@ -4,13 +4,15 @@
package com.android.tools.r8.ir;
-import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.ValueNumberGenerator;
+import com.android.tools.r8.smali.SmaliBuilder;
+import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
import com.android.tools.r8.smali.SmaliTestBase;
+import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableList;
import java.util.List;
@@ -45,8 +47,7 @@
" return p0"
);
- InternalOptions options = new InternalOptions();
- DexApplication application = buildApplication(builder, options);
+ AndroidApp application = buildApplication(builder);
// Build the code, and split the code into three blocks.
ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
diff --git a/src/test/java/com/android/tools/r8/ir/InlineTest.java b/src/test/java/com/android/tools/r8/ir/InlineTest.java
index 27a3ce9..b190933 100644
--- a/src/test/java/com/android/tools/r8/ir/InlineTest.java
+++ b/src/test/java/com/android/tools/r8/ir/InlineTest.java
@@ -13,7 +13,8 @@
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.ValueNumberGenerator;
-import com.android.tools.r8.smali.SmaliTestBase;
+import com.android.tools.r8.smali.SmaliBuilder;
+import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
@@ -22,7 +23,7 @@
import java.util.ListIterator;
import org.junit.Test;
-public class InlineTest extends SmaliTestBase {
+public class InlineTest extends IrInjectionTestBase {
TestApplication codeForMethodReplaceTest(int a, int b) throws Exception {
SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
@@ -84,7 +85,7 @@
DexEncodedMethod methodB = getMethod(application, signatureB);
IRCode codeB = methodB.buildIR(new InternalOptions(), valueNumberGenerator);
- return new SmaliTestBase.TestApplication(application, method, code,
+ return new TestApplication(application, method, code,
ImmutableList.of(codeA, codeB), valueNumberGenerator, options);
}
diff --git a/src/test/java/com/android/tools/r8/ir/InstructionIteratorTest.java b/src/test/java/com/android/tools/r8/ir/InstructionIteratorTest.java
index a688ffe..5d3b0fb 100644
--- a/src/test/java/com/android/tools/r8/ir/InstructionIteratorTest.java
+++ b/src/test/java/com/android/tools/r8/ir/InstructionIteratorTest.java
@@ -4,14 +4,16 @@
package com.android.tools.r8.ir;
-import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.ValueNumberGenerator;
+import com.android.tools.r8.smali.SmaliBuilder;
+import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
import com.android.tools.r8.smali.SmaliTestBase;
+import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableList;
import java.util.List;
@@ -46,8 +48,7 @@
" return p0"
);
- InternalOptions options = new InternalOptions();
- DexApplication application = buildApplication(builder, options);
+ AndroidApp application = buildApplication(builder);
// Build the code, and split the code into three blocks.
ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
diff --git a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
new file mode 100644
index 0000000..9f3e0ac
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
@@ -0,0 +1,146 @@
+// 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.ir;
+
+import com.android.tools.r8.R8;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.errors.DexOverflowException;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.ValueNumberGenerator;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.smali.SmaliBuilder;
+import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
+import com.android.tools.r8.smali.SmaliTestBase;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.AndroidAppOutputSink;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
+import java.io.IOException;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import org.antlr.runtime.RecognitionException;
+
+public class IrInjectionTestBase extends SmaliTestBase {
+
+ protected DexApplication buildApplication(SmaliBuilder builder, InternalOptions options) {
+ try {
+ return buildApplication(AndroidApp.fromDexProgramData(builder.compile()), options);
+ } catch (IOException | RecognitionException | ExecutionException | DexOverflowException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected DexApplication buildApplication(AndroidApp input, InternalOptions options) {
+ try {
+ options.itemFactory.resetSortedIndices();
+ return new ApplicationReader(input, options, new Timing("IrInjectionTest")).read();
+ } catch (IOException | ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected DexEncodedMethod getMethod(DexApplication application, MethodSignature signature) {
+ return getMethod(application,
+ signature.clazz, signature.returnType, signature.name, signature.parameterTypes);
+ }
+
+ protected DexEncodedMethod getMethod(
+ DexApplication application,
+ String className,
+ String returnType,
+ String methodName,
+ List<String> parameters) {
+ DexInspector inspector = new DexInspector(application);
+ return getMethod(inspector, className, returnType, methodName, parameters);
+ }
+
+ public class TestApplication {
+
+ public final DexApplication application;
+ public final DexEncodedMethod method;
+ public final IRCode code;
+ public final List<IRCode> additionalCode;
+ public final ValueNumberGenerator valueNumberGenerator;
+ public final InternalOptions options;
+
+ public TestApplication(
+ DexApplication application,
+ DexEncodedMethod method,
+ IRCode code,
+ ValueNumberGenerator valueNumberGenerator,
+ InternalOptions options) {
+ this(application, method, code, null, valueNumberGenerator, options);
+ }
+
+ public TestApplication(
+ DexApplication application,
+ DexEncodedMethod method,
+ IRCode code,
+ List<IRCode> additionalCode,
+ ValueNumberGenerator valueNumberGenerator,
+ InternalOptions options) {
+ this.application = application;
+ this.method = method;
+ this.code = code;
+ this.additionalCode = additionalCode;
+ this.valueNumberGenerator = valueNumberGenerator;
+ this.options = options;
+ }
+
+ public int countArgumentInstructions() {
+ int count = 0;
+ ListIterator<Instruction> iterator = code.blocks.get(0).listIterator();
+ while (iterator.next().isArgument()) {
+ count++;
+ }
+ return count;
+ }
+
+ public InstructionListIterator listIteratorAt(BasicBlock block, int index) {
+ InstructionListIterator iterator = block.listIterator();
+ for (int i = 0; i < index; i++) {
+ iterator.next();
+ }
+ return iterator;
+ }
+
+ private AndroidApp writeDex(DexApplication application, InternalOptions options)
+ throws DexOverflowException {
+ try {
+ AndroidAppOutputSink compatSink = new AndroidAppOutputSink();
+ R8.writeApplication(
+ Executors.newSingleThreadExecutor(),
+ application,
+ compatSink,
+ null,
+ NamingLens.getIdentityLens(),
+ null,
+ options);
+ compatSink.close();
+ return compatSink.build();
+ } catch (ExecutionException | IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public String run() throws DexOverflowException, IOException {
+ AppInfo appInfo = new AppInfo(application);
+ IRConverter converter = new IRConverter(appInfo, options);
+ converter.replaceCodeForTesting(method, code);
+ AndroidApp app = writeDex(application, options);
+ return runOnArtRaw(app, DEFAULT_MAIN_CLASS_NAME).stdout;
+ }
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
index 5165432..25a237d 100644
--- a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
+++ b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
@@ -21,14 +21,15 @@
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.smali.SmaliTestBase;
+import com.android.tools.r8.smali.SmaliBuilder;
+import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
-public class SplitBlockTest extends SmaliTestBase {
+public class SplitBlockTest extends IrInjectionTestBase {
TestApplication codeWithoutCatchHandlers() throws Exception {
SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/Regress68656641.java b/src/test/java/com/android/tools/r8/ir/regalloc/Regress68656641.java
index 1d17e31..74a91a8 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/Regress68656641.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/Regress68656641.java
@@ -3,13 +3,15 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.regalloc;
-import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.ir.code.IRCode;
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.smali.SmaliBuilder;
+import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
import com.android.tools.r8.smali.SmaliTestBase;
+import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableList;
import java.util.PriorityQueue;
@@ -43,7 +45,7 @@
ImmutableList.of(),
1,
" return-void");
- DexApplication application = buildApplication(builder, options);
+ AndroidApp application = buildApplication(builder);
// Build the code, and split the code into three blocks.
ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
DexEncodedMethod method = getMethod(application, signature);
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
index 7421046..5579380 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
@@ -4,18 +4,15 @@
package com.android.tools.r8.jasmin;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import com.android.tools.r8.Resource;
+import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OutputMode;
import com.android.tools.r8.utils.StringUtils;
@@ -33,13 +30,8 @@
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
-import org.junit.Rule;
-import org.junit.rules.TemporaryFolder;
-public class JasminTestBase {
-
- @Rule
- public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+public class JasminTestBase extends TestBase {
public static String getPathFromDescriptor(String classDescriptor) {
assert classDescriptor.startsWith("L");
@@ -95,6 +87,10 @@
return runOnArt(compileWithD8(builder), main);
}
+ protected AndroidApp compileWithR8(JasminBuilder builder) throws Exception {
+ return compileWithR8(builder, null);
+ }
+
protected AndroidApp compileWithR8(JasminBuilder builder,
Consumer<InternalOptions> optionsConsumer)
throws Exception {
@@ -169,20 +165,6 @@
protected DexEncodedMethod getMethod(AndroidApp application, String clazz,
MethodSignature signature) {
return getMethod(application,
- clazz, signature.type, signature.name, signature.parameters);
- }
-
- protected DexEncodedMethod getMethod(AndroidApp application, String className,
- String returnType, String methodName, String[] parameters) {
- try {
- DexInspector inspector = new DexInspector(application);
- ClassSubject clazz = inspector.clazz(className);
- assertTrue(clazz.isPresent());
- MethodSubject method = clazz.method(returnType, methodName, Arrays.asList(parameters));
- assertTrue(method.isPresent());
- return method.getMethod();
- } catch (Exception e) {
- return null;
- }
+ clazz, signature.type, signature.name, Arrays.asList(signature.parameters));
}
}
diff --git a/src/test/java/com/android/tools/r8/naming/RenameSourceFileSmaliTest.java b/src/test/java/com/android/tools/r8/naming/RenameSourceFileSmaliTest.java
index acb0a51..de654b8 100644
--- a/src/test/java/com/android/tools/r8/naming/RenameSourceFileSmaliTest.java
+++ b/src/test/java/com/android/tools/r8/naming/RenameSourceFileSmaliTest.java
@@ -17,6 +17,8 @@
import com.android.tools.r8.graph.DexDebugEvent.SetFile;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.shaking.ProguardConfiguration;
+import com.android.tools.r8.smali.SmaliBuilder;
+import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
import com.android.tools.r8.smali.SmaliTestBase;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.StringUtils;
diff --git a/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java b/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java
index 4d1d619..51772fe 100644
--- a/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.code.Instruction;
import com.android.tools.r8.code.Sput;
import com.android.tools.r8.code.SputObject;
-import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueBoolean;
@@ -23,11 +22,11 @@
import com.android.tools.r8.graph.DexValue.DexValueLong;
import com.android.tools.r8.graph.DexValue.DexValueShort;
import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.android.tools.r8.smali.SmaliBuilder;
import com.android.tools.r8.smali.SmaliTestBase;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.DexInspector.MethodSubject;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.StringUtils;
import org.junit.Test;
@@ -92,9 +91,8 @@
"return-void"
);
- InternalOptions options = new InternalOptions();
- DexApplication originalApplication = buildApplication(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
+ AndroidApp originalApplication = buildApplication(builder);
+ AndroidApp processedApplication = processApplication(originalApplication);
DexInspector inspector = new DexInspector(processedApplication);
// Test is running without tree-shaking, so the empty <clinit> is not removed.
@@ -147,7 +145,7 @@
assertTrue(value instanceof DexValueString);
assertEquals(("8"), ((DexValueString) value).getValue().toString());
- String result = runArt(processedApplication, options);
+ String result = runArt(processedApplication);
assertEquals(StringUtils.lines("true", "1", "2", "3", "4", "5.0", "6.0", "7", "8"), result);
}
@@ -177,16 +175,15 @@
"return-void"
);
- InternalOptions options = new InternalOptions();
- DexApplication originalApplication = buildApplication(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
+ AndroidApp originalApplication = buildApplication(builder);
+ AndroidApp processedApplication = processApplication(originalApplication);
DexInspector inspector = new DexInspector(processedApplication);
MethodSubject clinit = inspector.clazz("Test").clinit();
// Nothing changed in the class initializer.
assertEquals(5, clinit.getMethod().getCode().asDexCode().instructions.length);
- String result = runArt(processedApplication, options);
+ String result = runArt(processedApplication);
assertEquals(StringUtils.lines("0", "1"), result);
}
@@ -219,16 +216,15 @@
"return-void"
);
- InternalOptions options = new InternalOptions();
- DexApplication originalApplication = buildApplication(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
+ AndroidApp originalApplication = buildApplication(builder);
+ AndroidApp processedApplication = processApplication(originalApplication);
DexInspector inspector = new DexInspector(processedApplication);
// Test is running without tree-shaking, so the empty <clinit> is not removed.
assertTrue(
inspector.clazz("Test").clinit().getMethod().getCode().asDexCode().isEmptyVoidMethod());
- String result = runArt(processedApplication, options);
+ String result = runArt(processedApplication);
assertEquals(StringUtils.lines("null", "null", "null"), result);
}
@@ -262,16 +258,15 @@
"return-void"
);
- InternalOptions options = new InternalOptions();
- DexApplication originalApplication = buildApplication(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
+ AndroidApp originalApplication = buildApplication(builder);
+ AndroidApp processedApplication = processApplication(originalApplication);
DexInspector inspector = new DexInspector(processedApplication);
// Test is running without tree-shaking, so the empty <clinit> is not removed.
assertTrue(
inspector.clazz("Test").clinit().getMethod().getCode().asDexCode().isEmptyVoidMethod());
- String result = runArt(processedApplication, options);
+ String result = runArt(processedApplication);
assertEquals(StringUtils.lines("Value1", "Value2", "Value2"), result);
}
@@ -313,9 +308,8 @@
"return-void"
);
- InternalOptions options = new InternalOptions();
- DexApplication originalApplication = buildApplication(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
+ AndroidApp originalApplication = buildApplication(builder);
+ AndroidApp processedApplication = processApplication(originalApplication);
DexInspector inspector = new DexInspector(processedApplication);
// Test is running without tree-shaking, so the empty <clinit> is not removed.
@@ -333,7 +327,7 @@
assertTrue(value instanceof DexValueString);
assertEquals(("7"), ((DexValueString) value).getValue().toString());
- String result = runArt(processedApplication, options);
+ String result = runArt(processedApplication);
assertEquals(StringUtils.lines("3", "7") , result);
}
@@ -387,9 +381,8 @@
"return-void"
);
- InternalOptions options = new InternalOptions();
- DexApplication originalApplication = buildApplication(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
+ AndroidApp originalApplication = buildApplication(builder);
+ AndroidApp processedApplication = processApplication(originalApplication);
DexInspector inspector = new DexInspector(processedApplication);
assertTrue(inspector.clazz("Test").clinit().isPresent());
@@ -417,7 +410,7 @@
}
}
- String result = runArt(processedApplication, options);
+ String result = runArt(processedApplication);
assertEquals(StringUtils.lines("3", "7"), result);
}
@@ -475,9 +468,8 @@
"return-void"
);
- InternalOptions options = new InternalOptions();
- DexApplication originalApplication = buildApplication(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
+ AndroidApp originalApplication = buildApplication(builder);
+ AndroidApp processedApplication = processApplication(originalApplication);
DexInspector inspector = new DexInspector(processedApplication);
assertTrue(inspector.clazz(className).isPresent());
@@ -485,7 +477,7 @@
assertTrue(
inspector.clazz(className).clinit().getMethod().getCode().asDexCode().isEmptyVoidMethod());
- String result = runArt(processedApplication, options, className);
+ String result = runArt(processedApplication, className);
assertEquals(StringUtils.lines("Test", className, "Test", className, "Test", className),
result);
@@ -525,15 +517,14 @@
builder.addClass("org.example.Test2");
- InternalOptions options = new InternalOptions();
- DexApplication originalApplication = buildApplication(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
+ AndroidApp originalApplication = buildApplication(builder);
+ AndroidApp processedApplication = processApplication(originalApplication);
DexInspector inspector = new DexInspector(processedApplication);
assertTrue(inspector.clazz(className).isPresent());
assertTrue(inspector.clazz(className).clinit().isPresent());
- String result = runArt(processedApplication, options, className);
+ String result = runArt(processedApplication, className);
assertEquals(StringUtils.lines("Test2", "org.example.Test2"), result);
}
@@ -559,16 +550,15 @@
builder.addClass("Other");
builder.addStaticField("field", "I", "1");
- InternalOptions options = new InternalOptions();
- DexApplication originalApplication = buildApplication(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
+ AndroidApp originalApplication = buildApplication(builder);
+ AndroidApp processedApplication = processApplication(originalApplication);
DexInspector inspector = new DexInspector(processedApplication);
MethodSubject clinit = inspector.clazz("Test").clinit();
// Nothing changed in the class initializer.
assertEquals(3, clinit.getMethod().getCode().asDexCode().instructions.length);
- String result = runArt(processedApplication, options);
+ String result = runArt(processedApplication);
assertEquals(StringUtils.lines("2"), result);
}
diff --git a/src/test/java/com/android/tools/r8/rewrite/staticvalues/inlibraries/StaticLibraryValuesChangeTest.java b/src/test/java/com/android/tools/r8/rewrite/staticvalues/inlibraries/StaticLibraryValuesChangeTest.java
index d1a2514..76c24f9 100644
--- a/src/test/java/com/android/tools/r8/rewrite/staticvalues/inlibraries/StaticLibraryValuesChangeTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/staticvalues/inlibraries/StaticLibraryValuesChangeTest.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.shaking.FilteredClassPath;
-import com.android.tools.r8.smali.SmaliTestBase.SmaliBuilder;
+import com.android.tools.r8.smali.SmaliBuilder;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.PreloadedClassFileProvider;
import com.google.common.collect.ImmutableList;
diff --git a/src/test/java/com/android/tools/r8/smali/CheckSwitchInTestClass.java b/src/test/java/com/android/tools/r8/rewrite/switches/CheckSwitchInTestClass.java
similarity index 96%
rename from src/test/java/com/android/tools/r8/smali/CheckSwitchInTestClass.java
rename to src/test/java/com/android/tools/r8/rewrite/switches/CheckSwitchInTestClass.java
index f47025a..93b7c19 100644
--- a/src/test/java/com/android/tools/r8/smali/CheckSwitchInTestClass.java
+++ b/src/test/java/com/android/tools/r8/rewrite/switches/CheckSwitchInTestClass.java
@@ -2,7 +2,7 @@
// 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.smali;
+package com.android.tools.r8.rewrite.switches;
import java.lang.reflect.Method;
import java.util.ArrayList;
diff --git a/src/test/java/com/android/tools/r8/rewrite/switches/SwitchRewritingJarTest.java b/src/test/java/com/android/tools/r8/rewrite/switches/SwitchRewritingJarTest.java
new file mode 100644
index 0000000..66cf986
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/switches/SwitchRewritingJarTest.java
@@ -0,0 +1,344 @@
+// 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.rewrite.switches;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.code.IfEq;
+import com.android.tools.r8.code.IfEqz;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.code.PackedSwitch;
+import com.android.tools.r8.code.SparseSwitch;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.jasmin.JasminBuilder;
+import com.android.tools.r8.jasmin.JasminTestBase;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.shaking.FilteredClassPath;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Test;
+
+public class SwitchRewritingJarTest extends JasminTestBase {
+
+ private void runSingleCaseJarTest(boolean packed, int key) throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ String switchCode;
+ if (packed) {
+ switchCode = StringUtils.join(
+ "\n",
+ " tableswitch " + key,
+ " case_0",
+ " default : case_default");
+ } else {
+ switchCode = StringUtils.join(
+ "\n",
+ " lookupswitch",
+ " " + key + " : case_0",
+ " default : case_default");
+ }
+
+ clazz.addStaticMethod("test", ImmutableList.of("I"), "I",
+ " .limit stack 1",
+ " .limit locals 1",
+ " iload 0",
+ switchCode,
+ " case_0:",
+ " iconst_3",
+ " goto return_",
+ " case_default:",
+ " ldc 5",
+ " return_:",
+ " ireturn");
+
+ clazz.addMainMethod(
+ " .limit stack 2",
+ " .limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc 2",
+ " invokestatic Test/test(I)I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " return");
+
+ AndroidApp app = builder.build();
+ app = ToolHelper.runR8(app);
+
+ MethodSignature signature = new MethodSignature("test", "int", ImmutableList.of("int"));
+ DexEncodedMethod method = getMethod(app, "Test", signature);
+ DexCode code = method.getCode().asDexCode();
+ if (key == 0) {
+ assertEquals(5, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof IfEqz);
+ } else {
+ assertEquals(6, code.instructions.length);
+ assertTrue(code.instructions[1] instanceof IfEq);
+ }
+ }
+
+ @Test
+ public void singleCaseJar() throws Exception {
+ for (boolean packed : new boolean[]{true, false}) {
+ runSingleCaseJarTest(packed, Integer.MIN_VALUE);
+ runSingleCaseJarTest(packed, -1);
+ runSingleCaseJarTest(packed, 0);
+ runSingleCaseJarTest(packed, 1);
+ runSingleCaseJarTest(packed, Integer.MAX_VALUE);
+ }
+ }
+
+ private void runTwoCaseSparseToPackedJarTest(int key1, int key2) throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ clazz.addStaticMethod("test", ImmutableList.of("I"), "I",
+ " .limit stack 1",
+ " .limit locals 1",
+ " iload 0",
+ " lookupswitch",
+ " " + key1 + " : case_1",
+ " " + key2 + " : case_2",
+ " default : case_default",
+ " case_1:",
+ " iconst_3",
+ " goto return_",
+ " case_2:",
+ " iconst_4",
+ " goto return_",
+ " case_default:",
+ " iconst_5",
+ " return_:",
+ " ireturn");
+
+ clazz.addMainMethod(
+ " .limit stack 2",
+ " .limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc 2",
+ " invokestatic Test/test(I)I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " return");
+
+ AndroidApp app = compileWithR8(builder);
+
+ MethodSignature signature = new MethodSignature("test", "int", ImmutableList.of("int"));
+ DexEncodedMethod method = getMethod(app, "Test", signature);
+ DexCode code = method.getCode().asDexCode();
+ if (SwitchRewritingTest.twoCaseWillUsePackedSwitch(key1, key2)) {
+ assertTrue(code.instructions[0] instanceof PackedSwitch);
+ } else {
+ if (key1 == 0) {
+ assertTrue(code.instructions[0] instanceof IfEqz);
+ } else {
+ // Const instruction before if.
+ assertTrue(code.instructions[1] instanceof IfEq);
+ }
+ }
+ }
+
+ @Test
+ public void twoCaseSparseToPackedJar() throws Exception {
+ for (int delta = 1; delta <= 3; delta++) {
+ runTwoCaseSparseToPackedJarTest(0, delta);
+ runTwoCaseSparseToPackedJarTest(-delta, 0);
+ runTwoCaseSparseToPackedJarTest(Integer.MIN_VALUE, Integer.MIN_VALUE + delta);
+ runTwoCaseSparseToPackedJarTest(Integer.MAX_VALUE - delta, Integer.MAX_VALUE);
+ }
+ runTwoCaseSparseToPackedJarTest(-1, 1);
+ runTwoCaseSparseToPackedJarTest(-2, 1);
+ runTwoCaseSparseToPackedJarTest(-1, 2);
+ runTwoCaseSparseToPackedJarTest(Integer.MIN_VALUE, Integer.MAX_VALUE);
+ runTwoCaseSparseToPackedJarTest(Integer.MIN_VALUE + 1, Integer.MAX_VALUE);
+ runTwoCaseSparseToPackedJarTest(Integer.MIN_VALUE, Integer.MAX_VALUE - 1);
+ }
+
+ private void runLargerSwitchJarTest(int firstKey, int keyStep, int totalKeys,
+ Integer additionalLastKey) throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ StringBuilder switchSource = new StringBuilder();
+ StringBuilder targetCode = new StringBuilder();
+ for (int i = 0; i < totalKeys; i++) {
+ String caseLabel = "case_" + i;
+ switchSource.append(" " + (firstKey + i * keyStep) + " : " + caseLabel + "\n");
+ targetCode.append(" " + caseLabel + ":\n");
+ targetCode.append(" ldc " + i + "\n");
+ targetCode.append(" goto return_\n");
+ }
+ if (additionalLastKey != null) {
+ String caseLabel = "case_" + totalKeys;
+ switchSource.append(" " + additionalLastKey + " : " + caseLabel + "\n");
+ targetCode.append(" " + caseLabel + ":\n");
+ targetCode.append(" ldc " + totalKeys + "\n");
+ targetCode.append(" goto return_\n");
+ }
+
+ clazz.addStaticMethod("test", ImmutableList.of("I"), "I",
+ " .limit stack 1",
+ " .limit locals 1",
+ " iload 0",
+ " lookupswitch",
+ switchSource.toString(),
+ " default : case_default",
+ targetCode.toString(),
+ " case_default:",
+ " iconst_5",
+ " return_:",
+ " ireturn");
+
+ clazz.addMainMethod(
+ " .limit stack 2",
+ " .limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc 2",
+ " invokestatic Test/test(I)I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " return");
+
+ AndroidApp app = compileWithR8(builder);
+
+ MethodSignature signature = new MethodSignature("test", "int", ImmutableList.of("int"));
+ DexEncodedMethod method = getMethod(app, "Test", signature);
+ DexCode code = method.getCode().asDexCode();
+ int packedSwitchCount = 0;
+ int sparseSwitchCount = 0;
+ for (Instruction instruction : code.instructions) {
+ if (instruction instanceof PackedSwitch) {
+ packedSwitchCount++;
+ }
+ if (instruction instanceof SparseSwitch) {
+ sparseSwitchCount++;
+ }
+ }
+ if (keyStep <= 2) {
+ assertEquals(1, packedSwitchCount);
+ assertEquals(0, sparseSwitchCount);
+ } else {
+ assertEquals(0, packedSwitchCount);
+ assertEquals(1, sparseSwitchCount);
+ }
+ }
+
+ @Test
+ public void largerSwitchJar() throws Exception {
+ runLargerSwitchJarTest(0, 1, 100, null);
+ runLargerSwitchJarTest(0, 2, 100, null);
+ runLargerSwitchJarTest(0, 3, 100, null);
+ runLargerSwitchJarTest(100, 100, 100, null);
+ runLargerSwitchJarTest(-10000, 100, 100, null);
+ runLargerSwitchJarTest(-10000, 200, 100, 10000);
+ runLargerSwitchJarTest(
+ Integer.MIN_VALUE, (int) ((-(long) Integer.MIN_VALUE) / 16), 32, Integer.MAX_VALUE);
+
+ // This is the maximal value possible with Jasmin with the generated code above. It depends on
+ // the source, so making smaller source can raise this limit. However we never get close to the
+ // class file max.
+ runLargerSwitchJarTest(0, 1, 5503, null);
+ }
+
+ private void runConvertCasesToIf(List<Integer> keys, int defaultValue, int expectedIfs,
+ int expectedPackedSwitches, int expectedSparceSwitches) throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ StringBuilder x = new StringBuilder();
+ StringBuilder y = new StringBuilder();
+ for (Integer key : keys) {
+ x.append(key).append(" : case_").append(key).append("\n");
+ y.append("case_").append(key).append(":\n");
+ y.append(" ldc ").append(key).append("\n");
+ y.append(" goto return_\n");
+ }
+
+ clazz.addStaticMethod("test", ImmutableList.of("I"), "I",
+ " .limit stack 1",
+ " .limit locals 1",
+ " iload_0",
+ " lookupswitch",
+ x.toString(),
+ " default : case_default",
+ y.toString(),
+ " case_default:",
+ " ldc " + defaultValue,
+ " return_:",
+ " ireturn");
+
+ // Add the Jasmin class and a class from Java source with the main method.
+ AndroidApp.Builder appBuilder = AndroidApp.builder();
+ appBuilder.addClassProgramData(builder.buildClasses());
+ appBuilder.addProgramFiles(FilteredClassPath
+ .unfiltered(ToolHelper.getClassFileForTestClass(CheckSwitchInTestClass.class)));
+ AndroidApp app = compileWithR8(appBuilder.build());
+
+ DexInspector inspector = new DexInspector(app);
+ MethodSubject method = inspector.clazz("Test").method("int", "test", ImmutableList.of("int"));
+ DexCode code = method.getMethod().getCode().asDexCode();
+
+ int packedSwitches = 0;
+ int sparseSwitches = 0;
+ int ifs = 0;
+ for (Instruction instruction : code.instructions) {
+ if (instruction instanceof PackedSwitch) {
+ packedSwitches++;
+ }
+ if (instruction instanceof SparseSwitch) {
+ sparseSwitches++;
+ }
+ if (instruction instanceof IfEq || instruction instanceof IfEqz) {
+ ifs++;
+ }
+ }
+
+ assertEquals(expectedPackedSwitches, packedSwitches);
+ assertEquals(expectedSparceSwitches, sparseSwitches);
+ assertEquals(expectedIfs, ifs);
+
+ // Run the code
+ List<String> args = keys.stream().map(Object::toString).collect(Collectors.toList());
+ args.add(Integer.toString(defaultValue));
+ runOnArt(app, CheckSwitchInTestClass.class, args);
+ }
+
+ @Test
+ public void convertCasesToIf() throws Exception {
+ // Switches that are completely converted to ifs.
+ runConvertCasesToIf(ImmutableList.of(0, 1000), -100, 2, 0, 0);
+ runConvertCasesToIf(ImmutableList.of(0, 1000, 2000), -100, 3, 0, 0);
+ runConvertCasesToIf(ImmutableList.of(0, 1000, 2000, 3000), -100, 4, 0, 0);
+
+ // Switches that are completely converted to ifs and one switch.
+ runConvertCasesToIf(ImmutableList.of(0, 1000, 1001, 1002, 1003, 1004), -100, 1, 1, 0);
+ runConvertCasesToIf(ImmutableList.of(1000, 1001, 1002, 1003, 1004, 2000), -100, 1, 1, 0);
+ runConvertCasesToIf(ImmutableList.of(
+ Integer.MIN_VALUE, 1000, 1001, 1002, 1003, 1004), -100, 1, 1, 0);
+ runConvertCasesToIf(ImmutableList.of(
+ 1000, 1001, 1002, 1003, 1004, Integer.MAX_VALUE), -100, 1, 1, 0);
+ runConvertCasesToIf(ImmutableList.of(0, 1000, 1001, 1002, 1003, 1004, 2000), -100, 2, 1, 0);
+ runConvertCasesToIf(ImmutableList.of(
+ Integer.MIN_VALUE, 1000, 1001, 1002, 1003, 1004, Integer.MAX_VALUE), -100, 2, 1, 0);
+
+ // Switches that are completely converted to ifs and two switches.
+ runConvertCasesToIf(ImmutableList.of(
+ 0, 1, 2, 3, 4, 1000, 1001, 1002, 1003, 1004), -100, 0, 2, 0);
+ runConvertCasesToIf(ImmutableList.of(
+ -1000, 0, 1, 2, 3, 4, 1000, 1001, 1002, 1003, 1004), -100, 1, 2, 0);
+ runConvertCasesToIf(ImmutableList.of(
+ -1000, 0, 1, 2, 3, 4, 1000, 1001, 1002, 1003, 1004, 2000), -100, 2, 2, 0);
+
+ // Switches that are completely converted two switches (one sparse and one packed).
+ runConvertCasesToIf(ImmutableList.of(
+ -1000, -900, -800, -700, -600, -500, -400, -300,
+ 1000, 1001, 1002, 1003, 1004,
+ 2000, 2100, 2200, 2300, 2400, 2500), -100, 0, 1, 1);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/switches/SwitchRewritingTest.java b/src/test/java/com/android/tools/r8/rewrite/switches/SwitchRewritingTest.java
new file mode 100644
index 0000000..3d921b6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/switches/SwitchRewritingTest.java
@@ -0,0 +1,250 @@
+// 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.rewrite.switches;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.code.Const;
+import com.android.tools.r8.code.Const4;
+import com.android.tools.r8.code.ConstHigh16;
+import com.android.tools.r8.code.IfEq;
+import com.android.tools.r8.code.IfEqz;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.code.PackedSwitch;
+import com.android.tools.r8.code.SparseSwitch;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.smali.SmaliBuilder;
+import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
+import com.android.tools.r8.smali.SmaliTestBase;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.function.Consumer;
+import org.junit.Test;
+
+public class SwitchRewritingTest extends SmaliTestBase {
+
+ static boolean twoCaseWillUsePackedSwitch(int key1, int key2) {
+ assert key1 != key2;
+ return Math.abs((long) key1 - (long) key2) == 1;
+ }
+
+ private boolean some16BitConst(Instruction instruction) {
+ return instruction instanceof Const4
+ || instruction instanceof ConstHigh16
+ || instruction instanceof Const;
+ }
+ private void runSingleCaseDexTest(boolean packed, int key) {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+ String switchInstruction;
+ String switchData;
+ if (packed) {
+ switchInstruction = "packed-switch";
+ switchData = StringUtils.join(
+ "\n",
+ " :switch_data",
+ " .packed-switch " + key,
+ " :case_0",
+ " .end packed-switch");
+ } else {
+ switchInstruction = "sparse-switch";
+ switchData = StringUtils.join(
+ "\n",
+ " :switch_data",
+ " .sparse-switch",
+ " " + key + " -> :case_0",
+ " .end sparse-switch");
+ }
+ MethodSignature signature = builder.addStaticMethod(
+ "int",
+ DEFAULT_METHOD_NAME,
+ ImmutableList.of("int"),
+ 0,
+ " " + switchInstruction + " p0, :switch_data",
+ " const/4 p0, 0x5",
+ " goto :return",
+ " :case_0",
+ " const/4 p0, 0x3",
+ " :return",
+ " return p0",
+ switchData);
+
+ builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " const/4 v1, 0",
+ " invoke-static { v1 }, LTest;->method(I)I",
+ " move-result v1",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+ " return-void"
+ );
+
+ AndroidApp originalApplication = buildApplication(builder);
+ AndroidApp processedApplication = processApplication(originalApplication);
+ DexEncodedMethod method = getMethod(processedApplication, signature);
+ DexCode code = method.getCode().asDexCode();
+
+ if (key == 0) {
+ assertEquals(5, code.instructions.length);
+ assertTrue(code.instructions[0] instanceof IfEqz);
+ } else {
+ assertEquals(6, code.instructions.length);
+ assertTrue(some16BitConst(code.instructions[0]));
+ assertTrue(code.instructions[1] instanceof IfEq);
+ }
+ }
+
+ @Test
+ public void singleCaseDex() {
+ for (boolean packed : new boolean[]{true, false}) {
+ runSingleCaseDexTest(packed, Integer.MIN_VALUE);
+ runSingleCaseDexTest(packed, -1);
+ runSingleCaseDexTest(packed, 0);
+ runSingleCaseDexTest(packed, 1);
+ runSingleCaseDexTest(packed, Integer.MAX_VALUE);
+ }
+ }
+
+ private void runTwoCaseSparseToPackedOrIfsDexTest(int key1, int key2) {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ MethodSignature signature = builder.addStaticMethod(
+ "int",
+ DEFAULT_METHOD_NAME,
+ ImmutableList.of("int"),
+ 0,
+ " sparse-switch p0, :sparse_switch_data",
+ " const/4 v0, 0x5",
+ " goto :return",
+ " :case_1",
+ " const/4 v0, 0x3",
+ " goto :return",
+ " :case_2",
+ " const/4 v0, 0x4",
+ " :return",
+ " return v0",
+ " :sparse_switch_data",
+ " .sparse-switch",
+ " " + key1 + " -> :case_1",
+ " " + key2 + " -> :case_2",
+ " .end sparse-switch");
+
+ builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " const/4 v1, 0",
+ " invoke-static { v1 }, LTest;->method(I)I",
+ " move-result v1",
+ " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+ " return-void"
+ );
+
+ AndroidApp originalApplication = buildApplication(builder);
+ AndroidApp processedApplication = processApplication(originalApplication);
+ DexEncodedMethod method = getMethod(processedApplication, signature);
+ DexCode code = method.getCode().asDexCode();
+ if (twoCaseWillUsePackedSwitch(key1, key2)) {
+ assertTrue(code.instructions[0] instanceof PackedSwitch);
+ } else {
+ if (key1 == 0) {
+ assertTrue(code.instructions[0] instanceof IfEqz);
+ } else {
+ // Const instruction before if.
+ assertTrue(code.instructions[1] instanceof IfEq);
+ }
+ }
+ }
+
+ @Test
+ public void twoCaseSparseToPackedOrIfsDex() {
+ for (int delta = 1; delta <= 3; delta++) {
+ runTwoCaseSparseToPackedOrIfsDexTest(0, delta);
+ runTwoCaseSparseToPackedOrIfsDexTest(-delta, 0);
+ runTwoCaseSparseToPackedOrIfsDexTest(Integer.MIN_VALUE, Integer.MIN_VALUE + delta);
+ runTwoCaseSparseToPackedOrIfsDexTest(Integer.MAX_VALUE - delta, Integer.MAX_VALUE);
+ }
+ runTwoCaseSparseToPackedOrIfsDexTest(-1, 1);
+ runTwoCaseSparseToPackedOrIfsDexTest(-2, 1);
+ runTwoCaseSparseToPackedOrIfsDexTest(-1, 2);
+ runTwoCaseSparseToPackedOrIfsDexTest(Integer.MIN_VALUE, Integer.MAX_VALUE);
+ runTwoCaseSparseToPackedOrIfsDexTest(Integer.MIN_VALUE + 1, Integer.MAX_VALUE);
+ runTwoCaseSparseToPackedOrIfsDexTest(Integer.MIN_VALUE, Integer.MAX_VALUE - 1);
+ }
+
+ private void runLargerSwitchDexTest(int firstKey, int keyStep, int totalKeys,
+ Integer additionalLastKey) throws Exception {
+ SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+ StringBuilder switchSource = new StringBuilder();
+ StringBuilder targetCode = new StringBuilder();
+ for (int i = 0; i < totalKeys; i++) {
+ String caseLabel = "case_" + i;
+ switchSource.append(" " + (firstKey + i * keyStep) + " -> :" + caseLabel + "\n");
+ targetCode.append(" :" + caseLabel + "\n");
+ targetCode.append(" goto :return\n");
+ }
+ if (additionalLastKey != null) {
+ String caseLabel = "case_" + totalKeys;
+ switchSource.append(" " + additionalLastKey + " -> :" + caseLabel + "\n");
+ targetCode.append(" :" + caseLabel + "\n");
+ targetCode.append(" goto :return\n");
+ }
+
+ MethodSignature signature = builder.addStaticMethod(
+ "void",
+ DEFAULT_METHOD_NAME,
+ ImmutableList.of("int"),
+ 0,
+ " sparse-switch p0, :sparse_switch_data",
+ " goto :return",
+ targetCode.toString(),
+ " :return",
+ " return-void",
+ " :sparse_switch_data",
+ " .sparse-switch",
+ switchSource.toString(),
+ " .end sparse-switch");
+
+ builder.addMainMethod(
+ 2,
+ " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+ " const/4 v1, 0",
+ " invoke-static { v1 }, LTest;->method(I)V",
+ " return-void"
+ );
+
+ Consumer<InternalOptions> optionsConsumer = options -> {
+ options.verbose = true;
+ options.printTimes = true;
+ };
+ AndroidApp originalApplication = buildApplication(builder);
+ AndroidApp processedApplication = processApplication(originalApplication, optionsConsumer);
+ DexEncodedMethod method = getMethod(processedApplication, signature);
+ DexCode code = method.getCode().asDexCode();
+ if (keyStep <= 2) {
+ assertTrue(code.instructions[0] instanceof PackedSwitch);
+ } else {
+ assertTrue(code.instructions[0] instanceof SparseSwitch);
+ }
+ }
+
+ @Test
+ public void twoMonsterSparseToPackedDex() throws Exception {
+ runLargerSwitchDexTest(0, 1, 100, null);
+ runLargerSwitchDexTest(0, 2, 100, null);
+ runLargerSwitchDexTest(0, 3, 100, null);
+ runLargerSwitchDexTest(100, 100, 100, null);
+ runLargerSwitchDexTest(-10000, 100, 100, null);
+ runLargerSwitchDexTest(-10000, 200, 100, 10000);
+ runLargerSwitchDexTest(
+ Integer.MIN_VALUE, (int) ((-(long)Integer.MIN_VALUE) / 16), 32, Integer.MAX_VALUE);
+
+ // TODO(63090177): Currently this is commented out as R8 gets really slow for large switches.
+ // runLargerSwitchDexTest(0, 1, Constants.U16BIT_MAX, null);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/smali/CatchSuccessorFallthroughTest.java b/src/test/java/com/android/tools/r8/smali/CatchSuccessorFallthroughTest.java
index 909f574..d2391ff 100644
--- a/src/test/java/com/android/tools/r8/smali/CatchSuccessorFallthroughTest.java
+++ b/src/test/java/com/android/tools/r8/smali/CatchSuccessorFallthroughTest.java
@@ -6,13 +6,14 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Return;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
+import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import java.util.Arrays;
import java.util.Collections;
@@ -62,13 +63,11 @@
" invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(I)V",
" return-void");
- InternalOptions options = new InternalOptions();
- DexApplication originalApplication = buildApplication(builder, options);
-
+ AndroidApp originalApplication = buildApplication(builder);
DexEncodedMethod method = getMethod(originalApplication, methodSig);
// Get the IR pre-optimization.
- IRCode code = method.buildIR(options);
+ IRCode code = method.buildIR(new InternalOptions());
// Find the exit block and assert that the value is a phi merging the exceptional edge
// with the normal edge.
diff --git a/src/test/java/com/android/tools/r8/smali/ComputeBlockTryRangeTest.java b/src/test/java/com/android/tools/r8/smali/ComputeBlockTryRangeTest.java
index 64aa946..de8b832 100644
--- a/src/test/java/com/android/tools/r8/smali/ComputeBlockTryRangeTest.java
+++ b/src/test/java/com/android/tools/r8/smali/ComputeBlockTryRangeTest.java
@@ -3,9 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.smali;
-import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
+import com.android.tools.r8.utils.AndroidApp;
import java.util.Arrays;
import java.util.Collections;
import org.junit.Test;
@@ -48,9 +48,8 @@
" goto :in_try"
);
- InternalOptions options = new InternalOptions();
- DexApplication originalApplication = buildApplication(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
+ AndroidApp originalApplication = buildApplication(builder);
+ AndroidApp processedApplication = processApplication(originalApplication);
DexEncodedMethod method = getMethod(processedApplication, methodSig);
assert method.getCode().asDexCode().tries.length > 0;
diff --git a/src/test/java/com/android/tools/r8/smali/JumboStringTest.java b/src/test/java/com/android/tools/r8/smali/JumboStringTest.java
index 8d4b841..0395177 100644
--- a/src/test/java/com/android/tools/r8/smali/JumboStringTest.java
+++ b/src/test/java/com/android/tools/r8/smali/JumboStringTest.java
@@ -6,9 +6,8 @@
import static org.junit.Assert.assertEquals;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
+import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import org.junit.Test;
@@ -56,10 +55,9 @@
" return-void"
);
- InternalOptions options = new InternalOptions();
- DexApplication originalApplication = buildApplication(smaliBuilder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
- String result = runArt(processedApplication, options);
+ AndroidApp originalApplication = buildApplication(smaliBuilder);
+ AndroidApp processedApplication = processApplication(originalApplication);
+ String result = runArt(processedApplication);
assertEquals(expectedBuilder.toString(), result);
}
diff --git a/src/test/java/com/android/tools/r8/smali/OutlineTest.java b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
index 1aa9be7..d7f16e8 100644
--- a/src/test/java/com/android/tools/r8/smali/OutlineTest.java
+++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -21,26 +21,26 @@
import com.android.tools.r8.code.ReturnObject;
import com.android.tools.r8.code.ReturnVoid;
import com.android.tools.r8.code.ReturnWide;
-import com.android.tools.r8.errors.DexOverflowException;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
+import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.DexInspector.ClassSubject;
import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.function.Consumer;
import java.util.stream.Collectors;
-import org.antlr.runtime.RecognitionException;
import org.junit.Test;
public class OutlineTest extends SmaliTestBase {
@@ -52,6 +52,15 @@
return result;
}
+ private Consumer<InternalOptions> configureOptions(Consumer<OutlineOptions> optionsConsumer) {
+ return options -> {
+ // Disable inlining to make sure that code looks as expected.
+ options.inlineAccessors = false;
+ // Also apply outline options.
+ optionsConsumer.accept(options.outline);
+ };
+ }
+
DexEncodedMethod getInvokedMethod(DexApplication application, InvokeStatic invoke) {
DexInspector inspector = new DexInspector(application);
ClassSubject clazz = inspector.clazz(invoke.getMethod().holder.toSourceString());
@@ -62,31 +71,19 @@
invokedMethod.proto.returnType.toSourceString(),
invokedMethod.name.toString(),
Arrays.stream(invokedMethod.proto.parameters.values)
- .map(p -> p.toSourceString())
+ .map(DexType::toSourceString)
.collect(Collectors.toList()));
assertTrue(method.isPresent());
return method.getMethod();
}
- String firstOutlineMethodName(InternalOptions options) {
- StringBuilder builder = new StringBuilder(options.outline.className);
- builder.append('.');
- builder.append(options.outline.methodPrefix);
- builder.append("0");
- return builder.toString();
+ private String firstOutlineMethodName() {
+ return OutlineOptions.CLASS_NAME + '.' + OutlineOptions.METHOD_PREFIX + "0";
}
- MethodSignature firstOutlineMethodSignature(
- String returnType, List<String> parameterTypes, InternalOptions options) {
- return new MethodSignature(
- options.outline.className, options.outline.methodPrefix + "0", returnType, parameterTypes);
- }
-
- boolean isOutlineMethodName(InternalOptions options, String qualifiedName) {
- StringBuilder builder = new StringBuilder(options.outline.className);
- builder.append('.');
- builder.append(options.outline.methodPrefix);
- return qualifiedName.indexOf(builder.toString()) == 0;
+ private boolean isOutlineMethodName(String qualifiedName) {
+ String qualifiedPrefix = OutlineOptions.CLASS_NAME + '.' + OutlineOptions.METHOD_PREFIX;
+ return qualifiedName.indexOf(qualifiedPrefix) == 0;
}
@Test
@@ -127,14 +124,16 @@
);
for (int i = 2; i < 6; i++) {
- InternalOptions options = createInternalOptions();
- options.outline.threshold = 1;
- options.outline.minSize = i;
- options.outline.maxSize = i;
+ final int j = i;
+ Consumer<InternalOptions> options = configureOptions(outline -> {
+ outline.threshold = 1;
+ outline.minSize = j;
+ outline.maxSize = j;
+ });
- DexApplication originalApplication = buildApplication(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
- assertEquals(2, Iterables.size(processedApplication.classes()));
+ AndroidApp originalApplication = buildApplication(builder);
+ AndroidApp processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, getNumberOfProgramClasses(processedApplication));
// Return the processed method for inspection.
DexEncodedMethod method = getMethod(processedApplication, signature);
@@ -143,10 +142,10 @@
assertTrue(code.instructions[0] instanceof ConstString);
assertTrue(code.instructions[1] instanceof InvokeStatic);
InvokeStatic invoke = (InvokeStatic) code.instructions[1];
- assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
// Run code and check result.
- String result = runArt(processedApplication, options);
+ String result = runArt(processedApplication);
assertEquals("TestTestTestTest", result);
}
}
@@ -192,14 +191,16 @@
);
for (int i = 2; i < 6; i++) {
- InternalOptions options = createInternalOptions();
- options.outline.threshold = 1;
- options.outline.minSize = i;
- options.outline.maxSize = i;
+ final int finalI = i;
+ Consumer<InternalOptions> options = configureOptions(outline -> {
+ outline.threshold = 1;
+ outline.minSize = finalI;
+ outline.maxSize = finalI;
+ });
- DexApplication originalApplication = buildApplication(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
- assertEquals(2, Iterables.size(processedApplication.classes()));
+ AndroidApp originalApplication = buildApplication(builder);
+ AndroidApp processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, getNumberOfProgramClasses(processedApplication));
// Return the processed method for inspection.
DexEncodedMethod method = getMethod(processedApplication, signature);
@@ -213,10 +214,10 @@
}
assertTrue(code.instructions[firstOutlineInvoke] instanceof InvokeStatic);
InvokeStatic invoke = (InvokeStatic) code.instructions[firstOutlineInvoke];
- assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
// Run code and check result.
- String result = runArt(processedApplication, options);
+ String result = runArt(processedApplication);
assertEquals("Test1Test2Test3Test4", result);
}
}
@@ -258,11 +259,12 @@
" return-void"
);
- InternalOptions options = createInternalOptions();
- options.outline.threshold = 1;
- DexApplication originalApplication = buildApplication(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
- assertEquals(2, Iterables.size(processedApplication.classes()));
+ Consumer<InternalOptions> options = configureOptions(outline -> {
+ outline.threshold = 1;
+ });
+ AndroidApp originalApplication = buildApplication(builder);
+ AndroidApp processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, getNumberOfProgramClasses(processedApplication));
// Return the processed method for inspection.
DexEncodedMethod method = getMethod(processedApplication, signature);
@@ -271,10 +273,10 @@
assertTrue(code.instructions[0] instanceof ConstString);
assertTrue(code.instructions[1] instanceof InvokeStatic);
InvokeStatic invoke = (InvokeStatic) code.instructions[1];
- assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
// Run code and check result.
- String result = runArt(processedApplication, options);
+ String result = runArt(processedApplication);
assertEquals("1", result);
}
@@ -322,11 +324,12 @@
" return-void"
);
- InternalOptions options = createInternalOptions();
- options.outline.threshold = 1;
- DexApplication originalApplication = buildApplication(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
- assertEquals(2, Iterables.size(processedApplication.classes()));
+ Consumer<InternalOptions> options = configureOptions(outline -> {
+ outline.threshold = 1;
+ });
+ AndroidApp originalApplication = buildApplication(builder);
+ AndroidApp processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, getNumberOfProgramClasses(processedApplication));
// Return the processed method for inspection.
DexEncodedMethod method = getMethod(processedApplication, signature);
@@ -336,10 +339,10 @@
assertTrue(code.instructions[1] instanceof ConstString);
assertTrue(code.instructions[2] instanceof InvokeStatic);
InvokeStatic invoke = (InvokeStatic) code.instructions[2];
- assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
// Run code and check result.
- String result = runArt(processedApplication, options);
+ String result = runArt(processedApplication);
assertEquals("TestXTest1TestYTest2Test3", result);
}
@@ -381,14 +384,16 @@
);
for (int i = 2; i < 4; i++) {
- InternalOptions options = createInternalOptions();
- options.outline.threshold = 1;
- options.outline.minSize = i;
- options.outline.maxSize = i;
+ final int finalI = i;
+ Consumer<InternalOptions> options = configureOptions(outline -> {
+ outline.threshold = 1;
+ outline.minSize = finalI;
+ outline.maxSize = finalI;
+ });
- DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
- assertEquals(2, Iterables.size(processedApplication.classes()));
+ AndroidApp originalApplication = buildApplicationWithAndroidJar(builder);
+ AndroidApp processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, getNumberOfProgramClasses(processedApplication));
// Return the processed method for inspection.
DexEncodedMethod method = getMethod(processedApplication, signature);
@@ -398,17 +403,17 @@
if (i < 3) {
assertTrue(code.instructions[1] instanceof InvokeStatic);
InvokeStatic invoke = (InvokeStatic) code.instructions[1];
- assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
} else {
assertTrue(code.instructions[1] instanceof InvokeVirtual);
assertTrue(code.instructions[2] instanceof InvokeVirtual);
assertTrue(code.instructions[3] instanceof InvokeStatic);
InvokeStatic invoke = (InvokeStatic) code.instructions[3];
- assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
}
// Run code and check result.
- String result = runArt(processedApplication, options);
+ String result = runArt(processedApplication);
StringBuilder resultBuilder = new StringBuilder();
for (int j = 0; j < 4; j++) {
resultBuilder.append(0x7fffffff00000000L);
@@ -455,14 +460,16 @@
);
for (int i = 2; i < 4; i++) {
- InternalOptions options = createInternalOptions();
- options.outline.threshold = 1;
- options.outline.minSize = i;
- options.outline.maxSize = i;
+ final int finalI = i;
+ Consumer<InternalOptions> options = configureOptions(outline -> {
+ outline.threshold = 1;
+ outline.minSize = finalI;
+ outline.maxSize = finalI;
+ });
- DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
- assertEquals(2, Iterables.size(processedApplication.classes()));
+ AndroidApp originalApplication = buildApplicationWithAndroidJar(builder);
+ AndroidApp processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, getNumberOfProgramClasses(processedApplication));
// Return the processed method for inspection.
DexEncodedMethod method = getMethod(processedApplication, signature);
@@ -472,17 +479,17 @@
if (i < 3) {
assertTrue(code.instructions[1] instanceof InvokeStatic);
InvokeStatic invoke = (InvokeStatic) code.instructions[1];
- assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
} else {
assertTrue(code.instructions[1] instanceof InvokeVirtual);
assertTrue(code.instructions[2] instanceof InvokeVirtual);
assertTrue(code.instructions[3] instanceof InvokeStatic);
InvokeStatic invoke = (InvokeStatic) code.instructions[3];
- assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
}
// Run code and check result.
- String result = runArt(processedApplication, options);
+ String result = runArt(processedApplication);
StringBuilder resultBuilder = new StringBuilder();
for (int j = 0; j < 4; j++) {
resultBuilder.append(1.0d);
@@ -525,14 +532,16 @@
);
for (int i = 2; i < 6; i++) {
- InternalOptions options = createInternalOptions();
- options.outline.threshold = 1;
- options.outline.minSize = i;
- options.outline.maxSize = i;
+ final int finalI = i;
+ Consumer<InternalOptions> options = configureOptions(outline -> {
+ outline.threshold = 1;
+ outline.minSize = finalI;
+ outline.maxSize = finalI;
+ });
- DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
- assertEquals(2, Iterables.size(processedApplication.classes()));
+ AndroidApp originalApplication = buildApplicationWithAndroidJar(builder);
+ AndroidApp processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, getNumberOfProgramClasses(processedApplication));
// Return the processed main method for inspection.
DexEncodedMethod mainMethod = getMethod(processedApplication, mainSignature);
@@ -548,18 +557,18 @@
}
if (i == 2) {
InvokeStatic invoke = (InvokeStatic) mainCode.instructions[4];
- assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
} else if (i == 3) {
InvokeStatic invoke = (InvokeStatic) mainCode.instructions[1];
- assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
} else {
assert i == 4 || i == 5;
InvokeStatic invoke = (InvokeStatic) mainCode.instructions[2];
- assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
}
// Run code and check result.
- String result = runArt(processedApplication, options);
+ String result = runArt(processedApplication);
assertEquals("1122", result);
}
}
@@ -623,29 +632,30 @@
" return-void"
);
- InternalOptions options = createInternalOptions();
- options.outline.threshold = 1;
- options.outline.minSize = 7;
- options.outline.maxSize = 7;
+ Consumer<InternalOptions> options = configureOptions(outline -> {
+ outline.threshold = 1;
+ outline.minSize = 7;
+ outline.maxSize = 7;
+ });
- DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
- assertEquals(2, Iterables.size(processedApplication.classes()));
+ AndroidApp originalApplication = buildApplicationWithAndroidJar(builder);
+ AndroidApp processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, getNumberOfProgramClasses(processedApplication));
DexCode code1 = getMethod(processedApplication, signature1).getCode().asDexCode();
assertEquals(4, code1.instructions.length);
assertTrue(code1.instructions[1] instanceof InvokeStatic);
InvokeStatic invoke1 = (InvokeStatic) code1.instructions[1];
- assertTrue(isOutlineMethodName(options, invoke1.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke1.getMethod().qualifiedName()));
DexCode code2 = getMethod(processedApplication, signature2).getCode().asDexCode();
assertEquals(5, code2.instructions.length);
assertTrue(code2.instructions[2] instanceof InvokeStatic);
InvokeStatic invoke2 = (InvokeStatic) code2.instructions[2];
- assertTrue(isOutlineMethodName(options, invoke1.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke1.getMethod().qualifiedName()));
// Run code and check result.
- String result = runArt(processedApplication, options);
+ String result = runArt(processedApplication);
assertEquals("Test1Test1Test1Test1Test2Test2Test2Test2", result);
}
@@ -687,14 +697,16 @@
);
for (int i = 2; i < 8; i++) {
- InternalOptions options = createInternalOptions();
- options.outline.threshold = 1;
- options.outline.minSize = i;
- options.outline.maxSize = i;
+ final int finalI = i;
+ Consumer<InternalOptions> options = configureOptions(outline -> {
+ outline.threshold = 1;
+ outline.minSize = finalI;
+ outline.maxSize = finalI;
+ });
- DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
- assertEquals(2, Iterables.size(processedApplication.classes()));
+ AndroidApp originalApplication = buildApplicationWithAndroidJar(builder);
+ AndroidApp processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, getNumberOfProgramClasses(processedApplication));
DexCode code = getMethod(processedApplication, signature).getCode().asDexCode();
InvokeStatic invoke;
@@ -711,10 +723,10 @@
outlineInstructionIndex = 2;
}
invoke = (InvokeStatic) code.instructions[outlineInstructionIndex];
- assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
// Run code and check result.
- String result = runArt(processedApplication, options);
+ String result = runArt(processedApplication);
assertEquals("Test2Test2", result);
}
}
@@ -744,23 +756,24 @@
" return-void"
);
- InternalOptions options = createInternalOptions();
- options.outline.threshold = 1;
- options.outline.minSize = 3;
- options.outline.maxSize = 3;
+ Consumer<InternalOptions> options = configureOptions(outline -> {
+ outline.threshold = 1;
+ outline.minSize = 3;
+ outline.maxSize = 3;
+ });
- DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
- assertEquals(2, Iterables.size(processedApplication.classes()));
+ AndroidApp originalApplication = buildApplicationWithAndroidJar(builder);
+ AndroidApp processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, getNumberOfProgramClasses(processedApplication));
DexCode code = getMethod(processedApplication, signature1).getCode().asDexCode();
InvokeStatic invoke;
assertTrue(code.instructions[0] instanceof InvokeStatic);
invoke = (InvokeStatic) code.instructions[0];
- assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
// Run code and check result.
- String result = runArt(processedApplication, options);
+ String result = runArt(processedApplication);
assertEquals("", result);
}
@@ -817,18 +830,19 @@
" return-void"
);
- InternalOptions options = createInternalOptions();
- options.outline.threshold = 1;
- options.outline.minSize = 3;
- options.outline.maxSize = 3;
+ Consumer<InternalOptions> options = configureOptions(outline -> {
+ outline.threshold = 1;
+ outline.minSize = 3;
+ outline.maxSize = 3;
+ });
- DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
- assertEquals(2, Iterables.size(processedApplication.classes()));
+ AndroidApp originalApplication = buildApplicationWithAndroidJar(builder);
+ AndroidApp processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, getNumberOfProgramClasses(processedApplication));
// Check that three outlining methods was created.
DexInspector inspector = new DexInspector(processedApplication);
- ClassSubject clazz = inspector.clazz(options.outline.className);
+ ClassSubject clazz = inspector.clazz(OutlineOptions.CLASS_NAME);
assertTrue(clazz.isPresent());
assertEquals(3, clazz.getDexClass().directMethods().length);
// Collect the return types of the putlines for the body of method1 and method2.
@@ -842,12 +856,12 @@
assert r.size() == 2;
DexType r1 = r.get(0);
DexType r2 = r.get(1);
- DexItemFactory factory = processedApplication.dexItemFactory;
+ DexItemFactory factory = inspector.getFactory();
assertTrue(r1 == factory.voidType && r2 == factory.stringType ||
r1 == factory.stringType && r2 == factory.voidType);
// Run the code.
- String result = runArt(processedApplication, options);
+ String result = runArt(processedApplication);
assertEquals("TestTestTestTest", result);
}
@@ -888,37 +902,40 @@
" return-void"
);
- InternalOptions options = createInternalOptions();
- options.outline.threshold = 1;
- options.outline.minSize = 3;
- options.outline.maxSize = 3;
+ Consumer<InternalOptions> options = configureOptions(outline -> {
+ outline.threshold = 1;
+ outline.minSize = 3;
+ outline.maxSize = 3;
+ });
- DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
- assertEquals(2, Iterables.size(processedApplication.classes()));
+ AndroidApp originalApplication = buildApplicationWithAndroidJar(builder);
+ AndroidApp processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, getNumberOfProgramClasses(processedApplication));
final int count = 10;
// Process the application several times. Each time will outline the previous outline.
for (int i = 0; i < count; i++) {
// Build a new application with the Outliner class.
- DexApplication.Builder appBuilder = processedApplication.builder();
- originalApplication = appBuilder.build();
+ originalApplication = processedApplication;
processedApplication = processApplication(originalApplication, options);
- assertEquals(i + 3, Iterables.size(processedApplication.classes()));
+ assertEquals(i + 3, getNumberOfProgramClasses(processedApplication));
}
// Process the application several times. No more outlining as threshold has been raised.
- options.outline.threshold = 2;
+ options = configureOptions(outline -> {
+ outline.threshold = 2;
+ outline.minSize = 3;
+ outline.maxSize = 3;
+ });
for (int i = 0; i < count; i++) {
// Build a new application with the Outliner class.
- DexApplication.Builder appBuilder = processedApplication.builder();
- originalApplication = appBuilder.build();
+ originalApplication = processedApplication;
processedApplication = processApplication(originalApplication, options);
- assertEquals(count - 1 + 3, Iterables.size(processedApplication.classes()));
+ assertEquals(count - 1 + 3, getNumberOfProgramClasses(processedApplication));
}
// Run the application with several levels of outlining.
- String result = runArt(processedApplication, options);
+ String result = runArt(processedApplication);
assertEquals("TestTestTestTest", result);
}
@@ -950,14 +967,15 @@
" return-void"
);
- InternalOptions options = createInternalOptions();
- options.outline.threshold = 1;
- options.outline.minSize = 5;
- options.outline.maxSize = 5;
+ Consumer<InternalOptions> options = configureOptions(outline -> {
+ outline.threshold = 1;
+ outline.minSize = 5;
+ outline.maxSize = 5;
+ });
- DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
- assertEquals(2, Iterables.size(processedApplication.classes()));
+ AndroidApp originalApplication = buildApplicationWithAndroidJar(builder);
+ AndroidApp processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, getNumberOfProgramClasses(processedApplication));
// Return the processed method for inspection.
DexEncodedMethod method = getMethod(processedApplication, signature);
@@ -968,10 +986,10 @@
assertTrue(code.instructions[1] instanceof MoveResultWide);
assertTrue(code.instructions[2] instanceof ReturnWide);
InvokeStatic invoke = (InvokeStatic) code.instructions[0];
- assertEquals(firstOutlineMethodName(options), invoke.getMethod().qualifiedName());
+ assertEquals(firstOutlineMethodName(), invoke.getMethod().qualifiedName());
// Run the code and expect a parsable long.
- String result = runArt(processedApplication, options);
+ String result = runArt(processedApplication);
Long.parseLong(result);
}
@@ -1010,14 +1028,15 @@
" return-void"
);
- InternalOptions options = createInternalOptions();
- options.outline.threshold = 1;
- options.outline.minSize = 4;
- options.outline.maxSize = 4;
+ Consumer<InternalOptions> options = configureOptions(outline -> {
+ outline.threshold = 1;
+ outline.minSize = 4;
+ outline.maxSize = 4;
+ });
- DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
- assertEquals(2, Iterables.size(processedApplication.classes()));
+ AndroidApp originalApplication = buildApplicationWithAndroidJar(builder);
+ AndroidApp processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, getNumberOfProgramClasses(processedApplication));
// Return the processed method for inspection.
DexEncodedMethod method = getMethod(processedApplication, signature);
@@ -1026,10 +1045,10 @@
assertTrue(code.instructions[0] instanceof InvokeStatic);
assertTrue(code.instructions[1] instanceof ReturnObject);
InvokeStatic invoke = (InvokeStatic) code.instructions[0];
- assertEquals(firstOutlineMethodName(options), invoke.getMethod().qualifiedName());
+ assertEquals(firstOutlineMethodName(), invoke.getMethod().qualifiedName());
// Run code and check result.
- String result = runArt(processedApplication, options);
+ String result = runArt(processedApplication);
assertEquals("null", result);
}
@@ -1084,14 +1103,15 @@
" return-void"
);
- InternalOptions options = createInternalOptions();
- options.outline.threshold = 1;
- options.outline.minSize = 4;
- options.outline.maxSize = 4;
+ Consumer<InternalOptions> options = configureOptions(outline -> {
+ outline.threshold = 1;
+ outline.minSize = 4;
+ outline.maxSize = 4;
+ });
- DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
- assertEquals(2, Iterables.size(processedApplication.classes()));
+ AndroidApp originalApplication = buildApplicationWithAndroidJar(builder);
+ AndroidApp processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, getNumberOfProgramClasses(processedApplication));
// Return the processed method for inspection.
DexEncodedMethod method1 = getMethod(processedApplication, signature1);
@@ -1101,7 +1121,7 @@
assertTrue(code1.instructions[1] instanceof MoveResult);
assertTrue(code1.instructions[2] instanceof Return);
InvokeStatic invoke1 = (InvokeStatic) code1.instructions[0];
- assertTrue(isOutlineMethodName(options, invoke1.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke1.getMethod().qualifiedName()));
DexEncodedMethod method2 = getMethod(processedApplication, signature2);
DexCode code2 = method2.getCode().asDexCode();
@@ -1110,7 +1130,7 @@
assertEquals(invoke1.getMethod().qualifiedName(), invoke2.getMethod().qualifiedName());
// Run code and check result.
- String result = runArt(processedApplication, options);
+ String result = runArt(processedApplication);
assertEquals("44", result);
}
@@ -1159,14 +1179,15 @@
" return-void"
);
- InternalOptions options = createInternalOptions();
- options.outline.threshold = 1;
- options.outline.minSize = 3; // Outline add, sub and mul.
- options.outline.maxSize = 3;
+ Consumer<InternalOptions> options = configureOptions(outline -> {
+ outline.threshold = 1;
+ outline.minSize = 3; // Outline add, sub and mul.
+ outline.maxSize = 3;
+ });
- DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
- assertEquals(2, Iterables.size(processedApplication.classes()));
+ AndroidApp originalApplication = buildApplicationWithAndroidJar(builder);
+ AndroidApp processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, getNumberOfProgramClasses(processedApplication));
// Return the processed method for inspection.
DexEncodedMethod method = getMethod(processedApplication, signature);
@@ -1180,10 +1201,10 @@
assertTrue(code.instructions[5] instanceof Const4);
assertTrue(code.instructions[6] instanceof Return);
InvokeStatic invoke = (InvokeStatic) code.instructions[1];
- assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+ assertTrue(isOutlineMethodName(invoke.getMethod().qualifiedName()));
// Run code and check result.
- String result = runArt(processedApplication, options);
+ String result = runArt(processedApplication);
assertEquals("4", result);
}
@@ -1211,14 +1232,15 @@
" return-void"
);
- InternalOptions options = createInternalOptions();
- options.outline.threshold = 1;
- options.outline.minSize = 3;
- options.outline.maxSize = 3;
+ Consumer<InternalOptions> options = configureOptions(outline -> {
+ outline.threshold = 1;
+ outline.minSize = 3;
+ outline.maxSize = 3;
+ });
- DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
- assertEquals(2, Iterables.size(processedApplication.classes()));
+ AndroidApp originalApplication = buildApplicationWithAndroidJar(builder);
+ AndroidApp processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, getNumberOfProgramClasses(processedApplication));
// Return the processed method for inspection.
DexEncodedMethod method = getMethod(processedApplication, signature);
@@ -1227,10 +1249,10 @@
assertTrue(code.instructions[0] instanceof InvokeStatic);
assertTrue(code.instructions[1] instanceof ReturnVoid);
InvokeStatic invoke = (InvokeStatic) code.instructions[0];
- assertEquals(firstOutlineMethodName(options), invoke.getMethod().qualifiedName());
+ assertEquals(firstOutlineMethodName(), invoke.getMethod().qualifiedName());
// Run code and check result.
- String result = runArt(processedApplication, options);
+ String result = runArt(processedApplication);
assertEquals("", result);
}
@@ -1256,14 +1278,15 @@
" return-void"
);
- InternalOptions options = createInternalOptions();
- options.outline.threshold = 1;
- options.outline.minSize = 3;
- options.outline.maxSize = 3;
+ Consumer<InternalOptions> options = configureOptions(outline -> {
+ outline.threshold = 1;
+ outline.minSize = 3;
+ outline.maxSize = 3;
+ });
- DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
- assertEquals(2, Iterables.size(processedApplication.classes()));
+ AndroidApp originalApplication = buildApplicationWithAndroidJar(builder);
+ AndroidApp processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, getNumberOfProgramClasses(processedApplication));
// Return the processed method for inspection.
DexEncodedMethod method = getMethod(processedApplication, signature);
@@ -1272,10 +1295,10 @@
assertTrue(code.instructions[0] instanceof InvokeStatic);
assertTrue(code.instructions[1] instanceof ReturnVoid);
InvokeStatic invoke = (InvokeStatic) code.instructions[0];
- assertEquals(firstOutlineMethodName(options), invoke.getMethod().qualifiedName());
+ assertEquals(firstOutlineMethodName(), invoke.getMethod().qualifiedName());
// Run code and check result.
- String result = runArt(processedApplication, options);
+ String result = runArt(processedApplication);
assertEquals("", result);
}
@@ -1462,14 +1485,15 @@
" return-void"
);
- InternalOptions options = createInternalOptions();
- options.outline.threshold = 2;
+ Consumer<InternalOptions> options = configureOptions(outline -> {
+ outline.threshold = 2;
+ });
- DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
- assertEquals(2, Iterables.size(processedApplication.classes()));
+ AndroidApp originalApplication = buildApplicationWithAndroidJar(builder);
+ AndroidApp processedApplication = processApplication(originalApplication, options);
+ assertEquals(2, getNumberOfProgramClasses(processedApplication));
// Verify the code.
- runDex2Oat(processedApplication, options);
+ runDex2Oat(processedApplication);
}
}
diff --git a/src/test/java/com/android/tools/r8/smali/Regress38014736.java b/src/test/java/com/android/tools/r8/smali/Regress38014736.java
index bf89f7f..1f0cd78 100644
--- a/src/test/java/com/android/tools/r8/smali/Regress38014736.java
+++ b/src/test/java/com/android/tools/r8/smali/Regress38014736.java
@@ -5,9 +5,7 @@
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.StringUtils;
import org.junit.Test;
@@ -48,10 +46,9 @@
"invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V",
"return-void");
- InternalOptions options = new InternalOptions();
- DexApplication originalApplication = buildApplication(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
- String result = runArt(processedApplication, options);
+ AndroidApp originalApplication = buildApplication(builder);
+ AndroidApp processedApplication = processApplication(originalApplication);
+ String result = runArt(processedApplication);
// The art runtime changed the way exceptions are printed. Therefore, we only check
// for the type of the exception and that the message mentions null.
assertTrue(result.startsWith(StringUtils.joinLines("0", "java.lang.NumberFormatException:")));
diff --git a/src/test/java/com/android/tools/r8/smali/RunArtSmokeTest.java b/src/test/java/com/android/tools/r8/smali/RunArtSmokeTest.java
index 214a3b4..4d13ae0 100644
--- a/src/test/java/com/android/tools/r8/smali/RunArtSmokeTest.java
+++ b/src/test/java/com/android/tools/r8/smali/RunArtSmokeTest.java
@@ -12,12 +12,11 @@
import com.android.tools.r8.code.InvokeVirtual;
import com.android.tools.r8.code.ReturnVoid;
import com.android.tools.r8.code.SgetObject;
-import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
+import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.StringUtils;
-import com.google.common.collect.Iterables;
import org.junit.Test;
public class RunArtSmokeTest extends SmaliTestBase {
@@ -34,10 +33,9 @@
" return-void"
);
- InternalOptions options = new InternalOptions();
- DexApplication originalApplication = buildApplication(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
- assertEquals(1, Iterables.size(processedApplication.classes()));
+ AndroidApp originalApplication = buildApplication(builder);
+ AndroidApp processedApplication = processApplication(originalApplication);
+ assertEquals(1, getNumberOfProgramClasses(processedApplication));
// Return the processed method for inspection.
DexEncodedMethod main = getMethod(processedApplication, mainSignature);
@@ -50,7 +48,7 @@
assertTrue(code.instructions[3] instanceof ReturnVoid);
// Run the generated code in Art.
- String result = runArt(processedApplication, options);
+ String result = runArt(processedApplication);
assertEquals(StringUtils.lines("Hello, world!"), result);
}
}
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliBuildTest.java b/src/test/java/com/android/tools/r8/smali/SmaliBuildTest.java
index b3a4060..8e946b6 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliBuildTest.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliBuildTest.java
@@ -7,20 +7,24 @@
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.shaking.FilteredClassPath;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.InternalOptions;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
import org.junit.Test;
public class SmaliBuildTest extends SmaliTestBase {
- private void checkJavaLangString(DexApplication application, boolean present) {
- DexInspector inspector = new DexInspector(application);
- ClassSubject clazz = inspector.clazz("java.lang.String");
- assertEquals(present, clazz.isPresent());
+ private void checkJavaLangString(AndroidApp application, boolean present) {
+ try {
+ DexInspector inspector = new DexInspector(application);
+ ClassSubject clazz = inspector.clazz("java.lang.String");
+ assertEquals(present, clazz.isPresent());
+ } catch (IOException | ExecutionException e) {
+ throw new RuntimeException(e);
+ }
}
@Test
@@ -35,12 +39,11 @@
" return-void"
);
- InternalOptions options = new InternalOptions();
// No libraries added - java.lang.String is not present.
- DexApplication originalApplication = buildApplication(builder, options);
+ AndroidApp originalApplication = buildApplication(builder);
checkJavaLangString(originalApplication, false);
- DexApplication processedApplication = processApplication(originalApplication, options);
+ AndroidApp processedApplication = processApplication(originalApplication);
checkJavaLangString(processedApplication, false);
}
@@ -56,17 +59,17 @@
" return-void"
);
- InternalOptions options = new InternalOptions();
- AndroidApp app = AndroidApp.builder()
+ AndroidApp originalApp = AndroidApp.builder()
.addDexProgramData(builder.compile())
.addLibraryFiles(FilteredClassPath.unfiltered(ToolHelper.getDefaultAndroidJar()))
.build();
// Java standard library added - java.lang.String is present.
- DexApplication originalApplication = buildApplication(app, options);
- checkJavaLangString(originalApplication, true);
+ checkJavaLangString(originalApp, true);
- DexApplication processedApplication = processApplication(originalApplication, options);
- checkJavaLangString(processedApplication, true);
+ AndroidApp processedApplication = processApplication(originalApp);
+
+ // The library method is not part of the output.
+ checkJavaLangString(processedApplication, false);
}
}
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java b/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java
new file mode 100644
index 0000000..ad1e097
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java
@@ -0,0 +1,349 @@
+// 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.smali;
+
+import com.android.tools.r8.errors.DexOverflowException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.Smali;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import org.antlr.runtime.RecognitionException;
+
+public class SmaliBuilder {
+
+ public static class MethodSignature {
+
+ public final String clazz;
+ public final String name;
+ public final String returnType;
+ public final List<String> parameterTypes;
+
+ public MethodSignature(String clazz, String name, String returnType,
+ List<String> parameterTypes) {
+ this.clazz = clazz;
+ this.name = name;
+ this.returnType = returnType;
+ this.parameterTypes = parameterTypes;
+ }
+
+ public static MethodSignature staticInitializer(String clazz) {
+ return new MethodSignature(clazz, "<clinit>", "void", ImmutableList.of());
+ }
+
+ @Override
+ public String toString() {
+ return returnType + " " + clazz + "." + name
+ + "(" + StringUtils.join(parameterTypes, ",") + ")";
+ }
+ }
+
+ abstract class Builder {
+
+ String name;
+ String superName;
+ List<String> implementedInterfaces;
+ String sourceFile = null;
+ List<String> source = new ArrayList<>();
+
+ Builder(String name, String superName, List<String> implementedInterfaces) {
+ this.name = name;
+ this.superName = superName;
+ this.implementedInterfaces = implementedInterfaces;
+ }
+
+ protected void appendSuper(StringBuilder builder) {
+ builder.append(".super ");
+ builder.append(DescriptorUtils.javaTypeToDescriptor(superName));
+ builder.append("\n");
+ }
+
+ protected void appendImplementedInterfaces(StringBuilder builder) {
+ for (String implementedInterface : implementedInterfaces) {
+ builder.append(".implements ");
+ builder.append(DescriptorUtils.javaTypeToDescriptor(implementedInterface));
+ builder.append("\n");
+ }
+ }
+
+ protected void writeSource(StringBuilder builder) {
+ for (String sourceLine : source) {
+ builder.append(sourceLine);
+ builder.append("\n");
+ }
+ }
+ }
+
+ public class ClassBuilder extends Builder {
+
+ ClassBuilder(String name, String superName, List<String> implementedInterfaces) {
+ super(name, superName, implementedInterfaces);
+ }
+
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(".class public ");
+ builder.append(DescriptorUtils.javaTypeToDescriptor(name));
+ builder.append("\n");
+ appendSuper(builder);
+ appendImplementedInterfaces(builder);
+ builder.append("\n");
+ if (sourceFile != null) {
+ builder.append(".source \"").append(sourceFile).append("\"\n");
+ }
+ writeSource(builder);
+ return builder.toString();
+ }
+ }
+
+ public class InterfaceBuilder extends Builder {
+
+ InterfaceBuilder(String name, String superName) {
+ super(name, superName, ImmutableList.of());
+ }
+
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(".class public interface abstract ");
+ builder.append(DescriptorUtils.javaTypeToDescriptor(name));
+ builder.append("\n");
+ appendSuper(builder);
+ appendImplementedInterfaces(builder);
+ builder.append("\n");
+ writeSource(builder);
+ return builder.toString();
+ }
+ }
+
+ private String currentClassName;
+ private final Map<String, Builder> classes = new HashMap<>();
+
+ public SmaliBuilder() {
+ // No default class.
+ }
+
+ public SmaliBuilder(String name) {
+ addClass(name);
+ }
+
+ public SmaliBuilder(String name, String superName) {
+ addClass(name, superName);
+ }
+
+ private List<String> getSource(String clazz) {
+ return classes.get(clazz).source;
+ }
+
+ public String getCurrentClassName() {
+ return currentClassName;
+ }
+
+ public String getCurrentClassDescriptor() {
+ return DescriptorUtils.javaTypeToDescriptor(currentClassName);
+ }
+
+ public void addClass(String name) {
+ addClass(name, "java.lang.Object");
+ }
+
+ public void addClass(String name, String superName) {
+ addClass(name, superName, ImmutableList.of());
+ }
+
+ public void addClass(String name, String superName, List<String> implementedInterfaces) {
+ assert !classes.containsKey(name);
+ currentClassName = name;
+ classes.put(name, new ClassBuilder(name, superName, implementedInterfaces));
+ }
+
+ public void addInterface(String name) {
+ addInterface(name, "java.lang.Object");
+ }
+
+ public void addInterface(String name, String superName) {
+ assert !classes.containsKey(name);
+ currentClassName = name;
+ classes.put(name, new InterfaceBuilder(name, superName));
+ }
+
+ public void setSourceFile(String file) {
+ classes.get(currentClassName).sourceFile = file;
+ }
+
+ public void addDefaultConstructor() {
+ String superDescriptor =
+ DescriptorUtils.javaTypeToDescriptor(classes.get(currentClassName).superName);
+ addMethodRaw(
+ " .method public constructor <init>()V",
+ " .locals 0",
+ " invoke-direct {p0}, " + superDescriptor + "-><init>()V",
+ " return-void",
+ " .end method"
+ );
+ }
+
+ public void addStaticField(String name, String type, String defaultValue) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(".field static ");
+ builder.append(name);
+ builder.append(":");
+ builder.append(type);
+ if (defaultValue != null) {
+ builder.append(" = ");
+ if (type.equals("Ljava/lang/String;")) {
+ builder.append('"');
+ builder.append(defaultValue);
+ builder.append('"');
+ } else {
+ builder.append(defaultValue);
+ }
+ }
+ getSource(currentClassName).add(builder.toString());
+ }
+
+ public void addStaticField(String name, String type) {
+ addStaticField(name, type, null);
+ }
+
+ public void addInstanceField(String name, String type) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(".field ");
+ builder.append(name);
+ builder.append(":");
+ builder.append(type);
+ getSource(currentClassName).add(builder.toString());
+ }
+
+ private MethodSignature addMethod(String flags, String returnType, String name,
+ List<String> parameters, int locals, String code) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(".method ");
+ if (flags != null && flags.length() > 0) {
+ builder.append(flags);
+ builder.append(" ");
+ }
+ builder.append(name);
+ builder.append("(");
+ for (String parameter : parameters) {
+ builder.append(DescriptorUtils.javaTypeToDescriptor(parameter));
+ }
+ builder.append(")");
+ builder.append(DescriptorUtils.javaTypeToDescriptor(returnType));
+ builder.append("\n");
+ if (locals >= 0) {
+ builder.append(".locals ");
+ builder.append(locals);
+ builder.append("\n\n");
+ assert code != null;
+ builder.append(code);
+ } else {
+ assert code == null;
+ }
+ builder.append(".end method");
+ getSource(currentClassName).add(builder.toString());
+ return new MethodSignature(currentClassName, name, returnType, parameters);
+ }
+
+ public MethodSignature addStaticMethod(String returnType, String name, List<String> parameters,
+ int locals, String... instructions) {
+ StringBuilder builder = new StringBuilder();
+ for (String instruction : instructions) {
+ builder.append(instruction);
+ builder.append("\n");
+ }
+ return addStaticMethod(returnType, name, parameters, locals, builder.toString());
+ }
+
+ public MethodSignature addStaticMethod(String returnType, String name, List<String> parameters,
+ int locals, String code) {
+ return addStaticMethod("", returnType, name, parameters, locals, code);
+ }
+
+ public MethodSignature addStaticInitializer(int locals, String... instructions) {
+ StringBuilder builder = new StringBuilder();
+ for (String instruction : instructions) {
+ builder.append(instruction);
+ builder.append("\n");
+ }
+ return addStaticInitializer(locals, builder.toString());
+ }
+
+ public MethodSignature addStaticInitializer(int locals, String code) {
+ return addStaticMethod("constructor", "void", "<clinit>", ImmutableList.of(), locals, code);
+ }
+
+ private MethodSignature addStaticMethod(String flags, String returnType, String name,
+ List<String> parameters, int locals, String code) {
+ StringBuilder builder = new StringBuilder();
+ return addMethod("public static " + flags, returnType, name, parameters, locals, code);
+ }
+
+ public MethodSignature addAbstractMethod(
+ String returnType, String name, List<String> parameters) {
+ return addMethod("public abstract", returnType, name, parameters, -1, null);
+ }
+
+ public MethodSignature addInstanceMethod(String returnType, String name,
+ List<String> parameters,
+ int locals, String... instructions) {
+ StringBuilder builder = new StringBuilder();
+ for (String instruction : instructions) {
+ builder.append(instruction);
+ builder.append("\n");
+ }
+ return addInstanceMethod(returnType, name, parameters, locals, builder.toString());
+ }
+
+ public MethodSignature addInstanceMethod(String returnType, String name,
+ List<String> parameters,
+ int locals, String code) {
+ return addMethod("public", returnType, name, parameters, locals, code);
+ }
+
+ public MethodSignature addMainMethod(int locals, String... instructions) {
+ return addStaticMethod(
+ "void", "main", Collections.singletonList("java.lang.String[]"), locals, instructions);
+ }
+
+ public void addMethodRaw(String... source) {
+ StringBuilder builder = new StringBuilder();
+ for (String line : source) {
+ builder.append(line);
+ builder.append("\n");
+ }
+ getSource(currentClassName).add(builder.toString());
+ }
+
+ public List<String> buildSource() {
+ List<String> result = new ArrayList<>(classes.size());
+ for (String clazz : classes.keySet()) {
+ Builder classBuilder = classes.get(clazz);
+ result.add(classBuilder.toString());
+ }
+ return result;
+ }
+
+ public byte[] compile()
+ throws IOException, RecognitionException, DexOverflowException, ExecutionException {
+ return Smali.compile(buildSource());
+ }
+
+ public AndroidApp build()
+ throws IOException, RecognitionException, DexOverflowException, ExecutionException {
+ return AndroidApp.fromDexProgramData(compile());
+ }
+
+
+ @Override
+ public String toString() {
+ return String.join("\n\n", buildSource());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java b/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java
index b35e768..3fe8da9 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java
@@ -6,16 +6,12 @@
import static org.junit.Assert.assertEquals;
-import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.errors.DexOverflowException;
-import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.SmaliWriter;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Smali;
-import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
import java.io.IOException;
import java.util.Collections;
import java.util.concurrent.ExecutionException;
@@ -27,12 +23,7 @@
// Run the provided smali through R8 smali disassembler and expect the exact same output.
void roundTripRawSmali(String smali) {
try {
- DexApplication application =
- new ApplicationReader(
- AndroidApp.fromDexProgramData(Smali.compile(smali)),
- new InternalOptions(),
- new Timing("SmaliTest"))
- .read();
+ AndroidApp application = AndroidApp.fromDexProgramData(Smali.compile(smali));
assertEquals(smali, SmaliWriter.smali(application, new InternalOptions()));
} catch (IOException | RecognitionException | ExecutionException | DexOverflowException e) {
throw new RuntimeException(e);
@@ -41,7 +32,7 @@
@Test
public void simpleSmokeTest() {
- DexApplication application = singleMethodApplication(
+ AndroidApp application = singleMethodApplication(
"int", Collections.singletonList("int"),
4,
" const/4 v0, 1 ",
@@ -81,7 +72,7 @@
@Test
public void sparseSwitchTest() {
- DexApplication application = singleMethodApplication(
+ AndroidApp application = singleMethodApplication(
"int", Collections.singletonList("int"),
0,
" sparse-switch v0, :sparse_switch_data",
@@ -137,7 +128,7 @@
@Test
public void packedSwitchTest() {
- DexApplication application = singleMethodApplication(
+ AndroidApp application = singleMethodApplication(
"int", Collections.singletonList("int"),
0,
" packed-switch v0, :packed_switch_data",
@@ -193,7 +184,7 @@
@Test
public void fillArrayDataTest8Bit() {
- DexApplication application = singleMethodApplication(
+ AndroidApp application = singleMethodApplication(
"int[]", ImmutableList.of(),
2,
" const/4 v1, 3",
@@ -236,7 +227,7 @@
@Test
public void fillArrayDataTest16Bit() {
- DexApplication application = singleMethodApplication(
+ AndroidApp application = singleMethodApplication(
"int[]", ImmutableList.of(),
2,
" const/4 v1, 3",
@@ -279,7 +270,7 @@
@Test
public void fillArrayDataTest32Bit() {
- DexApplication application = singleMethodApplication(
+ AndroidApp application = singleMethodApplication(
"int[]", ImmutableList.of(),
2,
" const/4 v1, 3",
@@ -322,7 +313,7 @@
@Test
public void fillArrayDataTest64Bit() {
- DexApplication application = singleMethodApplication(
+ AndroidApp application = singleMethodApplication(
"int[]", ImmutableList.of(),
2,
" const/4 v1, 3",
@@ -368,8 +359,7 @@
SmaliBuilder builder = new SmaliBuilder();
builder.addInterface("Test");
builder.addAbstractMethod("int", "test", ImmutableList.of());
- DexApplication application = buildApplication(builder);
- assertEquals(1, Iterables.size(application.classes()));
+ AndroidApp application = buildApplication(builder);
String expected =
".class public interface abstract LTest;\n" +
@@ -391,8 +381,7 @@
SmaliBuilder builder = new SmaliBuilder();
builder.addClass("Test", "java.lang.Object", ImmutableList.of("java.util.List"));
builder.addAbstractMethod("int", "test", ImmutableList.of());
- DexApplication application = buildApplication(builder);
- assertEquals(1, Iterables.size(application.classes()));
+ AndroidApp application = buildApplication(builder);
String expected =
".class public LTest;\n" +
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
index 2d2f887..4949153 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -8,8 +8,8 @@
import com.android.tools.r8.CompilationException;
import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.R8;
import com.android.tools.r8.R8Command;
+import com.android.tools.r8.Resource;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.dex.ApplicationReader;
@@ -18,39 +18,21 @@
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InstructionListIterator;
-import com.android.tools.r8.ir.code.ValueNumberGenerator;
-import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.FilteredClassPath;
import com.android.tools.r8.shaking.ProguardConfiguration;
+import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.AndroidAppOutputSink;
-import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OutputMode;
-import com.android.tools.r8.utils.Smali;
-import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.Timing;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
+import java.util.Collection;
import java.util.List;
-import java.util.ListIterator;
-import java.util.Map;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executors;
import java.util.function.Consumer;
import org.antlr.runtime.RecognitionException;
@@ -60,414 +42,20 @@
public static final String DEFAULT_MAIN_CLASS_NAME = DEFAULT_CLASS_NAME;
public static final String DEFAULT_METHOD_NAME = "method";
- public static class MethodSignature {
-
- public final String clazz;
- public final String name;
- public final String returnType;
- public final List<String> parameterTypes;
-
- public MethodSignature(String clazz, String name, String returnType,
- List<String> parameterTypes) {
- this.clazz = clazz;
- this.name = name;
- this.returnType = returnType;
- this.parameterTypes = parameterTypes;
- }
-
- public static MethodSignature staticInitializer(String clazz) {
- return new MethodSignature(clazz, "<clinit>", "void", ImmutableList.of());
- }
-
- @Override
- public String toString() {
- return returnType + " " + clazz + "." + name
- + "(" + StringUtils.join(parameterTypes, ",") + ")";
- }
- }
-
- public static class SmaliBuilder {
-
- abstract class Builder {
-
- String name;
- String superName;
- List<String> implementedInterfaces;
- String sourceFile = null;
- List<String> source = new ArrayList<>();
-
- Builder(String name, String superName, List<String> implementedInterfaces) {
- this.name = name;
- this.superName = superName;
- this.implementedInterfaces = implementedInterfaces;
- }
-
- protected void appendSuper(StringBuilder builder) {
- builder.append(".super ");
- builder.append(DescriptorUtils.javaTypeToDescriptor(superName));
- builder.append("\n");
- }
-
- protected void appendImplementedInterfaces(StringBuilder builder) {
- for (String implementedInterface : implementedInterfaces) {
- builder.append(".implements ");
- builder.append(DescriptorUtils.javaTypeToDescriptor(implementedInterface));
- builder.append("\n");
- }
- }
-
- protected void writeSource(StringBuilder builder) {
- for (String sourceLine : source) {
- builder.append(sourceLine);
- builder.append("\n");
- }
- }
- }
-
- public class ClassBuilder extends Builder {
-
- ClassBuilder(String name, String superName, List<String> implementedInterfaces) {
- super(name, superName, implementedInterfaces);
- }
-
- public String toString() {
- StringBuilder builder = new StringBuilder();
- builder.append(".class public ");
- builder.append(DescriptorUtils.javaTypeToDescriptor(name));
- builder.append("\n");
- appendSuper(builder);
- appendImplementedInterfaces(builder);
- builder.append("\n");
- if (sourceFile != null) {
- builder.append(".source \"").append(sourceFile).append("\"\n");
- }
- writeSource(builder);
- return builder.toString();
- }
- }
-
- public class InterfaceBuilder extends Builder {
-
- InterfaceBuilder(String name, String superName) {
- super(name, superName, ImmutableList.of());
- }
-
- public String toString() {
- StringBuilder builder = new StringBuilder();
- builder.append(".class public interface abstract ");
- builder.append(DescriptorUtils.javaTypeToDescriptor(name));
- builder.append("\n");
- appendSuper(builder);
- appendImplementedInterfaces(builder);
- builder.append("\n");
- writeSource(builder);
- return builder.toString();
- }
- }
-
- private String currentClassName;
- private final Map<String, Builder> classes = new HashMap<>();
-
- public SmaliBuilder() {
- // No default class.
- }
-
- public SmaliBuilder(String name) {
- addClass(name);
- }
-
- public SmaliBuilder(String name, String superName) {
- addClass(name, superName);
- }
-
- private List<String> getSource(String clazz) {
- return classes.get(clazz).source;
- }
-
- public String getCurrentClassName() {
- return currentClassName;
- }
-
- public String getCurrentClassDescriptor() {
- return DescriptorUtils.javaTypeToDescriptor(currentClassName);
- }
-
- public void addClass(String name) {
- addClass(name, "java.lang.Object");
- }
-
- public void addClass(String name, String superName) {
- addClass(name, superName, ImmutableList.of());
- }
-
- public void addClass(String name, String superName, List<String> implementedInterfaces) {
- assert !classes.containsKey(name);
- currentClassName = name;
- classes.put(name, new ClassBuilder(name, superName, implementedInterfaces));
- }
-
- public void addInterface(String name) {
- addInterface(name, "java.lang.Object");
- }
-
- public void addInterface(String name, String superName) {
- assert !classes.containsKey(name);
- currentClassName = name;
- classes.put(name, new InterfaceBuilder(name, superName));
- }
-
- public void setSourceFile(String file) {
- classes.get(currentClassName).sourceFile = file;
- }
-
- public void addDefaultConstructor() {
- String superDescriptor =
- DescriptorUtils.javaTypeToDescriptor(classes.get(currentClassName).superName);
- addMethodRaw(
- " .method public constructor <init>()V",
- " .locals 0",
- " invoke-direct {p0}, " + superDescriptor + "-><init>()V",
- " return-void",
- " .end method"
- );
- }
-
- public void addStaticField(String name, String type, String defaultValue) {
- StringBuilder builder = new StringBuilder();
- builder.append(".field static ");
- builder.append(name);
- builder.append(":");
- builder.append(type);
- if (defaultValue != null) {
- builder.append(" = ");
- if (type.equals("Ljava/lang/String;")) {
- builder.append('"');
- builder.append(defaultValue);
- builder.append('"');
- } else {
- builder.append(defaultValue);
- }
- }
- getSource(currentClassName).add(builder.toString());
- }
-
- public void addStaticField(String name, String type) {
- addStaticField(name, type, null);
- }
-
- public void addInstanceField(String name, String type) {
- StringBuilder builder = new StringBuilder();
- builder.append(".field ");
- builder.append(name);
- builder.append(":");
- builder.append(type);
- getSource(currentClassName).add(builder.toString());
- }
-
- private MethodSignature addMethod(String flags, String returnType, String name,
- List<String> parameters, int locals, String code) {
- StringBuilder builder = new StringBuilder();
- builder.append(".method ");
- if (flags != null && flags.length() > 0) {
- builder.append(flags);
- builder.append(" ");
- }
- builder.append(name);
- builder.append("(");
- for (String parameter : parameters) {
- builder.append(DescriptorUtils.javaTypeToDescriptor(parameter));
- }
- builder.append(")");
- builder.append(DescriptorUtils.javaTypeToDescriptor(returnType));
- builder.append("\n");
- if (locals >= 0) {
- builder.append(".locals ");
- builder.append(locals);
- builder.append("\n\n");
- assert code != null;
- builder.append(code);
- } else {
- assert code == null;
- }
- builder.append(".end method");
- getSource(currentClassName).add(builder.toString());
- return new MethodSignature(currentClassName, name, returnType, parameters);
- }
-
- public MethodSignature addStaticMethod(String returnType, String name, List<String> parameters,
- int locals, String... instructions) {
- StringBuilder builder = new StringBuilder();
- for (String instruction : instructions) {
- builder.append(instruction);
- builder.append("\n");
- }
- return addStaticMethod(returnType, name, parameters, locals, builder.toString());
- }
-
- public MethodSignature addStaticMethod(String returnType, String name, List<String> parameters,
- int locals, String code) {
- return addStaticMethod("", returnType, name, parameters, locals, code);
- }
-
- public MethodSignature addStaticInitializer(int locals, String... instructions) {
- StringBuilder builder = new StringBuilder();
- for (String instruction : instructions) {
- builder.append(instruction);
- builder.append("\n");
- }
- return addStaticInitializer(locals, builder.toString());
- }
-
- public MethodSignature addStaticInitializer(int locals, String code) {
- return addStaticMethod("constructor", "void", "<clinit>", ImmutableList.of(), locals, code);
- }
-
- private MethodSignature addStaticMethod(String flags, String returnType, String name,
- List<String> parameters, int locals, String code) {
- StringBuilder builder = new StringBuilder();
- return addMethod("public static " + flags, returnType, name, parameters, locals, code);
- }
-
- public MethodSignature addAbstractMethod(
- String returnType, String name, List<String> parameters) {
- return addMethod("public abstract", returnType, name, parameters, -1, null);
- }
-
- public MethodSignature addInstanceMethod(String returnType, String name,
- List<String> parameters,
- int locals, String... instructions) {
- StringBuilder builder = new StringBuilder();
- for (String instruction : instructions) {
- builder.append(instruction);
- builder.append("\n");
- }
- return addInstanceMethod(returnType, name, parameters, locals, builder.toString());
- }
-
- public MethodSignature addInstanceMethod(String returnType, String name,
- List<String> parameters,
- int locals, String code) {
- return addMethod("public", returnType, name, parameters, locals, code);
- }
-
- public MethodSignature addMainMethod(int locals, String... instructions) {
- return addStaticMethod(
- "void", "main", Collections.singletonList("java.lang.String[]"), locals, instructions);
- }
-
- public void addMethodRaw(String... source) {
- StringBuilder builder = new StringBuilder();
- for (String line : source) {
- builder.append(line);
- builder.append("\n");
- }
- getSource(currentClassName).add(builder.toString());
- }
-
- public List<String> buildSource() {
- List<String> result = new ArrayList<>(classes.size());
- for (String clazz : classes.keySet()) {
- Builder classBuilder = classes.get(clazz);
- result.add(classBuilder.toString());
- }
- return result;
- }
-
- public byte[] compile()
- throws IOException, RecognitionException, DexOverflowException, ExecutionException {
- return Smali.compile(buildSource());
- }
-
- public AndroidApp build()
- throws IOException, RecognitionException, DexOverflowException, ExecutionException {
- return AndroidApp.fromDexProgramData(compile());
- }
-
-
- @Override
- public String toString() {
- return String.join("\n\n", buildSource());
- }
- }
-
- public class TestApplication {
-
- public final DexApplication application;
- public final DexEncodedMethod method;
- public final IRCode code;
- public final List<IRCode> additionalCode;
- public final ValueNumberGenerator valueNumberGenerator;
- public final InternalOptions options;
-
- public TestApplication(
- DexApplication application,
- DexEncodedMethod method,
- IRCode code,
- ValueNumberGenerator valueNumberGenerator,
- InternalOptions options) {
- this(application, method, code, null, valueNumberGenerator, options);
- }
-
- public TestApplication(
- DexApplication application,
- DexEncodedMethod method,
- IRCode code,
- List<IRCode> additionalCode,
- ValueNumberGenerator valueNumberGenerator,
- InternalOptions options) {
- this.application = application;
- this.method = method;
- this.code = code;
- this.additionalCode = additionalCode;
- this.valueNumberGenerator = valueNumberGenerator;
- this.options = options;
- }
-
- public int countArgumentInstructions() {
- int count = 0;
- ListIterator<Instruction> iterator = code.blocks.get(0).listIterator();
- while (iterator.next().isArgument()) {
- count++;
- }
- return count;
- }
-
- public InstructionListIterator listIteratorAt(BasicBlock block, int index) {
- InstructionListIterator iterator = block.listIterator();
- for (int i = 0; i < index; i++) {
- iterator.next();
- }
- return iterator;
- }
-
- public String run() throws DexOverflowException {
- AppInfo appInfo = new AppInfo(application);
- IRConverter converter = new IRConverter(appInfo, options);
- converter.replaceCodeForTesting(method, code);
- return runArt(application, options);
- }
- }
-
- protected DexApplication buildApplication(SmaliBuilder builder) {
- return buildApplication(builder, new InternalOptions());
- }
-
- protected DexApplication buildApplication(SmaliBuilder builder, InternalOptions options) {
+ protected AndroidApp buildApplication(SmaliBuilder builder) {
try {
- return buildApplication(AndroidApp.fromDexProgramData(builder.compile()), options);
- } catch (IOException | RecognitionException | ExecutionException | DexOverflowException e) {
+ return AndroidApp.fromDexProgramData(builder.compile());
+ } catch (IOException | RecognitionException | DexOverflowException | ExecutionException e) {
throw new RuntimeException(e);
}
}
- protected DexApplication buildApplicationWithAndroidJar(
- SmaliBuilder builder, InternalOptions options) {
+ protected AndroidApp buildApplicationWithAndroidJar(SmaliBuilder builder) {
try {
- AndroidApp input = AndroidApp.builder()
+ return AndroidApp.builder()
.addDexProgramData(builder.compile())
.addLibraryFiles(FilteredClassPath.unfiltered(ToolHelper.getDefaultAndroidJar()))
.build();
- return buildApplication(input, options);
} catch (IOException | RecognitionException | ExecutionException | DexOverflowException e) {
throw new RuntimeException(e);
}
@@ -482,10 +70,15 @@
}
}
- protected DexApplication processApplication(DexApplication application, InternalOptions options) {
+ protected AndroidApp processApplication(AndroidApp application) {
+ return processApplication(application, null);
+ }
+
+ protected AndroidApp processApplication(AndroidApp application,
+ Consumer<InternalOptions> optionsConsumer) {
try {
- return ToolHelper.optimizeWithR8(application, options);
- } catch (IOException | CompilationException | ExecutionException e) {
+ return ToolHelper.runR8(application, optionsConsumer);
+ } catch (IOException | CompilationException e) {
throw new RuntimeException(e);
}
}
@@ -535,29 +128,6 @@
}
}
- protected DexEncodedMethod getMethod(
- DexInspector inspector,
- String className,
- String returnType,
- String methodName,
- List<String> parameters) {
- ClassSubject clazz = inspector.clazz(className);
- assertTrue(clazz.isPresent());
- MethodSubject method = clazz.method(returnType, methodName, parameters);
- assertTrue(method.isPresent());
- return method.getMethod();
- }
-
- protected DexEncodedMethod getMethod(
- DexApplication application,
- String className,
- String returnType,
- String methodName,
- List<String> parameters) {
- DexInspector inspector = new DexInspector(application);
- return getMethod(inspector, className, returnType, methodName, parameters);
- }
-
protected DexEncodedMethod getMethod(Path appPath, MethodSignature signature) {
try {
DexInspector inspector = new DexInspector(appPath);
@@ -572,7 +142,7 @@
}
}
- protected DexEncodedMethod getMethod(DexApplication application, MethodSignature signature) {
+ protected DexEncodedMethod getMethod(AndroidApp application, MethodSignature signature) {
return getMethod(application,
signature.clazz, signature.returnType, signature.name, signature.parameterTypes);
}
@@ -588,16 +158,45 @@
* @param instructions instructions for the method
* @return the processed method for inspection
*/
- public DexApplication singleMethodApplication(String returnType, List<String> parameters,
+ public AndroidApp singleMethodApplication(String returnType, List<String> parameters,
int locals, String... instructions) {
// Build a one class method.
SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
builder.addStaticMethod(returnType, DEFAULT_METHOD_NAME, parameters, locals, instructions);
// Read the one class method as an application.
- DexApplication application = buildApplication(builder);
- assertEquals(1, Iterables.size(application.classes()));
- return application;
+ return buildApplication(builder);
+ }
+
+ private int getNumberOfClassesForResources(Iterable<Resource> resources) {
+ int count = 0;
+ for (Resource resource : resources) {
+ Collection<String> descriptors = resource.getClassDescriptors();
+ if (descriptors == null) {
+ throw new IllegalStateException("Cannot count classes in application without descriptors.");
+ }
+ count += descriptors.size();
+ }
+ return count;
+ }
+
+ protected int getNumberOfProgramClasses(AndroidApp application) {
+ try {
+ return getNumberOfClassesForResources(application.getClassProgramResources())
+ + getNumberOfClassesForResources(application.getDexProgramResources());
+ } catch (IOException e) {
+ return -1;
+ }
+ }
+
+ protected AppInfo getAppInfo(AndroidApp application) {
+ try {
+ DexApplication dexApplication = new ApplicationReader(application, new InternalOptions(),
+ new Timing("SmaliTest.getAppInfo")).read();
+ return new AppInfo(dexApplication);
+ } catch (IOException | ExecutionException e) {
+ throw new RuntimeException(e);
+ }
}
/**
@@ -616,30 +215,29 @@
InternalOptions options = new InternalOptions();
// Build a one class application.
- DexApplication application = singleMethodApplication(
+ AndroidApp application = singleMethodApplication(
returnType, parameters, locals, instructions);
// Process the application with R8.
- DexApplication processdApplication = processApplication(application, options);
- assertEquals(1, Iterables.size(processdApplication.classes()));
+ AndroidApp processdApplication = processApplication(application);
+ assertEquals(1, getNumberOfProgramClasses(processdApplication));
// Return the processed method for inspection.
return getMethod(
processdApplication, DEFAULT_CLASS_NAME, returnType, DEFAULT_METHOD_NAME, parameters);
}
- public String runArt(DexApplication application, InternalOptions options)
- throws DexOverflowException {
- return runArt(application, options, DEFAULT_MAIN_CLASS_NAME);
+ public String runArt(AndroidApp application) throws DexOverflowException {
+ return runArt(application, DEFAULT_MAIN_CLASS_NAME);
}
- public String runArt(DexApplication application, InternalOptions options, String mainClass)
+
+ public String runArt(AndroidApp application, String mainClass)
throws DexOverflowException {
try {
- AndroidApp app = writeDex(application, options);
Path out = temp.getRoot().toPath().resolve("run-art-input.zip");
// TODO(sgjesse): Pass in a unique temp directory for each run.
- app.writeToZip(out, OutputMode.Indexed);
+ application.writeToZip(out, OutputMode.Indexed);
return ToolHelper.runArtNoVerificationErrors(out.toString(), mainClass);
} catch (IOException e) {
throw new RuntimeException(e);
@@ -654,36 +252,15 @@
}
}
- public void runDex2Oat(DexApplication application, InternalOptions options)
+ public void runDex2Oat(AndroidApp application)
throws DexOverflowException {
try {
- AndroidApp app = writeDex(application, options);
Path dexOut = temp.getRoot().toPath().resolve("run-dex2oat-input.zip");
Path oatFile = temp.getRoot().toPath().resolve("oat-file");
- app.writeToZip(dexOut, OutputMode.Indexed);
+ application.writeToZip(dexOut, OutputMode.Indexed);
ToolHelper.runDex2Oat(dexOut, oatFile);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
-
- public AndroidApp writeDex(DexApplication application, InternalOptions options)
- throws DexOverflowException {
- try {
- AndroidAppOutputSink compatSink = new AndroidAppOutputSink();
- R8.writeApplication(
- Executors.newSingleThreadExecutor(),
- application,
- compatSink,
- null,
- NamingLens.getIdentityLens(),
- null,
- options);
- compatSink.close();
- return compatSink.build();
- } catch (ExecutionException | IOException e) {
- throw new RuntimeException(e);
- }
- }
-
}
diff --git a/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java b/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java
deleted file mode 100644
index 3fc329c..0000000
--- a/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java
+++ /dev/null
@@ -1,570 +0,0 @@
-// Copyright (c) 2016, 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.smali;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.code.Const;
-import com.android.tools.r8.code.Const4;
-import com.android.tools.r8.code.ConstHigh16;
-import com.android.tools.r8.code.IfEq;
-import com.android.tools.r8.code.IfEqz;
-import com.android.tools.r8.code.Instruction;
-import com.android.tools.r8.code.PackedSwitch;
-import com.android.tools.r8.code.SparseSwitch;
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexCode;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.jasmin.JasminBuilder;
-import com.android.tools.r8.shaking.FilteredClassPath;
-import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.MethodSubject;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.StringUtils;
-import com.google.common.collect.ImmutableList;
-import java.util.List;
-import java.util.stream.Collectors;
-import org.junit.Test;
-
-public class SwitchRewritingTest extends SmaliTestBase {
-
- private boolean twoCaseWillUsePackedSwitch(int key1, int key2) {
- assert key1 != key2;
- return Math.abs((long) key1 - (long) key2) == 1;
- }
-
- private boolean some16BitConst(Instruction instruction) {
- return instruction instanceof Const4
- || instruction instanceof ConstHigh16
- || instruction instanceof Const;
- }
- private void runSingleCaseDexTest(boolean packed, int key) {
- SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
- String switchInstruction;
- String switchData;
- if (packed) {
- switchInstruction = "packed-switch";
- switchData = StringUtils.join(
- "\n",
- " :switch_data",
- " .packed-switch " + key,
- " :case_0",
- " .end packed-switch");
- } else {
- switchInstruction = "sparse-switch";
- switchData = StringUtils.join(
- "\n",
- " :switch_data",
- " .sparse-switch",
- " " + key + " -> :case_0",
- " .end sparse-switch");
- }
- MethodSignature signature = builder.addStaticMethod(
- "int",
- DEFAULT_METHOD_NAME,
- ImmutableList.of("int"),
- 0,
- " " + switchInstruction + " p0, :switch_data",
- " const/4 p0, 0x5",
- " goto :return",
- " :case_0",
- " const/4 p0, 0x3",
- " :return",
- " return p0",
- switchData);
-
- builder.addMainMethod(
- 2,
- " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
- " const/4 v1, 0",
- " invoke-static { v1 }, LTest;->method(I)I",
- " move-result v1",
- " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
- " return-void"
- );
-
- InternalOptions options = new InternalOptions();
- DexApplication originalApplication = buildApplication(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
- DexEncodedMethod method = getMethod(processedApplication, signature);
- DexCode code = method.getCode().asDexCode();
-
- if (key == 0) {
- assertEquals(5, code.instructions.length);
- assertTrue(code.instructions[0] instanceof IfEqz);
- } else {
- assertEquals(6, code.instructions.length);
- assertTrue(some16BitConst(code.instructions[0]));
- assertTrue(code.instructions[1] instanceof IfEq);
- }
- }
-
- @Test
- public void singleCaseDex() {
- for (boolean packed : new boolean[]{true, false}) {
- runSingleCaseDexTest(packed, Integer.MIN_VALUE);
- runSingleCaseDexTest(packed, -1);
- runSingleCaseDexTest(packed, 0);
- runSingleCaseDexTest(packed, 1);
- runSingleCaseDexTest(packed, Integer.MAX_VALUE);
- }
- }
-
- private void runTwoCaseSparseToPackedOrIfsDexTest(int key1, int key2) {
- SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
-
- MethodSignature signature = builder.addStaticMethod(
- "int",
- DEFAULT_METHOD_NAME,
- ImmutableList.of("int"),
- 0,
- " sparse-switch p0, :sparse_switch_data",
- " const/4 v0, 0x5",
- " goto :return",
- " :case_1",
- " const/4 v0, 0x3",
- " goto :return",
- " :case_2",
- " const/4 v0, 0x4",
- " :return",
- " return v0",
- " :sparse_switch_data",
- " .sparse-switch",
- " " + key1 + " -> :case_1",
- " " + key2 + " -> :case_2",
- " .end sparse-switch");
-
- builder.addMainMethod(
- 2,
- " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
- " const/4 v1, 0",
- " invoke-static { v1 }, LTest;->method(I)I",
- " move-result v1",
- " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
- " return-void"
- );
-
- InternalOptions options = new InternalOptions();
- DexApplication originalApplication = buildApplication(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
- DexEncodedMethod method = getMethod(processedApplication, signature);
- DexCode code = method.getCode().asDexCode();
- if (twoCaseWillUsePackedSwitch(key1, key2)) {
- assertTrue(code.instructions[0] instanceof PackedSwitch);
- } else {
- if (key1 == 0) {
- assertTrue(code.instructions[0] instanceof IfEqz);
- } else {
- // Const instruction before if.
- assertTrue(code.instructions[1] instanceof IfEq);
- }
- }
- }
-
- @Test
- public void twoCaseSparseToPackedOrIfsDex() {
- for (int delta = 1; delta <= 3; delta++) {
- runTwoCaseSparseToPackedOrIfsDexTest(0, delta);
- runTwoCaseSparseToPackedOrIfsDexTest(-delta, 0);
- runTwoCaseSparseToPackedOrIfsDexTest(Integer.MIN_VALUE, Integer.MIN_VALUE + delta);
- runTwoCaseSparseToPackedOrIfsDexTest(Integer.MAX_VALUE - delta, Integer.MAX_VALUE);
- }
- runTwoCaseSparseToPackedOrIfsDexTest(-1, 1);
- runTwoCaseSparseToPackedOrIfsDexTest(-2, 1);
- runTwoCaseSparseToPackedOrIfsDexTest(-1, 2);
- runTwoCaseSparseToPackedOrIfsDexTest(Integer.MIN_VALUE, Integer.MAX_VALUE);
- runTwoCaseSparseToPackedOrIfsDexTest(Integer.MIN_VALUE + 1, Integer.MAX_VALUE);
- runTwoCaseSparseToPackedOrIfsDexTest(Integer.MIN_VALUE, Integer.MAX_VALUE - 1);
- }
-
- private void runLargerSwitchDexTest(int firstKey, int keyStep, int totalKeys,
- Integer additionalLastKey) throws Exception {
- SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
-
- StringBuilder switchSource = new StringBuilder();
- StringBuilder targetCode = new StringBuilder();
- for (int i = 0; i < totalKeys; i++) {
- String caseLabel = "case_" + i;
- switchSource.append(" " + (firstKey + i * keyStep) + " -> :" + caseLabel + "\n");
- targetCode.append(" :" + caseLabel + "\n");
- targetCode.append(" goto :return\n");
- }
- if (additionalLastKey != null) {
- String caseLabel = "case_" + totalKeys;
- switchSource.append(" " + additionalLastKey + " -> :" + caseLabel + "\n");
- targetCode.append(" :" + caseLabel + "\n");
- targetCode.append(" goto :return\n");
- }
-
- MethodSignature signature = builder.addStaticMethod(
- "void",
- DEFAULT_METHOD_NAME,
- ImmutableList.of("int"),
- 0,
- " sparse-switch p0, :sparse_switch_data",
- " goto :return",
- targetCode.toString(),
- " :return",
- " return-void",
- " :sparse_switch_data",
- " .sparse-switch",
- switchSource.toString(),
- " .end sparse-switch");
-
- builder.addMainMethod(
- 2,
- " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
- " const/4 v1, 0",
- " invoke-static { v1 }, LTest;->method(I)V",
- " return-void"
- );
-
- InternalOptions options = new InternalOptions();
- options.verbose = true;
- options.printTimes = true;
- DexApplication originalApplication = buildApplication(builder, options);
- DexApplication processedApplication = processApplication(originalApplication, options);
- DexEncodedMethod method = getMethod(processedApplication, signature);
- DexCode code = method.getCode().asDexCode();
- if (keyStep <= 2) {
- assertTrue(code.instructions[0] instanceof PackedSwitch);
- } else {
- assertTrue(code.instructions[0] instanceof SparseSwitch);
- }
- }
-
- @Test
- public void twoMonsterSparseToPackedDex() throws Exception {
- runLargerSwitchDexTest(0, 1, 100, null);
- runLargerSwitchDexTest(0, 2, 100, null);
- runLargerSwitchDexTest(0, 3, 100, null);
- runLargerSwitchDexTest(100, 100, 100, null);
- runLargerSwitchDexTest(-10000, 100, 100, null);
- runLargerSwitchDexTest(-10000, 200, 100, 10000);
- runLargerSwitchDexTest(
- Integer.MIN_VALUE, (int) ((-(long)Integer.MIN_VALUE) / 16), 32, Integer.MAX_VALUE);
-
- // TODO(63090177): Currently this is commented out as R8 gets really slow for large switches.
- // runLargerSwitchDexTest(0, 1, Constants.U16BIT_MAX, null);
- }
-
- private void runSingleCaseJarTest(boolean packed, int key) throws Exception {
- JasminBuilder builder = new JasminBuilder();
- JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
-
- String switchCode;
- if (packed) {
- switchCode = StringUtils.join(
- "\n",
- " tableswitch " + key,
- " case_0",
- " default : case_default");
- } else {
- switchCode = StringUtils.join(
- "\n",
- " lookupswitch",
- " " + key + " : case_0",
- " default : case_default");
- }
-
- clazz.addStaticMethod("test", ImmutableList.of("I"), "I",
- " .limit stack 1",
- " .limit locals 1",
- " iload 0",
- switchCode,
- " case_0:",
- " iconst_3",
- " goto return_",
- " case_default:",
- " ldc 5",
- " return_:",
- " ireturn");
-
- clazz.addMainMethod(
- " .limit stack 2",
- " .limit locals 1",
- " getstatic java/lang/System/out Ljava/io/PrintStream;",
- " ldc 2",
- " invokestatic Test/test(I)I",
- " invokevirtual java/io/PrintStream/print(I)V",
- " return");
-
- DexApplication app = builder.read();
- app = ToolHelper.optimizeWithR8(app, new InternalOptions());
-
- MethodSignature signature = new MethodSignature("Test", "test", "int", ImmutableList.of("int"));
- DexEncodedMethod method = getMethod(app, signature);
- DexCode code = method.getCode().asDexCode();
- if (key == 0) {
- assertEquals(5, code.instructions.length);
- assertTrue(code.instructions[0] instanceof IfEqz);
- } else {
- assertEquals(6, code.instructions.length);
- assertTrue(code.instructions[1] instanceof IfEq);
- }
- }
-
- @Test
- public void singleCaseJar() throws Exception {
- for (boolean packed : new boolean[]{true, false}) {
- runSingleCaseJarTest(packed, Integer.MIN_VALUE);
- runSingleCaseJarTest(packed, -1);
- runSingleCaseJarTest(packed, 0);
- runSingleCaseJarTest(packed, 1);
- runSingleCaseJarTest(packed, Integer.MAX_VALUE);
- }
- }
-
- private void runTwoCaseSparseToPackedJarTest(int key1, int key2) throws Exception {
- JasminBuilder builder = new JasminBuilder();
- JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
-
- clazz.addStaticMethod("test", ImmutableList.of("I"), "I",
- " .limit stack 1",
- " .limit locals 1",
- " iload 0",
- " lookupswitch",
- " " + key1 + " : case_1",
- " " + key2 + " : case_2",
- " default : case_default",
- " case_1:",
- " iconst_3",
- " goto return_",
- " case_2:",
- " iconst_4",
- " goto return_",
- " case_default:",
- " iconst_5",
- " return_:",
- " ireturn");
-
- clazz.addMainMethod(
- " .limit stack 2",
- " .limit locals 1",
- " getstatic java/lang/System/out Ljava/io/PrintStream;",
- " ldc 2",
- " invokestatic Test/test(I)I",
- " invokevirtual java/io/PrintStream/print(I)V",
- " return");
-
- DexApplication app = builder.read();
- app = ToolHelper.optimizeWithR8(app, new InternalOptions());
-
- MethodSignature signature = new MethodSignature("Test", "test", "int", ImmutableList.of("int"));
- DexEncodedMethod method = getMethod(app, signature);
- DexCode code = method.getCode().asDexCode();
- if (twoCaseWillUsePackedSwitch(key1, key2)) {
- assertTrue(code.instructions[0] instanceof PackedSwitch);
- } else {
- if (key1 == 0) {
- assertTrue(code.instructions[0] instanceof IfEqz);
- } else {
- // Const instruction before if.
- assertTrue(code.instructions[1] instanceof IfEq);
- }
- }
- }
-
- @Test
- public void twoCaseSparseToPackedJar() throws Exception {
- for (int delta = 1; delta <= 3; delta++) {
- runTwoCaseSparseToPackedJarTest(0, delta);
- runTwoCaseSparseToPackedJarTest(-delta, 0);
- runTwoCaseSparseToPackedJarTest(Integer.MIN_VALUE, Integer.MIN_VALUE + delta);
- runTwoCaseSparseToPackedJarTest(Integer.MAX_VALUE - delta, Integer.MAX_VALUE);
- }
- runTwoCaseSparseToPackedJarTest(-1, 1);
- runTwoCaseSparseToPackedJarTest(-2, 1);
- runTwoCaseSparseToPackedJarTest(-1, 2);
- runTwoCaseSparseToPackedJarTest(Integer.MIN_VALUE, Integer.MAX_VALUE);
- runTwoCaseSparseToPackedJarTest(Integer.MIN_VALUE + 1, Integer.MAX_VALUE);
- runTwoCaseSparseToPackedJarTest(Integer.MIN_VALUE, Integer.MAX_VALUE - 1);
- }
-
- private void runLargerSwitchJarTest(int firstKey, int keyStep, int totalKeys,
- Integer additionalLastKey) throws Exception {
- JasminBuilder builder = new JasminBuilder();
- JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
-
- StringBuilder switchSource = new StringBuilder();
- StringBuilder targetCode = new StringBuilder();
- for (int i = 0; i < totalKeys; i++) {
- String caseLabel = "case_" + i;
- switchSource.append(" " + (firstKey + i * keyStep) + " : " + caseLabel + "\n");
- targetCode.append(" " + caseLabel + ":\n");
- targetCode.append(" ldc " + i + "\n");
- targetCode.append(" goto return_\n");
- }
- if (additionalLastKey != null) {
- String caseLabel = "case_" + totalKeys;
- switchSource.append(" " + additionalLastKey + " : " + caseLabel + "\n");
- targetCode.append(" " + caseLabel + ":\n");
- targetCode.append(" ldc " + totalKeys + "\n");
- targetCode.append(" goto return_\n");
- }
-
- clazz.addStaticMethod("test", ImmutableList.of("I"), "I",
- " .limit stack 1",
- " .limit locals 1",
- " iload 0",
- " lookupswitch",
- switchSource.toString(),
- " default : case_default",
- targetCode.toString(),
- " case_default:",
- " iconst_5",
- " return_:",
- " ireturn");
-
- clazz.addMainMethod(
- " .limit stack 2",
- " .limit locals 1",
- " getstatic java/lang/System/out Ljava/io/PrintStream;",
- " ldc 2",
- " invokestatic Test/test(I)I",
- " invokevirtual java/io/PrintStream/print(I)V",
- " return");
-
- DexApplication app = builder.read();
- app = ToolHelper.optimizeWithR8(app, new InternalOptions());
-
- MethodSignature signature = new MethodSignature("Test", "test", "int", ImmutableList.of("int"));
- DexEncodedMethod method = getMethod(app, signature);
- DexCode code = method.getCode().asDexCode();
- int packedSwitchCount = 0;
- int sparseSwitchCount = 0;
- for (Instruction instruction : code.instructions) {
- if (instruction instanceof PackedSwitch) {
- packedSwitchCount++;
- }
- if (instruction instanceof SparseSwitch) {
- sparseSwitchCount++;
- }
- }
- if (keyStep <= 2) {
- assertEquals(1, packedSwitchCount);
- assertEquals(0, sparseSwitchCount);
- } else {
- assertEquals(0, packedSwitchCount);
- assertEquals(1, sparseSwitchCount);
- }
- }
-
- @Test
- public void largerSwitchJar() throws Exception {
- runLargerSwitchJarTest(0, 1, 100, null);
- runLargerSwitchJarTest(0, 2, 100, null);
- runLargerSwitchJarTest(0, 3, 100, null);
- runLargerSwitchJarTest(100, 100, 100, null);
- runLargerSwitchJarTest(-10000, 100, 100, null);
- runLargerSwitchJarTest(-10000, 200, 100, 10000);
- runLargerSwitchJarTest(
- Integer.MIN_VALUE, (int) ((-(long)Integer.MIN_VALUE) / 16), 32, Integer.MAX_VALUE);
-
- // This is the maximal value possible with Jasmin with the generated code above. It depends on
- // the source, so making smaller source can raise this limit. However we never get close to the
- // class file max.
- runLargerSwitchJarTest(0, 1, 5503, null);
- }
-
- private void runConvertCasesToIf(List<Integer> keys, int defaultValue, int expectedIfs,
- int expectedPackedSwitches, int expectedSparceSwitches) throws Exception {
- JasminBuilder builder = new JasminBuilder();
- JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
-
- StringBuilder x = new StringBuilder();
- StringBuilder y = new StringBuilder();
- for (Integer key : keys) {
- x.append(key).append(" : case_").append(key).append("\n");
- y.append("case_").append(key).append(":\n");
- y.append(" ldc ").append(key).append("\n");
- y.append(" goto return_\n");
- }
-
- clazz.addStaticMethod("test", ImmutableList.of("I"), "I",
- " .limit stack 1",
- " .limit locals 1",
- " iload_0",
- " lookupswitch",
- x.toString(),
- " default : case_default",
- y.toString(),
- " case_default:",
- " ldc " + defaultValue,
- " return_:",
- " ireturn");
-
- // Add the Jasmin class and a class from Java source with the main method.
- AndroidApp.Builder appBuilder = AndroidApp.builder();
- appBuilder.addClassProgramData(builder.buildClasses());
- appBuilder.addProgramFiles(FilteredClassPath
- .unfiltered(ToolHelper.getClassFileForTestClass(CheckSwitchInTestClass.class)));
- AndroidApp app = compileWithR8(appBuilder.build());
-
- DexInspector inspector = new DexInspector(app);
- MethodSubject method = inspector.clazz("Test").method("int", "test", ImmutableList.of("int"));
- DexCode code = method.getMethod().getCode().asDexCode();
-
- int packedSwitches = 0;
- int sparseSwitches = 0;
- int ifs = 0;
- for (Instruction instruction : code.instructions) {
- if (instruction instanceof PackedSwitch) {
- packedSwitches++;
- }
- if (instruction instanceof SparseSwitch) {
- sparseSwitches++;
- }
- if (instruction instanceof IfEq || instruction instanceof IfEqz) {
- ifs++;
- }
- }
-
- assertEquals(expectedPackedSwitches, packedSwitches);
- assertEquals(expectedSparceSwitches, sparseSwitches);
- assertEquals(expectedIfs, ifs);
-
- // Run the code
- List<String> args = keys.stream().map(Object::toString).collect(Collectors.toList());
- args.add(Integer.toString(defaultValue));
- runOnArt(app, CheckSwitchInTestClass.class, args);
- }
-
- @Test
- public void convertCasesToIf() throws Exception {
- // Switches that are completely converted to ifs.
- runConvertCasesToIf(ImmutableList.of(0, 1000), -100, 2, 0, 0);
- runConvertCasesToIf(ImmutableList.of(0, 1000, 2000), -100, 3, 0, 0);
- runConvertCasesToIf(ImmutableList.of(0, 1000, 2000, 3000), -100, 4, 0, 0);
-
- // Switches that are completely converted to ifs and one switch.
- runConvertCasesToIf(ImmutableList.of(0, 1000, 1001, 1002, 1003, 1004), -100, 1, 1, 0);
- runConvertCasesToIf(ImmutableList.of(1000, 1001, 1002, 1003, 1004, 2000), -100, 1, 1, 0);
- runConvertCasesToIf(ImmutableList.of(
- Integer.MIN_VALUE, 1000, 1001, 1002, 1003, 1004), -100, 1, 1, 0);
- runConvertCasesToIf(ImmutableList.of(
- 1000, 1001, 1002, 1003, 1004, Integer.MAX_VALUE), -100, 1, 1, 0);
- runConvertCasesToIf(ImmutableList.of(0, 1000, 1001, 1002, 1003, 1004, 2000), -100, 2, 1, 0);
- runConvertCasesToIf(ImmutableList.of(
- Integer.MIN_VALUE, 1000, 1001, 1002, 1003, 1004, Integer.MAX_VALUE), -100, 2, 1, 0);
-
- // Switches that are completely converted to ifs and two switches.
- runConvertCasesToIf(ImmutableList.of(
- 0, 1, 2, 3, 4, 1000, 1001, 1002, 1003, 1004), -100, 0, 2, 0);
- runConvertCasesToIf(ImmutableList.of(
- -1000, 0, 1, 2, 3, 4, 1000, 1001, 1002, 1003, 1004), -100, 1, 2, 0);
- runConvertCasesToIf(ImmutableList.of(
- -1000, 0, 1, 2, 3, 4, 1000, 1001, 1002, 1003, 1004, 2000), -100, 2, 2, 0);
-
- // Switches that are completely converted two switches (one sparse and one packed).
- runConvertCasesToIf(ImmutableList.of(
- -1000, -900, -800, -700, -600, -500, -400, -300,
- 1000, 1001, 1002, 1003, 1004,
- 2000, 2100, 2200, 2300, 2400, 2500), -100, 0, 1, 1);
- }
-}
\ No newline at end of file