blob: bbeed31d70c9e7a1e6a6c9652d8bbeda54f04727 [file] [log] [blame]
// 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 com.android.tools.r8.graph.SmaliWriter;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Smali;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.util.Collections;
import java.util.concurrent.ExecutionException;
import org.antlr.runtime.RecognitionException;
import org.junit.Test;
public class SmaliDisassembleTest extends SmaliTestBase {
// Run the provided smali through R8 smali disassembler and expect the exact same output.
void roundTripRawSmali(String smali) {
try {
AndroidApp application =
AndroidApp.builder().addDexProgramData(Smali.compile(smali), Origin.unknown()).build();
assertEquals(smali, SmaliWriter.smali(application, new InternalOptions()));
} catch (IOException | RecognitionException | ExecutionException e) {
throw new RuntimeException(e);
}
}
@Test
public void simpleSmokeTest() {
AndroidApp application = singleMethodApplication(
"int", Collections.singletonList("int"),
4,
" const/4 v0, 1 ",
" const/16 v1, 2 ",
" const v2, 3 ",
" const-wide v3, -1 ",
" add-int/2addr v1, v0 ",
" mul-int/2addr v2, v1 ",
" div-int/2addr v3, v2 ",
" return v3\n "
);
String expected =
".class public LTest;\n" +
"\n" +
".super Ljava/lang/Object;\n" +
"\n" +
".method public static method(I)I\n" +
" .registers 5\n" +
"\n" +
" const/4 v0, 0x01 # 1\n" +
" const/16 v1, 0x0002 # 2\n" +
" const v2, 0x00000003 # 3\n" +
" const-wide v3, 0xffffffffffffffffL # -1\n" +
" add-int/2addr v1, v0\n" +
" mul-int/2addr v2, v1\n" +
" div-int/2addr v3, v2\n" +
" return v3\n" +
".end method\n" +
"\n" +
"# End of class LTest;\n";
assertEquals(expected, SmaliWriter.smali(application, new InternalOptions()));
roundTripRawSmali(expected);
}
@Test
public void sparseSwitchTest() {
AndroidApp application = singleMethodApplication(
"int", Collections.singletonList("int"),
0,
" sparse-switch v0, :sparse_switch_data",
" const/4 v0, 0x0",
" goto :return",
" :case_1",
" const/4 v0, 0x1",
" goto :return",
" :case_2",
" const/4 v0, 0x2",
" :return",
" return v0",
" :sparse_switch_data ",
" .sparse-switch ",
" 0x1 -> :case_1 ",
" 0x2 -> :case_2 ",
" .end sparse-switch "
);
String expected =
".class public LTest;\n" +
"\n" +
".super Ljava/lang/Object;\n" +
"\n" +
".method public static method(I)I\n" +
" .registers 1\n" +
"\n" +
" sparse-switch v0, :label_10\n" +
" const/4 v0, 0x00 # 0\n" +
" goto :label_8\n" +
" :label_5\n" +
" const/4 v0, 0x01 # 1\n" +
" goto :label_8\n" +
" :label_7\n" +
" const/4 v0, 0x02 # 2\n" +
" :label_8\n" +
" return v0\n" +
" nop\n" +
" :label_10\n" +
" .sparse-switch\n" +
" 0x00000001 -> :label_5 # 1\n" +
" 0x00000002 -> :label_7 # 2\n" +
" .end sparse-switch\n" +
".end method\n" +
"\n" +
"# End of class LTest;\n";
assertEquals(expected, SmaliWriter.smali(application, new InternalOptions()));
roundTripRawSmali(expected);
}
@Test
public void packedSwitchTest() {
AndroidApp application = singleMethodApplication(
"int", Collections.singletonList("int"),
0,
" packed-switch v0, :packed_switch_data",
" const/4 v0, 0x0 ",
" goto :return ",
" :case_1 ",
" const/4 v0, 0x1 ",
" goto :return ",
" :case_2 ",
" const/4 v0, 0x2 ",
" :return ",
" return v0 ",
" :packed_switch_data ",
" .packed-switch 0x1 ",
" :case_1 ",
" :case_2 ",
" .end packed-switch "
);
String expected =
".class public LTest;\n" +
"\n" +
".super Ljava/lang/Object;\n" +
"\n" +
".method public static method(I)I\n" +
" .registers 1\n" +
"\n" +
" packed-switch v0, :label_10\n" +
" const/4 v0, 0x00 # 0\n" +
" goto :label_8\n" +
" :label_5\n" +
" const/4 v0, 0x01 # 1\n" +
" goto :label_8\n" +
" :label_7\n" +
" const/4 v0, 0x02 # 2\n" +
" :label_8\n" +
" return v0\n" +
" nop\n" +
" :label_10\n" +
" .packed-switch 0x00000001 # 1\n" +
" :label_5\n" +
" :label_7\n" +
" .end packed-switch\n" +
".end method\n" +
"\n" +
"# End of class LTest;\n";
assertEquals(expected, SmaliWriter.smali(application, new InternalOptions()));
roundTripRawSmali(expected);
}
@Test
public void fillArrayDataTest8Bit() {
AndroidApp application = singleMethodApplication(
"int[]", ImmutableList.of(),
2,
" const/4 v1, 3",
" new-array v0, v1, [I",
" fill-array-data v0, :array_data",
" return-object v0",
" :array_data",
" .array-data 1",
" 1 2 255",
" .end array-data"
);
String expected =
".class public LTest;\n" +
"\n" +
".super Ljava/lang/Object;\n" +
"\n" +
".method public static method()[I\n" +
" .registers 2\n" +
"\n" +
" const/4 v1, 0x03 # 3\n" +
" new-array v0, v1, [I\n" +
" fill-array-data v0, :label_8\n" +
" return-object v0\n" +
" nop\n" +
" :label_8\n" +
" .array-data 0x1 # 1\n" +
" 0x01 # 1\n" +
" 0x02 # 2\n" +
" 0xff # 255\n" +
" .end array-data\n" +
".end method\n" +
"\n" +
"# End of class LTest;\n";
assertEquals(expected, SmaliWriter.smali(application, new InternalOptions()));
roundTripRawSmali(expected);
}
@Test
public void fillArrayDataTest16Bit() {
AndroidApp application = singleMethodApplication(
"int[]", ImmutableList.of(),
2,
" const/4 v1, 3",
" new-array v0, v1, [I",
" fill-array-data v0, :array_data",
" return-object v0",
" :array_data",
" .array-data 2",
" 1 2 65535",
" .end array-data"
);
String expected =
".class public LTest;\n" +
"\n" +
".super Ljava/lang/Object;\n" +
"\n" +
".method public static method()[I\n" +
" .registers 2\n" +
"\n" +
" const/4 v1, 0x03 # 3\n" +
" new-array v0, v1, [I\n" +
" fill-array-data v0, :label_8\n" +
" return-object v0\n" +
" nop\n" +
" :label_8\n" +
" .array-data 0x2 # 2\n" +
" 0x0001 # 1\n" +
" 0x0002 # 2\n" +
" 0xffff # 65535\n" +
" .end array-data\n" +
".end method\n" +
"\n" +
"# End of class LTest;\n";
assertEquals(expected, SmaliWriter.smali(application, new InternalOptions()));
roundTripRawSmali(expected);
}
@Test
public void fillArrayDataTest32Bit() {
AndroidApp application = singleMethodApplication(
"int[]", ImmutableList.of(),
2,
" const/4 v1, 3",
" new-array v0, v1, [I",
" fill-array-data v0, :array_data",
" return-object v0",
" :array_data",
" .array-data 4",
" 1 2 4294967295",
" .end array-data"
);
String expected =
".class public LTest;\n" +
"\n" +
".super Ljava/lang/Object;\n" +
"\n" +
".method public static method()[I\n" +
" .registers 2\n" +
"\n" +
" const/4 v1, 0x03 # 3\n" +
" new-array v0, v1, [I\n" +
" fill-array-data v0, :label_8\n" +
" return-object v0\n" +
" nop\n" +
" :label_8\n" +
" .array-data 0x4 # 4\n" +
" 0x00000001 # 1\n" +
" 0x00000002 # 2\n" +
" 0xffffffff # 4294967295\n" +
" .end array-data\n" +
".end method\n" +
"\n" +
"# End of class LTest;\n";
assertEquals(expected, SmaliWriter.smali(application, new InternalOptions()));
roundTripRawSmali(expected);
}
@Test
public void fillArrayDataTest64Bit() {
AndroidApp application = singleMethodApplication(
"int[]", ImmutableList.of(),
2,
" const/4 v1, 3",
" new-array v0, v1, [I",
" fill-array-data v0, :array_data",
" return-object v0",
" :array_data",
" .array-data 8",
" 1 2 -1",
" .end array-data"
);
String expected =
".class public LTest;\n" +
"\n" +
".super Ljava/lang/Object;\n" +
"\n" +
".method public static method()[I\n" +
" .registers 2\n" +
"\n" +
" const/4 v1, 0x03 # 3\n" +
" new-array v0, v1, [I\n" +
" fill-array-data v0, :label_8\n" +
" return-object v0\n" +
" nop\n" +
" :label_8\n" +
" .array-data 0x8 # 8\n" +
" 0x0000000000000001 # 1\n" +
" 0x0000000000000002 # 2\n" +
" 0xffffffffffffffff # -1\n" +
" .end array-data\n" +
".end method\n" +
"\n" +
"# End of class LTest;\n";
assertEquals(expected, SmaliWriter.smali(application, new InternalOptions()));
roundTripRawSmali(expected);
}
@Test
public void interfaceClass() {
SmaliBuilder builder = new SmaliBuilder();
builder.addInterface("Test");
builder.addAbstractMethod("int", "test", ImmutableList.of());
AndroidApp application = buildApplication(builder);
String expected =
".class public interface abstract LTest;\n" +
"\n" +
".super Ljava/lang/Object;\n" +
"\n" +
".method public abstract test()I\n" +
".end method\n" +
"\n" +
"# End of class LTest;\n";
assertEquals(expected, SmaliWriter.smali(application, new InternalOptions()));
roundTripRawSmali(expected);
}
@Test
public void implementsInterface() {
SmaliBuilder builder = new SmaliBuilder();
builder.addClass("Test", "java.lang.Object", ImmutableList.of("java.util.List")).setAbstract();
builder.addAbstractMethod("int", "test", ImmutableList.of());
AndroidApp application = buildApplication(builder);
String expected =
".class public abstract LTest;\n"
+ "\n"
+ ".super Ljava/lang/Object;\n"
+ ".implements Ljava/util/List;\n"
+ "\n"
+ ".method public abstract test()I\n"
+ ".end method\n"
+ "\n"
+ "# End of class LTest;\n";
assertEquals(expected, SmaliWriter.smali(application, new InternalOptions()));
roundTripRawSmali(expected);
}
}