Merge changes I2342b395,Ie0918957,I0a9a2a17
* changes:
Use command-line parsing in apiUsageSample
Add @Keep and @KeepForSubclassing annotations for public R8 API
Make R8 and D8 command-line parsing use only public API
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index ef247a0..df30c18 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -813,8 +813,8 @@
if (overlappingMoveExceptionIntervals) {
for (LiveIntervals intervals : moveExceptionIntervals) {
if (intervals.getUses().size() > 1) {
- LiveIntervalsUse secondUse = intervals.getUses().stream().skip(1).findFirst().get();
- LiveIntervals split = intervals.splitBefore(secondUse.getPosition());
+ LiveIntervals split =
+ intervals.splitBefore(intervals.getFirstUse() + INSTRUCTION_NUMBER_DELTA);
unhandled.add(split);
}
}
@@ -1354,6 +1354,12 @@
if (!hasDedicatedMoveExceptionRegister()) {
return false;
}
+ // If there are that many move exception intervals we don't spent the time
+ // going through them all. In that case it is unlikely that we can reuse the move exception
+ // register in any case.
+ if (moveExceptionIntervals.size() > 1000) {
+ return true;
+ }
for (LiveIntervals moveExceptionInterval : moveExceptionIntervals) {
if (intervals.anySplitOverlaps(moveExceptionInterval)) {
return true;
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 19fe18e..085d9b3 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -785,6 +785,9 @@
// Mark the type live here, so that the class exists at runtime. Note that this also marks all
// supertypes as live, so even if the field is actually on a supertype, its class will be live.
markTypeAsLive(field.clazz);
+ if (field.type.isClassType()) {
+ markTypeAsLive(field.type);
+ }
// Find the actual field.
DexEncodedField encodedField = appInfo.resolveFieldOn(field.clazz, field);
if (encodedField == null) {
@@ -814,6 +817,9 @@
private void markInstanceFieldAsLive(DexEncodedField field, KeepReason reason) {
assert field != null;
markTypeAsLive(field.field.clazz);
+ if (field.field.type.isClassType()) {
+ markTypeAsLive(field.field.type);
+ }
if (Log.ENABLED) {
Log.verbose(getClass(), "Adding instance field `%s` to live set.", field.field);
}
diff --git a/src/test/examples/catchhandleroverlap/CatchHandlerOverlap.java b/src/test/examples/catchhandleroverlap/CatchHandlerOverlap.java
new file mode 100644
index 0000000..7a4950c
--- /dev/null
+++ b/src/test/examples/catchhandleroverlap/CatchHandlerOverlap.java
@@ -0,0 +1,33 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package catchhandleroverlap;
+
+public class CatchHandlerOverlap {
+ private static void f() throws Exception {
+ throw new Exception("f");
+ }
+
+ private static void g() throws Exception {
+ throw new Exception("g");
+ }
+
+ private static void h(int i1, int i2, int i3, int i4, int i5, int i6, int i7, int i8, int i9,
+ int i10, int i11, int i12, int i13, int i14, int i15, int i16, int i17) {
+ System.out.println(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 + i10 + i11 +
+ i12 + i13 + i14 + i15 + i16 + i17);
+ try {
+ f();
+ } catch (Exception e0) {
+ try {
+ g();
+ } catch (Exception e1) {
+ System.out.println(e0.getMessage() + " " + e1.getMessage());
+ }
+ }
+ }
+
+ public static void main(String[] args) {
+ h(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17);
+ }
+}
diff --git a/src/test/examples/multidex005/ref-list-4-r8.txt b/src/test/examples/multidex005/ref-list-4-r8.txt
new file mode 100644
index 0000000..ea7b0f7
--- /dev/null
+++ b/src/test/examples/multidex005/ref-list-4-r8.txt
@@ -0,0 +1,8 @@
+Lmultidex005/DirectlyReferenced;
+Lmultidex005/FieldReference;
+Lmultidex005/Interface1;
+Lmultidex005/Interface2;
+Lmultidex005/Interface3;
+Lmultidex005/SuperClass;
+Lmultidex005/SuperInterface;
+Lmultidex005/SuperSuperClass;
\ No newline at end of file
diff --git a/src/test/examples/multidex005/ref-list-4.txt b/src/test/examples/multidex005/ref-list-4.txt
index ea7b0f7..7733fba 100644
--- a/src/test/examples/multidex005/ref-list-4.txt
+++ b/src/test/examples/multidex005/ref-list-4.txt
@@ -1,5 +1,8 @@
Lmultidex005/DirectlyReferenced;
Lmultidex005/FieldReference;
+Lmultidex005/IndirectlyReferenced;
+Lmultidex005/IndirectlyReferencedInterface;
+Lmultidex005/IndirectlyReferencedSuperClass;
Lmultidex005/Interface1;
Lmultidex005/Interface2;
Lmultidex005/Interface3;
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index 2ac50a6..d8b886c 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -32,6 +32,7 @@
"arrayaccess.ArrayAccess",
"barray.BArray",
"bridge.BridgeMethod",
+ "catchhandleroverlap.CatchHandlerOverlap",
"cse.CommonSubexpressionElimination",
"constants.Constants",
"controlflow.ControlFlow",
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
index 43b4c0f..374d58f 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.GenerateMainDexListCommand;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ir.desugar.LambdaRewriter;
import com.android.tools.r8.utils.AndroidApiLevel;
@@ -30,19 +31,15 @@
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.junit.Assert;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-public class MainDexTracingTest {
+public class MainDexTracingTest extends TestBase {
private static final String EXAMPLE_BUILD_DIR = ToolHelper.EXAMPLES_BUILD_DIR;
private static final String EXAMPLE_O_BUILD_DIR = ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR;
private static final String EXAMPLE_SRC_DIR = ToolHelper.EXAMPLES_DIR;
private static final String EXAMPLE_O_SRC_DIR = ToolHelper.EXAMPLES_ANDROID_O_DIR;
- @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
-
@Test
public void traceMainDexList001_whyareyoukeeping() throws Throwable {
PrintStream stdout = System.out;
@@ -54,6 +51,7 @@
EXAMPLE_BUILD_DIR,
Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules-whyareyoukeeping.txt"),
Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-1.txt"),
+ Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-1.txt"),
AndroidApiLevel.I);
String output = new String(baos.toByteArray(), Charset.defaultCharset());
Assert.assertTrue(output.contains("is live because referenced in keep rule:"));
@@ -68,6 +66,7 @@
EXAMPLE_BUILD_DIR,
Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules.txt"),
Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-1.txt"),
+ Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-1.txt"),
AndroidApiLevel.I);
}
@@ -79,6 +78,7 @@
EXAMPLE_BUILD_DIR,
Paths.get(EXAMPLE_SRC_DIR, "multidex001", "main-dex-rules-2.txt"),
Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-2.txt"),
+ Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-2.txt"),
AndroidApiLevel.I);
}
@@ -90,6 +90,7 @@
EXAMPLE_BUILD_DIR,
Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules.txt"),
Paths.get(EXAMPLE_SRC_DIR, "multidex002", "ref-list-1.txt"),
+ Paths.get(EXAMPLE_SRC_DIR, "multidex002", "ref-list-1.txt"),
AndroidApiLevel.I);
}
@@ -101,6 +102,7 @@
EXAMPLE_BUILD_DIR,
Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules.txt"),
Paths.get(EXAMPLE_SRC_DIR, "multidex003", "ref-list-1.txt"),
+ Paths.get(EXAMPLE_SRC_DIR, "multidex003", "ref-list-1.txt"),
AndroidApiLevel.I);
}
@@ -112,6 +114,7 @@
EXAMPLE_O_BUILD_DIR,
Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules.txt"),
Paths.get(EXAMPLE_O_SRC_DIR, "multidex004", "ref-list-1.txt"),
+ Paths.get(EXAMPLE_O_SRC_DIR, "multidex004", "ref-list-1.txt"),
AndroidApiLevel.I);
}
@@ -132,7 +135,14 @@
@Test
public void traceMainDexList005_4() throws Throwable {
- doTest5(4);
+ doTest(
+ "traceMainDexList005",
+ "multidex005",
+ EXAMPLE_BUILD_DIR,
+ Paths.get(EXAMPLE_SRC_DIR, "multidex005", "main-dex-rules-4.txt"),
+ Paths.get(EXAMPLE_SRC_DIR, "multidex005", "ref-list-4-r8.txt"),
+ Paths.get(EXAMPLE_SRC_DIR, "multidex005", "ref-list-4.txt"),
+ AndroidApiLevel.I);
}
@Test
@@ -158,6 +168,7 @@
EXAMPLE_BUILD_DIR,
Paths.get(EXAMPLE_SRC_DIR, "multidex006", "main-dex-rules-1.txt"),
Paths.get(EXAMPLE_SRC_DIR, "multidex006", "ref-list-1.txt"),
+ Paths.get(EXAMPLE_SRC_DIR, "multidex006", "ref-list-1.txt"),
AndroidApiLevel.I);
}
@@ -168,6 +179,7 @@
EXAMPLE_BUILD_DIR,
Paths.get(EXAMPLE_SRC_DIR, "multidex005", "main-dex-rules-" + variant + ".txt"),
Paths.get(EXAMPLE_SRC_DIR, "multidex005", "ref-list-" + variant + ".txt"),
+ Paths.get(EXAMPLE_SRC_DIR, "multidex005", "ref-list-" + variant + ".txt"),
AndroidApiLevel.I);
}
@@ -176,6 +188,7 @@
String packageName,
String buildDir,
Path mainDexRules,
+ Path expectedR8MainDexList,
Path expectedMainDexList,
AndroidApiLevel minSdk)
throws Throwable {
@@ -184,6 +197,7 @@
packageName,
buildDir,
mainDexRules,
+ expectedR8MainDexList,
expectedMainDexList,
minSdk,
(options) -> {
@@ -196,6 +210,7 @@
String packageName,
String buildDir,
Path mainDexRules,
+ Path expectedR8MainDexList,
Path expectedMainDexList,
AndroidApiLevel minSdk,
Consumer<InternalOptions> optionsConsumer)
@@ -259,17 +274,22 @@
.map(this::mainDexStringToDescriptor)
.sorted()
.collect(Collectors.toList());
- // Check that both generated lists are the same as the reference list, except for lambda
+ // Check that generated lists are the same as the reference list, except for lambda
// classes which are only produced when running R8.
+ String[] r8RefList = new String(Files.readAllBytes(
+ expectedR8MainDexList), StandardCharsets.UTF_8).split("\n");
+ for (int i = 0; i < r8RefList.length; i++) {
+ String reference = r8RefList[i].trim();
+ if (r8MainDexList.size() <= i) {
+ Assert.fail("R8 main dex list is missing '" + reference + "'");
+ }
+ checkSameMainDexEntry(reference, r8MainDexList.get(i));
+ }
String[] refList = new String(Files.readAllBytes(
expectedMainDexList), StandardCharsets.UTF_8).split("\n");
int nonLambdaOffset = 0;
for (int i = 0; i < refList.length; i++) {
String reference = refList[i].trim();
- if (r8MainDexList.size() <= i) {
- Assert.fail("R8 main dex list is missing '" + reference + "'");
- }
- checkSameMainDexEntry(reference, r8MainDexList.get(i));
// The main dex list generator does not do any lambda desugaring.
if (!isLambda(reference)) {
if (mainDexGeneratorMainDexList.size() <= i - nonLambdaOffset) {
diff --git a/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java b/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java
index 9e0429b..8f00f8d 100644
--- a/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java
@@ -21,22 +21,15 @@
import com.android.tools.r8.utils.DexInspector.ClassSubject;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(VmTestRunner.class)
public class FieldTypeTest extends TestBase {
- @Ignore("b/78788577")
- @Test
- public void test_brokenTypeHierarchy() throws Exception {
- JasminBuilder jasminBuilder = new JasminBuilder();
- // interface Itf
- ClassBuilder itf = jasminBuilder.addInterface("Itf");
- MethodSignature foo = itf.addAbstractMethod("foo", ImmutableList.of(), "V");
- // class Impl /* implements Itf */
- ClassBuilder impl = jasminBuilder.addClass("Impl");
+ private ClassBuilder addImplementor(
+ JasminBuilder jasminBuilder, String name, String superName, String... interfaces) {
+ ClassBuilder impl = jasminBuilder.addClass(name, superName, interfaces);
impl.addDefaultConstructor();
impl.addVirtualMethod("foo", ImmutableList.of(), "V",
".limit locals 2",
@@ -50,15 +43,43 @@
".limit stack 2",
"ldc \"" + impl.name + "\"",
"areturn");
+ return impl;
+ }
+
+ @Test
+ public void test_brokenTypeHierarchy() throws Exception {
+ JasminBuilder jasminBuilder = new JasminBuilder();
+ // interface Itf1
+ ClassBuilder itf1 = jasminBuilder.addInterface("Itf1");
+ MethodSignature foo1 = itf1.addAbstractMethod("foo", ImmutableList.of(), "V");
+ // class Impl1 /* implements Itf1 */
+ ClassBuilder impl1 = addImplementor(jasminBuilder, "Impl1", "java/lang/Object");
+
+ // Another interface and implementer with a correct relation.
+ ClassBuilder itf2 = jasminBuilder.addInterface("Itf2");
+ MethodSignature foo2 = itf2.addAbstractMethod("foo", ImmutableList.of(), "V");
+ ClassBuilder impl2 = addImplementor(jasminBuilder, "Impl2", "java/lang/Object", itf2.name);
+
ClassBuilder client = jasminBuilder.addClass("Client");
- FieldSignature obj = client.addStaticFinalField("obj", itf.getDescriptor(), null);
+ client.setAccess("final");
+ client.addDefaultConstructor();
+ FieldSignature a = client.addStaticFinalField("a", "Ljava/lang/Object;", null);
+ FieldSignature obj1 = client.addField("private static", "obj1", itf1.getDescriptor(), null);
+ FieldSignature obj2 = client.addStaticFinalField("obj2", itf2.getDescriptor(), null);
client.addClassInitializer(
".limit locals 1",
".limit stack 2",
- "new " + impl.name,
+ "aconst_null",
+ "putstatic " + client.name + "/" + a.name + " " + "Ljava/lang/Object;",
+ "new " + impl1.name,
"dup",
- "invokespecial " + impl.name + "/<init>()V",
- "putstatic " + client.name + "/" + obj.name + " " + itf.getDescriptor(),
+ "invokespecial " + impl1.name + "/<init>()V",
+ // Unused, i.e., not read, field, yet still remained in the output.
+ "putstatic " + client.name + "/" + obj1.name + " " + itf1.getDescriptor(),
+ "new " + impl2.name,
+ "dup",
+ "invokespecial " + impl2.name + "/<init>()V",
+ "putstatic " + client.name + "/" + obj2.name + " " + itf2.getDescriptor(),
"return"
);
@@ -67,8 +88,8 @@
".limit locals 2",
".limit stack 2",
"getstatic java/lang/System/out Ljava/io/PrintStream;",
- "getstatic " + client.name + "/" + obj.name + " " + itf.getDescriptor(),
/*
+ "getstatic " + client.name + "/" + obj1.name + " " + itf1.getDescriptor(),
"astore_0",
"aload_0",
// java.lang.IncompatibleClassChangeError:
@@ -76,19 +97,24 @@
"invokeinterface " + itf.name + "/" + foo.name + "()V 1",
"aload_0",
*/
+ "getstatic " + client.name + "/" + obj2.name + " " + itf2.getDescriptor(),
"invokevirtual java/io/PrintStream/print(Ljava/lang/Object;)V",
"return"
);
final String mainClassName = mainClass.name;
- String proguardConfig = keepMainProguardConfiguration(mainClass.name, false, false);
+ String proguardConfig =
+ keepMainProguardConfiguration(mainClass.name, false, false)
+ // AGP default is to not turn optimizations on, which disables MemberValuePropagation,
+ // resulting in the problematic putstatic being remained.
+ + "-dontoptimize\n";
// Run input program on java.
Path outputDirectory = temp.newFolder().toPath();
jasminBuilder.writeClassFiles(outputDirectory);
ProcessResult javaResult = ToolHelper.runJava(outputDirectory, mainClassName);
assertEquals(0, javaResult.exitCode);
- assertThat(javaResult.stdout, containsString(impl.name));
+ assertThat(javaResult.stdout, containsString(impl2.name));
AndroidApp processedApp = compileWithR8(jasminBuilder.build(), proguardConfig,
// Disable inlining to avoid the (short) tested method from being inlined and then removed.
@@ -97,12 +123,12 @@
// Run processed (output) program on ART
ProcessResult artResult = runOnArtRaw(processedApp, mainClassName);
assertEquals(0, artResult.exitCode);
- assertThat(artResult.stdout, containsString(impl.name));
+ assertThat(artResult.stdout, containsString(impl2.name));
assertEquals(-1, artResult.stderr.indexOf("DoFieldPut"));
DexInspector inspector = new DexInspector(processedApp);
- ClassSubject itfSubject = inspector.clazz(itf.name);
- assertThat(itfSubject, isPresent());
+ ClassSubject itf1Subject = inspector.clazz(itf1.name);
+ assertThat(itf1Subject, isPresent());
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
index d578815..cae3b06 100644
--- a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
@@ -94,12 +94,13 @@
Result result = runTest(mainClass, proguardConfig);
- // Without includedescriptorclasses return type argument type and field type are removed.
+ // Without includedescriptorclasses return type and argument type are removed.
result.assertKept(ClassWithNativeMethods.class);
result.assertRemoved(NativeArgumentType.class);
result.assertRemoved(NativeReturnType.class);
- result.assertRemoved(InstanceFieldType.class);
- result.assertRemoved(StaticFieldType.class);
+ // Field type is not removed due to the concern about the broken type hierarchy.
+ result.assertRenamed(InstanceFieldType.class);
+ result.assertRenamed(StaticFieldType.class);
}
}
diff --git a/src/test/java/com/android/tools/r8/smali/DexMoveInstructionsTest.java b/src/test/java/com/android/tools/r8/smali/DexMoveInstructionsTest.java
new file mode 100644
index 0000000..c92b73b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/DexMoveInstructionsTest.java
@@ -0,0 +1,95 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.smali;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.DescriptorUtils;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+
+public class DexMoveInstructionsTest extends SmaliTestBase {
+
+ public static final String CLASS = "Test";
+
+ @Test
+ public void testValidObjectMoves() throws Throwable {
+ ProcessResult result = testMoves("ExpectedToPass", "Ljava/lang/String;", Arrays.asList(
+ "move-object",
+ "move-object/from16",
+ "move-object/16"));
+ assertEquals(result.toString(), 0, result.exitCode);
+ }
+
+ @Test
+ public void testInvalidObjectMoves() throws Throwable {
+ ProcessResult result = testMoves("ExpectedToFail", "Ljava/lang/String;", Arrays.asList(
+ "move",
+ "move/from16",
+ "move/16"));
+ assertEquals(result.toString(), 1, result.exitCode);
+ assertTrue("Did not find 'Verification error' in " + result.stderr,
+ result.stderr.contains("Verification error") || result.stderr.contains("VerifyError"));
+ }
+
+ @Test
+ public void testValidSingleMoves() throws Throwable {
+ ProcessResult result = testMoves("ExpectedToPass", "I", Arrays.asList(
+ "move",
+ "move/from16",
+ "move/16"));
+ assertEquals(result.toString(), 0, result.exitCode);
+ }
+
+ @Test
+ public void testInvalidSingleMoves() throws Throwable {
+ ProcessResult result = testMoves("ExpectedToFail", "I", Arrays.asList(
+ "move-object",
+ "move-object/from16",
+ "move-object/16"));
+ assertEquals(result.toString(), 1, result.exitCode);
+ assertTrue("Did not find 'Verification error' in " + result.stderr,
+ result.stderr.contains("Verification error") || result.stderr.contains("VerifyError"));
+ }
+
+ private ProcessResult testMoves(String clazz, String typeDesc, List<String> moveOps)
+ throws Throwable {
+ String typeName = DescriptorUtils.descriptorToJavaType(typeDesc);
+
+ SmaliBuilder builder = new SmaliBuilder(clazz);
+ int i = 0;
+ for (String moveOp : moveOps) {
+ builder.addStaticMethod(typeName, "test" + i++, Collections.singletonList(typeName),
+ 1,
+ " " + moveOp + " v0, p0",
+ typeDesc.startsWith("L") ? "return-object v0" : " return v0"
+ );
+ }
+
+ List<String> main = new ArrayList<>();
+ main.add(" sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;");
+ main.add(" const v2, 0");
+ i = 0;
+ for (String moveOp : moveOps) {
+ main.add(" invoke-static { v2 }, L" + clazz + ";->test" + i++
+ + "(" + typeDesc + ")" + typeDesc);
+ if (typeDesc.startsWith("L")) {
+ main.add(" move-result-object v1");
+ } else {
+ main.add(" move-result v1");
+ }
+ main.add(" invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(" + typeDesc + ")V");
+ }
+ main.add(" return-void");
+ builder.addMainMethod(3, main.toArray(new String[0]));
+
+ return runOnArtRaw(builder.build(), clazz);
+ }
+}