aboutsummaryrefslogtreecommitdiff
path: root/baksmali
diff options
context:
space:
mode:
authorBen Gruver <bgruv@google.com>2015-03-18 20:42:12 -0700
committerBen Gruver <bgruv@google.com>2015-03-18 21:00:32 -0700
commit75bef01d100126e6f2e46614302fe5cad7c8287d (patch)
treebd0fe933ae742498109c7fb0d54fd3b3ae7a1637 /baksmali
parentddc7c35e1cfdddc7276d72e5e79c69bf932046e5 (diff)
parent2a0e4657ea73c67a44d1711da2539aa9c1a88524 (diff)
downloadsmali-75bef01d100126e6f2e46614302fe5cad7c8287d.tar.gz
Merge branch 'master' into smalidea
Diffstat (limited to 'baksmali')
-rw-r--r--baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java73
-rw-r--r--baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java75
-rw-r--r--baksmali/src/main/java/org/jf/baksmali/baksmali.java2
-rw-r--r--baksmali/src/main/java/org/jf/baksmali/baksmaliOptions.java1
-rw-r--r--baksmali/src/main/java/org/jf/baksmali/dump.java4
-rw-r--r--baksmali/src/main/java/org/jf/baksmali/main.java23
-rw-r--r--baksmali/src/test/java/org/jf/baksmali/AnalysisTest.java2
-rw-r--r--baksmali/src/test/java/org/jf/baksmali/BaksmaliTestUtils.java119
-rw-r--r--baksmali/src/test/java/org/jf/baksmali/DisassemblyTest.java104
-rw-r--r--baksmali/src/test/java/org/jf/baksmali/IdenticalRoundtripTest.java59
-rw-r--r--baksmali/src/test/java/org/jf/baksmali/ImplicitReferenceTest.java112
-rw-r--r--baksmali/src/test/java/org/jf/baksmali/LambdaTest.java49
-rw-r--r--baksmali/src/test/java/org/jf/baksmali/ManyRegistersTest.java42
-rw-r--r--baksmali/src/test/java/org/jf/baksmali/MultiSwitchTest.java42
-rw-r--r--baksmali/src/test/java/org/jf/baksmali/ParamListMethodNameTest.java42
-rw-r--r--baksmali/src/test/java/org/jf/baksmali/RoundtripTest.java78
-rw-r--r--baksmali/src/test/java/org/jf/baksmali/SwitchTest.java41
-rw-r--r--baksmali/src/test/resources/LambdaTest/HelloWorldLambda.smali55
-rw-r--r--baksmali/src/test/resources/ManyRegistersTest/ManyRegisters.smali7
-rw-r--r--baksmali/src/test/resources/MultiSwitchTest/MultiSwitchInput.dexbin0 -> 616 bytes
-rw-r--r--baksmali/src/test/resources/MultiSwitchTest/MultiSwitchInput.smali72
-rw-r--r--baksmali/src/test/resources/MultiSwitchTest/MultiSwitchOutput.smali119
-rw-r--r--baksmali/src/test/resources/ParamListMethodNameTest/ParamListMethodName.smali5
-rw-r--r--baksmali/src/test/resources/SwitchTest/UnorderedSparseSwitchInput.smali35
-rw-r--r--baksmali/src/test/resources/SwitchTest/UnorderedSparseSwitchOutput.smali28
25 files changed, 1047 insertions, 142 deletions
diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java
index 12acd791..b3f9ae17 100644
--- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java
+++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java
@@ -41,8 +41,6 @@ import org.jf.dexlib2.iface.instruction.*;
import org.jf.dexlib2.iface.instruction.formats.Instruction20bc;
import org.jf.dexlib2.iface.instruction.formats.Instruction31t;
import org.jf.dexlib2.iface.instruction.formats.UnknownInstruction;
-import org.jf.dexlib2.iface.reference.FieldReference;
-import org.jf.dexlib2.iface.reference.MethodReference;
import org.jf.dexlib2.iface.reference.Reference;
import org.jf.dexlib2.util.ReferenceUtil;
import org.jf.util.ExceptionWithContext;
@@ -132,26 +130,37 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
}
if (instruction instanceof Instruction31t) {
- Opcode payloadOpcode;
+ boolean validPayload = true;
+
switch (instruction.getOpcode()) {
case PACKED_SWITCH:
- payloadOpcode = Opcode.PACKED_SWITCH_PAYLOAD;
+ int baseAddress = methodDef.getPackedSwitchBaseAddress(
+ this.codeAddress + ((Instruction31t)instruction).getCodeOffset());
+ if (baseAddress == -1) {
+ validPayload = false;
+ }
break;
case SPARSE_SWITCH:
- payloadOpcode = Opcode.SPARSE_SWITCH_PAYLOAD;
+ baseAddress = methodDef.getSparseSwitchBaseAddress(
+ this.codeAddress + ((Instruction31t)instruction).getCodeOffset());
+ if (baseAddress == -1) {
+ validPayload = false;
+ }
break;
case FILL_ARRAY_DATA:
- payloadOpcode = Opcode.ARRAY_PAYLOAD;
+ try {
+ methodDef.findPayloadOffset(this.codeAddress + ((Instruction31t)instruction).getCodeOffset(),
+ Opcode.ARRAY_PAYLOAD);
+ } catch (InvalidSwitchPayload ex) {
+ validPayload = false;
+ }
break;
default:
throw new ExceptionWithContext("Invalid 31t opcode: %s", instruction.getOpcode());
}
- try {
- methodDef.findSwitchPayload(this.codeAddress + ((Instruction31t)instruction).getCodeOffset(),
- payloadOpcode);
- } catch (InvalidSwitchPayload ex) {
- writer.write("#invalid payload reference");
+ if (!validPayload) {
+ writer.write("#invalid payload reference\n");
commentOutInstruction = true;
}
}
@@ -300,6 +309,11 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
writer.write(", ");
writeThirdRegister(writer);
break;
+ case Format25x:
+ writeOpcode(writer);
+ writer.write(' ');
+ writeInvoke25xRegisters(writer); // vC, {vD, ...}
+ break;
case Format35c:
writeOpcode(writer);
writer.write(' ');
@@ -425,6 +439,43 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
writer.write('}');
}
+ protected void writeInvoke25xRegisters(IndentingWriter writer) throws IOException {
+ OneFixedFourParameterRegisterInstruction instruction =
+ (OneFixedFourParameterRegisterInstruction)this.instruction;
+ final int parameterRegCount = instruction.getParameterRegisterCount();
+
+ writeRegister(writer, instruction.getRegisterFixedC()); // fixed register always present
+
+ writer.write(", {");
+ switch (parameterRegCount) {
+ case 1:
+ writeRegister(writer, instruction.getRegisterParameterD());
+ break;
+ case 2:
+ writeRegister(writer, instruction.getRegisterParameterD());
+ writer.write(", ");
+ writeRegister(writer, instruction.getRegisterParameterE());
+ break;
+ case 3:
+ writeRegister(writer, instruction.getRegisterParameterD());
+ writer.write(", ");
+ writeRegister(writer, instruction.getRegisterParameterE());
+ writer.write(", ");
+ writeRegister(writer, instruction.getRegisterParameterF());
+ break;
+ case 4:
+ writeRegister(writer, instruction.getRegisterParameterD());
+ writer.write(", ");
+ writeRegister(writer, instruction.getRegisterParameterE());
+ writer.write(", ");
+ writeRegister(writer, instruction.getRegisterParameterF());
+ writer.write(", ");
+ writeRegister(writer, instruction.getRegisterParameterG());
+ break;
+ }
+ writer.write('}');
+ }
+
protected void writeInvokeRangeRegisters(IndentingWriter writer) throws IOException {
RegisterRangeInstruction instruction = (RegisterRangeInstruction)this.instruction;
diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java
index eb5caa67..4081a75c 100644
--- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java
+++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java
@@ -29,6 +29,7 @@
package org.jf.baksmali.Adaptors;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
import org.jf.baksmali.Adaptors.Debug.DebugMethodItem;
import org.jf.baksmali.Adaptors.Format.InstructionMethodItemFactory;
import org.jf.baksmali.baksmaliOptions;
@@ -45,11 +46,14 @@ import org.jf.dexlib2.iface.debug.DebugItem;
import org.jf.dexlib2.iface.instruction.Instruction;
import org.jf.dexlib2.iface.instruction.OffsetInstruction;
import org.jf.dexlib2.iface.instruction.ReferenceInstruction;
+import org.jf.dexlib2.iface.instruction.formats.Instruction31t;
import org.jf.dexlib2.iface.reference.MethodReference;
+import org.jf.dexlib2.immutable.instruction.ImmutableInstruction31t;
import org.jf.dexlib2.util.InstructionOffsetMap;
import org.jf.dexlib2.util.InstructionOffsetMap.InvalidInstructionOffset;
import org.jf.dexlib2.util.ReferenceUtil;
import org.jf.dexlib2.util.SyntheticAccessorResolver;
+import org.jf.dexlib2.util.SyntheticAccessorResolver.AccessedMember;
import org.jf.dexlib2.util.TypeUtils;
import org.jf.util.ExceptionWithContext;
import org.jf.util.IndentingWriter;
@@ -65,6 +69,8 @@ public class MethodDefinition {
@Nonnull public final Method method;
@Nonnull public final MethodImplementation methodImpl;
@Nonnull public final ImmutableList<Instruction> instructions;
+ @Nonnull public final List<Instruction> effectiveInstructions;
+
@Nonnull public final ImmutableList<MethodParameter> methodParameters;
public RegisterFormatter registerFormatter;
@@ -86,10 +92,15 @@ public class MethodDefinition {
instructions = ImmutableList.copyOf(methodImpl.getInstructions());
methodParameters = ImmutableList.copyOf(method.getParameters());
+ effectiveInstructions = Lists.newArrayList(instructions);
+
packedSwitchMap = new SparseIntArray(0);
sparseSwitchMap = new SparseIntArray(0);
instructionOffsetMap = new InstructionOffsetMap(instructions);
+ int endOffset = instructionOffsetMap.getInstructionCodeOffset(instructions.size()-1) +
+ instructions.get(instructions.size()-1).getCodeUnits();
+
for (int i=0; i<instructions.size(); i++) {
Instruction instruction = instructions.get(i);
@@ -99,11 +110,20 @@ public class MethodDefinition {
int codeOffset = instructionOffsetMap.getInstructionCodeOffset(i);
int targetOffset = codeOffset + ((OffsetInstruction)instruction).getCodeOffset();
try {
- targetOffset = findSwitchPayload(targetOffset, Opcode.PACKED_SWITCH_PAYLOAD);
+ targetOffset = findPayloadOffset(targetOffset, Opcode.PACKED_SWITCH_PAYLOAD);
} catch (InvalidSwitchPayload ex) {
valid = false;
}
if (valid) {
+ if (packedSwitchMap.get(targetOffset, -1) != -1) {
+ Instruction payloadInstruction =
+ findSwitchPayload(targetOffset, Opcode.PACKED_SWITCH_PAYLOAD);
+ targetOffset = endOffset;
+ effectiveInstructions.set(i, new ImmutableInstruction31t(opcode,
+ ((Instruction31t)instruction).getRegisterA(), targetOffset-codeOffset));
+ effectiveInstructions.add(payloadInstruction);
+ endOffset += payloadInstruction.getCodeUnits();
+ }
packedSwitchMap.append(targetOffset, codeOffset);
}
} else if (opcode == Opcode.SPARSE_SWITCH) {
@@ -111,18 +131,27 @@ public class MethodDefinition {
int codeOffset = instructionOffsetMap.getInstructionCodeOffset(i);
int targetOffset = codeOffset + ((OffsetInstruction)instruction).getCodeOffset();
try {
- targetOffset = findSwitchPayload(targetOffset, Opcode.SPARSE_SWITCH_PAYLOAD);
+ targetOffset = findPayloadOffset(targetOffset, Opcode.SPARSE_SWITCH_PAYLOAD);
} catch (InvalidSwitchPayload ex) {
valid = false;
// The offset to the payload instruction was invalid. Nothing to do, except that we won't
// add this instruction to the map.
}
if (valid) {
+ if (sparseSwitchMap.get(targetOffset, -1) != -1) {
+ Instruction payloadInstruction =
+ findSwitchPayload(targetOffset, Opcode.SPARSE_SWITCH_PAYLOAD);
+ targetOffset = endOffset;
+ effectiveInstructions.set(i, new ImmutableInstruction31t(opcode,
+ ((Instruction31t)instruction).getRegisterA(), targetOffset-codeOffset));
+ effectiveInstructions.add(payloadInstruction);
+ endOffset += payloadInstruction.getCodeUnits();
+ }
sparseSwitchMap.append(targetOffset, codeOffset);
}
}
}
- }catch (Exception ex) {
+ } catch (Exception ex) {
String methodString;
try {
methodString = ReferenceUtil.getMethodDescriptor(method);
@@ -216,7 +245,36 @@ public class MethodDefinition {
writer.write(".end method\n");
}
- public int findSwitchPayload(int targetOffset, Opcode type) {
+ public Instruction findSwitchPayload(int targetOffset, Opcode type) {
+ int targetIndex;
+ try {
+ targetIndex = instructionOffsetMap.getInstructionIndexAtCodeOffset(targetOffset);
+ } catch (InvalidInstructionOffset ex) {
+ throw new InvalidSwitchPayload(targetOffset);
+ }
+
+ //TODO: does dalvik let you pad with multiple nops?
+ //TODO: does dalvik let a switch instruction point to a non-payload instruction?
+
+ Instruction instruction = instructions.get(targetIndex);
+ if (instruction.getOpcode() != type) {
+ // maybe it's pointing to a NOP padding instruction. Look at the next instruction
+ if (instruction.getOpcode() == Opcode.NOP) {
+ targetIndex += 1;
+ if (targetIndex < instructions.size()) {
+ instruction = instructions.get(targetIndex);
+ if (instruction.getOpcode() == type) {
+ return instruction;
+ }
+ }
+ }
+ throw new InvalidSwitchPayload(targetOffset);
+ } else {
+ return instruction;
+ }
+ }
+
+ public int findPayloadOffset(int targetOffset, Opcode type) {
int targetIndex;
try {
targetIndex = instructionOffsetMap.getInstructionIndexAtCodeOffset(targetOffset);
@@ -343,15 +401,16 @@ public class MethodDefinition {
private void addInstructionMethodItems(List<MethodItem> methodItems) {
int currentCodeAddress = 0;
- for (int i=0; i<instructions.size(); i++) {
- Instruction instruction = instructions.get(i);
+
+ for (int i=0; i<effectiveInstructions.size(); i++) {
+ Instruction instruction = effectiveInstructions.get(i);
MethodItem methodItem = InstructionMethodItemFactory.makeInstructionFormatMethodItem(this,
currentCodeAddress, instruction);
methodItems.add(methodItem);
- if (i != instructions.size() - 1) {
+ if (i != effectiveInstructions.size() - 1) {
methodItems.add(new BlankMethodItem(currentCodeAddress));
}
@@ -386,7 +445,7 @@ public class MethodDefinition {
if (methodReference != null &&
SyntheticAccessorResolver.looksLikeSyntheticAccessor(methodReference.getName())) {
- SyntheticAccessorResolver.AccessedMember accessedMember =
+ AccessedMember accessedMember =
classDef.options.syntheticAccessorResolver.getAccessedMember(methodReference);
if (accessedMember != null) {
methodItems.add(new SyntheticAccessCommentMethodItem(accessedMember, currentCodeAddress));
diff --git a/baksmali/src/main/java/org/jf/baksmali/baksmali.java b/baksmali/src/main/java/org/jf/baksmali/baksmali.java
index 765e77ec..47fa406d 100644
--- a/baksmali/src/main/java/org/jf/baksmali/baksmali.java
+++ b/baksmali/src/main/java/org/jf/baksmali/baksmali.java
@@ -67,7 +67,7 @@ public class baksmali {
options.classPath = ClassPath.fromClassPath(options.bootClassPathDirs,
Iterables.concat(options.bootClassPathEntries, extraClassPathEntries), dexFile,
- options.apiLevel, options.checkPackagePrivateAccess);
+ options.apiLevel, options.checkPackagePrivateAccess, options.experimental);
if (options.customInlineDefinitions != null) {
options.inlineResolver = new CustomInlineMethodResolver(options.classPath,
diff --git a/baksmali/src/main/java/org/jf/baksmali/baksmaliOptions.java b/baksmali/src/main/java/org/jf/baksmali/baksmaliOptions.java
index ada28239..b6cc1571 100644
--- a/baksmali/src/main/java/org/jf/baksmali/baksmaliOptions.java
+++ b/baksmali/src/main/java/org/jf/baksmali/baksmaliOptions.java
@@ -71,6 +71,7 @@ public class baksmaliOptions {
public boolean noAccessorComments = false;
public boolean allowOdex = false;
public boolean deodex = false;
+ public boolean experimental = false;
public boolean ignoreErrors = false;
public boolean checkPackagePrivateAccess = false;
public boolean useImplicitReferences = false;
diff --git a/baksmali/src/main/java/org/jf/baksmali/dump.java b/baksmali/src/main/java/org/jf/baksmali/dump.java
index bd040e6d..1ef7df0e 100644
--- a/baksmali/src/main/java/org/jf/baksmali/dump.java
+++ b/baksmali/src/main/java/org/jf/baksmali/dump.java
@@ -40,7 +40,7 @@ import java.io.IOException;
import java.io.Writer;
public class dump {
- public static void dump(DexBackedDexFile dexFile, String dumpFileName, int apiLevel) throws IOException {
+ public static void dump(DexBackedDexFile dexFile, String dumpFileName, int apiLevel, boolean experimental) throws IOException {
if (dumpFileName != null) {
Writer writer = null;
@@ -52,7 +52,7 @@ public class dump {
consoleWidth = 120;
}
- RawDexFile rawDexFile = new RawDexFile(new Opcodes(apiLevel), dexFile);
+ RawDexFile rawDexFile = new RawDexFile(new Opcodes(apiLevel, experimental), dexFile);
DexAnnotator annotator = new DexAnnotator(rawDexFile, consoleWidth);
annotator.writeAnnotations(writer);
} catch (IOException ex) {
diff --git a/baksmali/src/main/java/org/jf/baksmali/main.java b/baksmali/src/main/java/org/jf/baksmali/main.java
index 28c42030..71598fa6 100644
--- a/baksmali/src/main/java/org/jf/baksmali/main.java
+++ b/baksmali/src/main/java/org/jf/baksmali/main.java
@@ -192,6 +192,9 @@ public class main {
case 'x':
options.deodex = true;
break;
+ case 'X':
+ options.experimental = true;
+ break;
case 'm':
options.noAccessorComments = true;
break;
@@ -253,7 +256,8 @@ public class main {
}
//Read in and parse the dex file
- DexBackedDexFile dexFile = DexFileFactory.loadDexFile(dexFileFile, options.dexEntry, options.apiLevel);
+ DexBackedDexFile dexFile = DexFileFactory.loadDexFile(dexFileFile, options.dexEntry,
+ options.apiLevel, options.experimental);
if (dexFile.isOdexFile()) {
if (!options.deodex) {
@@ -270,13 +274,15 @@ public class main {
if (dexFile instanceof DexBackedOdexFile) {
options.bootClassPathEntries = ((DexBackedOdexFile)dexFile).getDependencies();
} else {
- options.bootClassPathEntries = getDefaultBootClassPathForApi(options.apiLevel);
+ options.bootClassPathEntries = getDefaultBootClassPathForApi(options.apiLevel,
+ options.experimental);
}
}
if (options.customInlineDefinitions == null && dexFile instanceof DexBackedOdexFile) {
options.inlineResolver =
- InlineMethodResolver.createInlineMethodResolver(((DexBackedOdexFile)dexFile).getOdexVersion());
+ InlineMethodResolver.createInlineMethodResolver(
+ ((DexBackedOdexFile)dexFile).getOdexVersion());
}
boolean errorOccurred = false;
@@ -288,7 +294,7 @@ public class main {
if (dumpFileName == null) {
dumpFileName = commandLine.getOptionValue(inputDexFileName + ".dump");
}
- dump.dump(dexFile, dumpFileName, options.apiLevel);
+ dump.dump(dexFile, dumpFileName, options.apiLevel, options.experimental);
}
if (errorOccurred) {
@@ -352,6 +358,10 @@ public class main {
"odex file")
.create("x");
+ Option experimentalOption = OptionBuilder.withLongOpt("experimental")
+ .withDescription("enable experimental opcodes to be disassembled, even if they aren't necessarily supported in the Android runtime yet")
+ .create("X");
+
Option useLocalsOption = OptionBuilder.withLongOpt("use-locals")
.withDescription("output the .locals directive with the number of non-parameter registers, rather" +
" than the .register directive with the total number of register")
@@ -469,6 +479,7 @@ public class main {
basicOptions.addOption(outputDirOption);
basicOptions.addOption(noParameterRegistersOption);
basicOptions.addOption(deodexerantOption);
+ basicOptions.addOption(experimentalOption);
basicOptions.addOption(useLocalsOption);
basicOptions.addOption(sequentialLabelsOption);
basicOptions.addOption(noDebugInfoOption);
@@ -496,9 +507,9 @@ public class main {
options.addOption((Option)option);
}
}
-
+
@Nonnull
- private static List<String> getDefaultBootClassPathForApi(int apiLevel) {
+ private static List<String> getDefaultBootClassPathForApi(int apiLevel, boolean experimental) {
if (apiLevel < 9) {
return Lists.newArrayList(
"/system/framework/core.jar",
diff --git a/baksmali/src/test/java/org/jf/baksmali/AnalysisTest.java b/baksmali/src/test/java/org/jf/baksmali/AnalysisTest.java
index bf353e83..725032af 100644
--- a/baksmali/src/test/java/org/jf/baksmali/AnalysisTest.java
+++ b/baksmali/src/test/java/org/jf/baksmali/AnalysisTest.java
@@ -85,7 +85,7 @@ public class AnalysisTest {
public void runTest(String test, boolean registerInfo) throws IOException, URISyntaxException {
String dexFilePath = String.format("%s%sclasses.dex", test, File.separatorChar);
- DexFile dexFile = DexFileFactory.loadDexFile(findResource(dexFilePath), 15);
+ DexFile dexFile = DexFileFactory.loadDexFile(findResource(dexFilePath), 15, false);
baksmaliOptions options = new baksmaliOptions();
if (registerInfo) {
diff --git a/baksmali/src/test/java/org/jf/baksmali/BaksmaliTestUtils.java b/baksmali/src/test/java/org/jf/baksmali/BaksmaliTestUtils.java
new file mode 100644
index 00000000..1c570b6c
--- /dev/null
+++ b/baksmali/src/test/java/org/jf/baksmali/BaksmaliTestUtils.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.baksmali;
+
+import com.google.common.io.ByteStreams;
+import junit.framework.Assert;
+
+import org.antlr.runtime.RecognitionException;
+import org.jf.baksmali.Adaptors.ClassDefinition;
+import org.jf.dexlib2.iface.ClassDef;
+import org.jf.smali.SmaliTestUtils;
+import org.jf.util.IndentingWriter;
+import org.jf.util.TextUtils;
+
+import javax.annotation.Nonnull;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+
+public class BaksmaliTestUtils {
+ public static void assertSmaliCompiledEquals(String source, String expected,
+ baksmaliOptions options, boolean stripComments) throws IOException,
+ RecognitionException {
+ ClassDef classDef = SmaliTestUtils.compileSmali(source, options.apiLevel,
+ options.experimental);
+
+ // Remove unnecessary whitespace and optionally strip all comments from smali file
+ String normalizedActual = getNormalizedSmali(classDef, options, stripComments);
+ String normalizedExpected = normalizeSmali(expected, stripComments);
+
+ // Assert that normalized strings are now equal
+ Assert.assertEquals(normalizedExpected, normalizedActual);
+ }
+
+ public static void assertSmaliCompiledEquals(String source, String expected,
+ baksmaliOptions options) throws IOException, RecognitionException {
+ assertSmaliCompiledEquals(source, expected, options, false);
+ }
+
+ public static void assertSmaliCompiledEquals(String source, String expected)
+ throws IOException, RecognitionException {
+ baksmaliOptions options = new baksmaliOptions();
+ assertSmaliCompiledEquals(source, expected, options);
+ }
+
+ @Nonnull
+ public static String normalizeSmali(@Nonnull String smaliText, boolean stripComments) {
+ if (stripComments) {
+ smaliText = TextUtils.stripComments(smaliText);
+ }
+ return TextUtils.normalizeWhitespace(smaliText);
+ }
+
+ @Nonnull
+ public static String getNormalizedSmali(@Nonnull ClassDef classDef, @Nonnull baksmaliOptions options,
+ boolean stripComments)
+ throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ IndentingWriter writer = new IndentingWriter(stringWriter);
+ ClassDefinition classDefinition = new ClassDefinition(options, classDef);
+ classDefinition.writeTo(writer);
+ writer.close();
+ return normalizeSmali(stringWriter.toString(), stripComments);
+ }
+
+ @Nonnull
+ public static byte[] readResourceBytesFully(@Nonnull String fileName) throws IOException {
+ InputStream smaliStream = RoundtripTest.class.getClassLoader().
+ getResourceAsStream(fileName);
+ if (smaliStream == null) {
+ org.junit.Assert.fail("Could not load " + fileName);
+ }
+
+ return ByteStreams.toByteArray(smaliStream);
+ }
+
+ @Nonnull
+ public static String readResourceFully(@Nonnull String fileName) throws IOException {
+ return readResourceFully(fileName, "UTF-8");
+ }
+
+ @Nonnull
+ public static String readResourceFully(@Nonnull String fileName, @Nonnull String encoding)
+ throws IOException {
+ return new String(readResourceBytesFully(fileName), encoding);
+ }
+
+ // Static helpers class; do not instantiate.
+ private BaksmaliTestUtils() { throw new AssertionError(); }
+}
diff --git a/baksmali/src/test/java/org/jf/baksmali/DisassemblyTest.java b/baksmali/src/test/java/org/jf/baksmali/DisassemblyTest.java
new file mode 100644
index 00000000..35304f7e
--- /dev/null
+++ b/baksmali/src/test/java/org/jf/baksmali/DisassemblyTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.baksmali;
+
+import com.google.common.collect.Iterables;
+import org.jf.dexlib2.Opcodes;
+import org.jf.dexlib2.dexbacked.DexBackedDexFile;
+import org.jf.dexlib2.iface.ClassDef;
+import org.junit.Assert;
+
+import javax.annotation.Nonnull;
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * A base test class for performing a disassembly on a dex file and verifying the results
+ *
+ * The test accepts a single-class dex file as input, disassembles it, and verifies that
+ * the result equals a known-good output smali file.
+ *
+ * By default, the input and output files should be resources at [testDir]/[testName]Input.dex
+ * and [testDir]/[testName]Output.smali respectively
+ */
+public class DisassemblyTest {
+ protected final String testDir;
+
+ protected DisassemblyTest(@Nonnull String testDir) {
+ this.testDir = testDir;
+ }
+
+ protected DisassemblyTest() {
+ this.testDir = this.getClass().getSimpleName();
+ }
+
+ @Nonnull
+ protected String getInputFilename(@Nonnull String testName) {
+ return String.format("%s%s%sInput.dex", testDir, File.separatorChar, testName);
+ }
+
+ @Nonnull
+ protected String getOutputFilename(@Nonnull String testName) {
+ return String.format("%s%s%sOutput.smali", testDir, File.separatorChar, testName);
+ }
+
+ protected void runTest(@Nonnull String testName) {
+ runTest(testName, new baksmaliOptions());
+ }
+
+ protected void runTest(@Nonnull String testName, @Nonnull baksmaliOptions options) {
+ try {
+ // Load file from resources as a stream
+ String inputFilename = getInputFilename(testName);
+ byte[] inputBytes = BaksmaliTestUtils.readResourceBytesFully(getInputFilename(testName));
+
+ DexBackedDexFile inputDex = new DexBackedDexFile(new Opcodes(options.apiLevel, false), inputBytes);
+ Assert.assertEquals(1, inputDex.getClassCount());
+ ClassDef inputClass = Iterables.getFirst(inputDex.getClasses(), null);
+ Assert.assertNotNull(inputClass);
+ String input = BaksmaliTestUtils.getNormalizedSmali(inputClass, options, true);
+
+ String output;
+ if (getOutputFilename(testName).equals(inputFilename)) {
+ output = input;
+ } else {
+ output = BaksmaliTestUtils.readResourceFully(getOutputFilename(testName));
+ }
+ output = BaksmaliTestUtils.normalizeSmali(output, true);
+
+ // Run smali, baksmali, and then compare strings are equal (minus comments/whitespace)
+ Assert.assertEquals(output, input);
+ } catch (IOException ex) {
+ Assert.fail();
+ }
+ }
+}
diff --git a/baksmali/src/test/java/org/jf/baksmali/IdenticalRoundtripTest.java b/baksmali/src/test/java/org/jf/baksmali/IdenticalRoundtripTest.java
new file mode 100644
index 00000000..e636ee1e
--- /dev/null
+++ b/baksmali/src/test/java/org/jf/baksmali/IdenticalRoundtripTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.baksmali;
+
+import javax.annotation.Nonnull;
+import java.io.File;
+
+/**
+ * A base test class for performing a roundtrip assembly/disassembly where the input and output
+ * should be identical.
+ *
+ * By default, the input/output file should be a resource at [testDir]/[testName].smali
+ */
+public abstract class IdenticalRoundtripTest extends RoundtripTest {
+
+ public IdenticalRoundtripTest(@Nonnull String testDir) {
+ super(testDir);
+ }
+
+ public IdenticalRoundtripTest() {
+ }
+
+ @Nonnull @Override protected String getInputFilename(@Nonnull String testName) {
+ return String.format("%s%s%s.smali", testDir, File.separatorChar, testName);
+ }
+
+ @Nonnull @Override protected String getOutputFilename(@Nonnull String testName) {
+ return getInputFilename(testName);
+ }
+}
diff --git a/baksmali/src/test/java/org/jf/baksmali/ImplicitReferenceTest.java b/baksmali/src/test/java/org/jf/baksmali/ImplicitReferenceTest.java
index 0cda27a1..1f2ae5bf 100644
--- a/baksmali/src/test/java/org/jf/baksmali/ImplicitReferenceTest.java
+++ b/baksmali/src/test/java/org/jf/baksmali/ImplicitReferenceTest.java
@@ -31,22 +31,15 @@
package org.jf.baksmali;
-import junit.framework.Assert;
import org.antlr.runtime.RecognitionException;
-import org.jf.baksmali.Adaptors.ClassDefinition;
-import org.jf.dexlib2.iface.ClassDef;
-import org.jf.smali.SmaliTestUtils;
-import org.jf.util.IndentingWriter;
-import org.jf.util.TextUtils;
import org.junit.Test;
import java.io.IOException;
-import java.io.StringWriter;
public class ImplicitReferenceTest {
@Test
public void testImplicitMethodReferences() throws IOException, RecognitionException {
- ClassDef classDef = SmaliTestUtils.compileSmali("" +
+ String source = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".method public static main([Ljava/lang/String;)V\n" +
@@ -55,7 +48,7 @@ public class ImplicitReferenceTest {
" invoke-static {p0}, LHelloWorld;->V()V\n" +
" invoke-static {p0}, LHelloWorld;->I()V\n" +
" return-void\n" +
- ".end method");
+ ".end method";
String expected = "" +
".class public LHelloWorld;\n" +
@@ -72,19 +65,12 @@ public class ImplicitReferenceTest {
baksmaliOptions options = new baksmaliOptions();
options.useImplicitReferences = true;
- StringWriter stringWriter = new StringWriter();
- IndentingWriter writer = new IndentingWriter(stringWriter);
- ClassDefinition classDefinition = new ClassDefinition(options, classDef);
- classDefinition.writeTo(writer);
- writer.close();
-
- Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
- TextUtils.normalizeWhitespace(stringWriter.toString()));
+ BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
}
@Test
public void testExplicitMethodReferences() throws IOException, RecognitionException {
- ClassDef classDef = SmaliTestUtils.compileSmali("" +
+ String source = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".method public static main([Ljava/lang/String;)V\n" +
@@ -93,7 +79,7 @@ public class ImplicitReferenceTest {
" invoke-static {p0}, LHelloWorld;->V()V\n" +
" invoke-static {p0}, LHelloWorld;->I()V\n" +
" return-void\n" +
- ".end method");
+ ".end method";
String expected = "" +
".class public LHelloWorld;\n" +
@@ -110,25 +96,18 @@ public class ImplicitReferenceTest {
baksmaliOptions options = new baksmaliOptions();
options.useImplicitReferences = false;
- StringWriter stringWriter = new StringWriter();
- IndentingWriter writer = new IndentingWriter(stringWriter);
- ClassDefinition classDefinition = new ClassDefinition(options, classDef);
- classDefinition.writeTo(writer);
- writer.close();
-
- Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
- TextUtils.normalizeWhitespace(stringWriter.toString()));
+ BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
}
@Test
public void testImplicitMethodLiterals() throws IOException, RecognitionException {
- ClassDef classDef = SmaliTestUtils.compileSmali("" +
+ String source = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".field public static field1:Ljava/lang/reflect/Method; = LHelloWorld;->toString()V\n" +
".field public static field2:Ljava/lang/reflect/Method; = LHelloWorld;->V()V\n" +
".field public static field3:Ljava/lang/reflect/Method; = LHelloWorld;->I()V\n" +
- ".field public static field4:Ljava/lang/Class; = I");
+ ".field public static field4:Ljava/lang/Class; = I";
String expected = "" +
".class public LHelloWorld;\n" +
@@ -142,25 +121,18 @@ public class ImplicitReferenceTest {
baksmaliOptions options = new baksmaliOptions();
options.useImplicitReferences = true;
- StringWriter stringWriter = new StringWriter();
- IndentingWriter writer = new IndentingWriter(stringWriter);
- ClassDefinition classDefinition = new ClassDefinition(options, classDef);
- classDefinition.writeTo(writer);
- writer.close();
-
- Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
- TextUtils.normalizeWhitespace(stringWriter.toString()));
+ BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
}
@Test
public void testExplicitMethodLiterals() throws IOException, RecognitionException {
- ClassDef classDef = SmaliTestUtils.compileSmali("" +
+ String source = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".field public static field1:Ljava/lang/reflect/Method; = LHelloWorld;->toString()V\n" +
".field public static field2:Ljava/lang/reflect/Method; = LHelloWorld;->V()V\n" +
".field public static field3:Ljava/lang/reflect/Method; = LHelloWorld;->I()V\n" +
- ".field public static field4:Ljava/lang/Class; = I");
+ ".field public static field4:Ljava/lang/Class; = I";
String expected = "" +
".class public LHelloWorld;\n" +
@@ -174,19 +146,12 @@ public class ImplicitReferenceTest {
baksmaliOptions options = new baksmaliOptions();
options.useImplicitReferences = false;
- StringWriter stringWriter = new StringWriter();
- IndentingWriter writer = new IndentingWriter(stringWriter);
- ClassDefinition classDefinition = new ClassDefinition(options, classDef);
- classDefinition.writeTo(writer);
- writer.close();
-
- Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
- TextUtils.normalizeWhitespace(stringWriter.toString()));
+ BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
}
@Test
public void testImplicitFieldReferences() throws IOException, RecognitionException {
- ClassDef classDef = SmaliTestUtils.compileSmali("" +
+ String source = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".method public static main([Ljava/lang/String;)V\n" +
@@ -195,7 +160,7 @@ public class ImplicitReferenceTest {
" sget v0, LHelloWorld;->I:I\n" +
" sget v0, LHelloWorld;->V:I\n" +
" return-void\n" +
- ".end method");
+ ".end method";
String expected = "" +
".class public LHelloWorld;\n" +
@@ -212,19 +177,12 @@ public class ImplicitReferenceTest {
baksmaliOptions options = new baksmaliOptions();
options.useImplicitReferences = true;
- StringWriter stringWriter = new StringWriter();
- IndentingWriter writer = new IndentingWriter(stringWriter);
- ClassDefinition classDefinition = new ClassDefinition(options, classDef);
- classDefinition.writeTo(writer);
- writer.close();
-
- Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
- TextUtils.normalizeWhitespace(stringWriter.toString()));
+ BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
}
@Test
public void testExplicitFieldReferences() throws IOException, RecognitionException {
- ClassDef classDef = SmaliTestUtils.compileSmali("" +
+ String source = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".method public static main([Ljava/lang/String;)V\n" +
@@ -233,7 +191,7 @@ public class ImplicitReferenceTest {
" sget v0, LHelloWorld;->I:I\n" +
" sget v0, LHelloWorld;->V:I\n" +
" return-void\n" +
- ".end method");
+ ".end method";
String expected = "" +
".class public LHelloWorld;\n" +
@@ -250,24 +208,17 @@ public class ImplicitReferenceTest {
baksmaliOptions options = new baksmaliOptions();
options.useImplicitReferences = false;
- StringWriter stringWriter = new StringWriter();
- IndentingWriter writer = new IndentingWriter(stringWriter);
- ClassDefinition classDefinition = new ClassDefinition(options, classDef);
- classDefinition.writeTo(writer);
- writer.close();
-
- Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
- TextUtils.normalizeWhitespace(stringWriter.toString()));
+ BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
}
@Test
public void testImplicitFieldLiterals() throws IOException, RecognitionException {
- ClassDef classDef = SmaliTestUtils.compileSmali("" +
+ String source = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".field public static field1:Ljava/lang/reflect/Field; = LHelloWorld;->someField:I\n" +
".field public static field2:Ljava/lang/reflect/Field; = LHelloWorld;->V:I\n" +
- ".field public static field3:Ljava/lang/reflect/Field; = LHelloWorld;->I:I");
+ ".field public static field3:Ljava/lang/reflect/Field; = LHelloWorld;->I:I";
String expected = "" +
".class public LHelloWorld;\n" +
@@ -280,24 +231,17 @@ public class ImplicitReferenceTest {
baksmaliOptions options = new baksmaliOptions();
options.useImplicitReferences = true;
- StringWriter stringWriter = new StringWriter();
- IndentingWriter writer = new IndentingWriter(stringWriter);
- ClassDefinition classDefinition = new ClassDefinition(options, classDef);
- classDefinition.writeTo(writer);
- writer.close();
-
- Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
- TextUtils.normalizeWhitespace(stringWriter.toString()));
+ BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
}
@Test
public void testExplicitFieldLiterals() throws IOException, RecognitionException {
- ClassDef classDef = SmaliTestUtils.compileSmali("" +
+ String source = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".field public static field1:Ljava/lang/reflect/Field; = LHelloWorld;->someField:I\n" +
".field public static field2:Ljava/lang/reflect/Field; = LHelloWorld;->V:I\n" +
- ".field public static field3:Ljava/lang/reflect/Field; = LHelloWorld;->I:I");
+ ".field public static field3:Ljava/lang/reflect/Field; = LHelloWorld;->I:I";
String expected = "" +
".class public LHelloWorld;\n" +
@@ -310,13 +254,7 @@ public class ImplicitReferenceTest {
baksmaliOptions options = new baksmaliOptions();
options.useImplicitReferences = false;
- StringWriter stringWriter = new StringWriter();
- IndentingWriter writer = new IndentingWriter(stringWriter);
- ClassDefinition classDefinition = new ClassDefinition(options, classDef);
- classDefinition.writeTo(writer);
- writer.close();
-
- Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
- TextUtils.normalizeWhitespace(stringWriter.toString()));
+ BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
}
+
}
diff --git a/baksmali/src/test/java/org/jf/baksmali/LambdaTest.java b/baksmali/src/test/java/org/jf/baksmali/LambdaTest.java
new file mode 100644
index 00000000..5431df54
--- /dev/null
+++ b/baksmali/src/test/java/org/jf/baksmali/LambdaTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.baksmali;
+
+import org.junit.Test;
+
+public class LambdaTest extends IdenticalRoundtripTest {
+
+ private baksmaliOptions createOptions() {
+ baksmaliOptions options = new baksmaliOptions();
+ options.apiLevel = 23; // since we need at least level 23 for lambda opcodes
+ options.experimental = true; // since these opcodes aren't implemented in runtime yet);
+ return options;
+ }
+
+ @Test
+ public void testHelloWorldLambda() {
+ runTest("HelloWorldLambda", createOptions());
+ }
+}
diff --git a/baksmali/src/test/java/org/jf/baksmali/ManyRegistersTest.java b/baksmali/src/test/java/org/jf/baksmali/ManyRegistersTest.java
new file mode 100644
index 00000000..5a267161
--- /dev/null
+++ b/baksmali/src/test/java/org/jf/baksmali/ManyRegistersTest.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.baksmali;
+
+import org.junit.Test;
+
+public class ManyRegistersTest extends IdenticalRoundtripTest {
+
+ @Test
+ public void testManyRegisters() {
+ runTest("ManyRegisters");
+ }
+}
diff --git a/baksmali/src/test/java/org/jf/baksmali/MultiSwitchTest.java b/baksmali/src/test/java/org/jf/baksmali/MultiSwitchTest.java
new file mode 100644
index 00000000..cb29402f
--- /dev/null
+++ b/baksmali/src/test/java/org/jf/baksmali/MultiSwitchTest.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.baksmali;
+
+import org.junit.Test;
+
+public class MultiSwitchTest extends DisassemblyTest {
+
+ @Test
+ public void testMultiSwitch() {
+ runTest("MultiSwitch");
+ }
+}
diff --git a/baksmali/src/test/java/org/jf/baksmali/ParamListMethodNameTest.java b/baksmali/src/test/java/org/jf/baksmali/ParamListMethodNameTest.java
new file mode 100644
index 00000000..42f7239d
--- /dev/null
+++ b/baksmali/src/test/java/org/jf/baksmali/ParamListMethodNameTest.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.baksmali;
+
+import org.junit.Test;
+
+public class ParamListMethodNameTest extends IdenticalRoundtripTest {
+
+ @Test
+ public void testParamListMethodName() {
+ runTest("ParamListMethodName");
+ }
+}
diff --git a/baksmali/src/test/java/org/jf/baksmali/RoundtripTest.java b/baksmali/src/test/java/org/jf/baksmali/RoundtripTest.java
index 266497f9..c9ff2d4d 100644
--- a/baksmali/src/test/java/org/jf/baksmali/RoundtripTest.java
+++ b/baksmali/src/test/java/org/jf/baksmali/RoundtripTest.java
@@ -32,38 +32,64 @@
package org.jf.baksmali;
import org.antlr.runtime.RecognitionException;
-import org.jf.baksmali.Adaptors.ClassDefinition;
-import org.jf.dexlib2.iface.ClassDef;
-import org.jf.smali.SmaliTestUtils;
-import org.jf.util.IndentingWriter;
-import org.jf.util.TextUtils;
import org.junit.Assert;
-import org.junit.Test;
+import javax.annotation.Nonnull;
+import java.io.File;
import java.io.IOException;
-import java.io.StringWriter;
-public class RoundtripTest {
- @Test
- public void testParamListMethodName() throws IOException, RecognitionException {
- String text = "" +
- ".class Lblah;\n" +
- ".super Ljava/lang/Object;\n" +
- "# virtual methods\n" +
- ".method public abstract II()V\n" +
- ".end method\n";
- ClassDef classDef = SmaliTestUtils.compileSmali(text);
+/**
+ * A base test class for performing a roundtrip assembly/disassembly
+ *
+ * The test accepts a smali file as input, performs a smali -> dex -> smali roundtrip, and
+ * verifies that the result equals a known-good output smali file.
+ *
+ * By default, the input and output files should be resources at [testDir]/[testName]Input.smali
+ * and [testDir]/[testName]Output.smali respectively
+ */
+public abstract class RoundtripTest {
+ protected final String testDir;
+
+ protected RoundtripTest(@Nonnull String testDir) {
+ this.testDir = testDir;
+ }
+
+ protected RoundtripTest() {
+ this.testDir = this.getClass().getSimpleName();
+ }
+
+ @Nonnull
+ protected String getInputFilename(@Nonnull String testName) {
+ return String.format("%s%s%sInput.smali", testDir, File.separatorChar, testName);
+ }
- baksmaliOptions options = new baksmaliOptions();
- options.useImplicitReferences = true;
+ @Nonnull
+ protected String getOutputFilename(@Nonnull String testName) {
+ return String.format("%s%s%sOutput.smali", testDir, File.separatorChar, testName);
+ }
+
+ protected void runTest(@Nonnull String testName) {
+ runTest(testName, new baksmaliOptions());
+ }
- StringWriter stringWriter = new StringWriter();
- IndentingWriter writer = new IndentingWriter(stringWriter);
- ClassDefinition classDefinition = new ClassDefinition(options, classDef);
- classDefinition.writeTo(writer);
- writer.close();
+ protected void runTest(@Nonnull String testName, @Nonnull baksmaliOptions options) {
+ try {
+ // Load file from resources as a stream
+ String inputFilename = getInputFilename(testName);
+ String input = BaksmaliTestUtils.readResourceFully(getInputFilename(testName));
+ String output;
+ if (getOutputFilename(testName).equals(inputFilename)) {
+ output = input;
+ } else {
+ output = BaksmaliTestUtils.readResourceFully(getOutputFilename(testName));
+ }
- Assert.assertEquals(TextUtils.normalizeWhitespace(text),
- TextUtils.normalizeWhitespace(stringWriter.toString()));
+ // Run smali, baksmali, and then compare strings are equal (minus comments/whitespace)
+ BaksmaliTestUtils.assertSmaliCompiledEquals(input, output, options, true);
+ } catch (IOException ex) {
+ Assert.fail();
+ } catch (RecognitionException ex) {
+ Assert.fail();
+ }
}
}
diff --git a/baksmali/src/test/java/org/jf/baksmali/SwitchTest.java b/baksmali/src/test/java/org/jf/baksmali/SwitchTest.java
new file mode 100644
index 00000000..48b64b22
--- /dev/null
+++ b/baksmali/src/test/java/org/jf/baksmali/SwitchTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.baksmali;
+
+import org.junit.Test;
+
+public class SwitchTest extends RoundtripTest {
+ @Test
+ public void testUnorderedSparseSwitch() {
+ runTest("UnorderedSparseSwitch");
+ }
+}
diff --git a/baksmali/src/test/resources/LambdaTest/HelloWorldLambda.smali b/baksmali/src/test/resources/LambdaTest/HelloWorldLambda.smali
new file mode 100644
index 00000000..d70ced50
--- /dev/null
+++ b/baksmali/src/test/resources/LambdaTest/HelloWorldLambda.smali
@@ -0,0 +1,55 @@
+.class public LHelloWorldLambda;
+
+#Ye olde hello world application (with lambdas!)
+#To assemble and run this on a phone or emulator:
+#
+#java -jar smali.jar -o classes.dex HelloWorldLambda.smali HelloWorldFunctionalInterface.smali
+#zip HelloWorld.zip classes.dex
+#adb push HelloWorld.zip /data/local
+#adb shell dalvikvm -cp /data/local/HelloWorld.zip HelloWorld
+#
+#if you get out of memory type errors when running smali.jar, try
+#java -Xmx512m -jar smali.jar HelloWorldLambda.smali
+#instead
+
+.super Ljava/lang/Object;
+
+.method public static doHelloWorld(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ .registers 6 # 4 parameters, 2 locals
+ liberate-variable v0, p0, "helloworld"
+
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+ return-void
+.end method
+
+.method public static main([Ljava/lang/String;)V
+ .registers 9 # 1 parameter, 8 locals
+
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+ const-string v1, "Hello World!"
+ const-string v2, "How" # vD
+ const-string v3, "are" # vE
+ const-string v4, "you" # vF
+ const-string v5, "doing?" # vG
+
+ capture-variable v1, "helloworld"
+
+ # TODO: do I need to pass the type of the lambda's functional interface here as a type id?
+ create-lambda v1, LHelloWorldLambda;->doHelloWorld(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ # Method descriptor is not required here, because only the single-abstract method is ever invoked.
+ invoke-lambda v1, {v2, v3, v4, v5}
+
+ box-lambda v6, v1
+ invoke-virtual {v6, v2, v3, v4, v5}, LHelloWorldFunctionalInterface;->applyFourStrings(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+
+ # FIXME: should be \HelloWorldFunctionalInterface; instead of L...;
+
+ # TODO: do we really need the type descriptor here at all?
+ unbox-lambda v7, v6, LHelloWorldFunctionalInterface;
+ invoke-lambda v7, {v2, v3, v4, v5}
+
+ return-void
+.end method
diff --git a/baksmali/src/test/resources/ManyRegistersTest/ManyRegisters.smali b/baksmali/src/test/resources/ManyRegistersTest/ManyRegisters.smali
new file mode 100644
index 00000000..7f7c7bb1
--- /dev/null
+++ b/baksmali/src/test/resources/ManyRegistersTest/ManyRegisters.smali
@@ -0,0 +1,7 @@
+.class LManyRegisters;
+.super Ljava/lang/Object;
+
+.method public manyRegisters()V
+ .registers 65535
+ return-void
+.end method \ No newline at end of file
diff --git a/baksmali/src/test/resources/MultiSwitchTest/MultiSwitchInput.dex b/baksmali/src/test/resources/MultiSwitchTest/MultiSwitchInput.dex
new file mode 100644
index 00000000..6ef3d349
--- /dev/null
+++ b/baksmali/src/test/resources/MultiSwitchTest/MultiSwitchInput.dex
Binary files differ
diff --git a/baksmali/src/test/resources/MultiSwitchTest/MultiSwitchInput.smali b/baksmali/src/test/resources/MultiSwitchTest/MultiSwitchInput.smali
new file mode 100644
index 00000000..b4fd27d8
--- /dev/null
+++ b/baksmali/src/test/resources/MultiSwitchTest/MultiSwitchInput.smali
@@ -0,0 +1,72 @@
+.class public LMultiSwitch;
+.super Ljava/lang/Object;
+.source "Format31t.smali"
+
+.method public multi-packed-switch()V
+ .registers 1
+ const p0, 0xc
+ packed-switch p0, :pswitch_data_12
+ goto :goto_b
+ :pswitch_7
+ return-void
+ :pswitch_8
+ return-void
+ :pswitch_9
+ return-void
+ :pswitch_a
+ return-void
+ :goto_b
+ packed-switch p0, :pswitch_data_12
+ nop
+ return-void
+ :pswitch_f
+ return-void
+ :pswitch_10
+ return-void
+ :pswitch_11
+ return-void
+ :pswitch_12
+ :pswitch_data_12
+ .packed-switch 0xa
+ :pswitch_7
+ :pswitch_8
+ :pswitch_9
+ :pswitch_a
+ .end packed-switch
+
+.end method
+
+.method public multi-sparse-switch()V
+ .registers 1
+ const p0, 0xd
+ sparse-switch p0, :sswitch_data_12
+ goto :goto_b
+ :sswitch_7
+ return-void
+ :sswitch_8
+ return-void
+ :sswitch_9
+ return-void
+ :sswitch_a
+ return-void
+ :goto_b
+ sparse-switch p0, :sswitch_data_12
+ nop
+ return-void
+ :sswitch_f
+ return-void
+ :sswitch_10
+ return-void
+ :sswitch_11
+ return-void
+
+ :sswitch_12
+
+ :sswitch_data_12
+ .sparse-switch
+ 0xa -> :sswitch_7
+ 0xf -> :sswitch_9
+ 0x14 -> :sswitch_8
+ 0x63 -> :sswitch_a
+ .end sparse-switch
+.end method \ No newline at end of file
diff --git a/baksmali/src/test/resources/MultiSwitchTest/MultiSwitchOutput.smali b/baksmali/src/test/resources/MultiSwitchTest/MultiSwitchOutput.smali
new file mode 100644
index 00000000..f3aeeed5
--- /dev/null
+++ b/baksmali/src/test/resources/MultiSwitchTest/MultiSwitchOutput.smali
@@ -0,0 +1,119 @@
+.class public LMultiSwitch;
+.super Ljava/lang/Object;
+.source "Format31t.smali"
+
+
+# virtual methods
+.method public multi-packed-switch()V
+ .registers 1
+
+ const p0, 0xc
+
+ packed-switch p0, :pswitch_data_14
+
+ goto :goto_b
+
+ :pswitch_7
+ return-void
+
+ :pswitch_8
+ return-void
+
+ :pswitch_9
+ return-void
+
+ :pswitch_a
+ return-void
+
+ :goto_b
+ packed-switch p0, :pswitch_data_20
+
+ nop
+
+ :pswitch_f
+ return-void
+
+ :pswitch_10
+ return-void
+
+ :pswitch_11
+ return-void
+
+ :pswitch_12
+ return-void
+
+ nop
+
+ :pswitch_data_14
+ .packed-switch 0xa
+ :pswitch_7
+ :pswitch_8
+ :pswitch_9
+ :pswitch_a
+ .end packed-switch
+
+ :pswitch_data_20
+ .packed-switch 0xa
+ :pswitch_f
+ :pswitch_10
+ :pswitch_11
+ :pswitch_12
+ .end packed-switch
+.end method
+
+.method public multi-sparse-switch()V
+ .registers 1
+
+ const p0, 0xd
+
+ sparse-switch p0, :sswitch_data_14
+
+ goto :goto_b
+
+ :sswitch_7
+ return-void
+
+ :sswitch_8
+ return-void
+
+ :sswitch_9
+ return-void
+
+ :sswitch_a
+ return-void
+
+ :goto_b
+ sparse-switch p0, :sswitch_data_26
+
+ nop
+
+ :sswitch_f
+ return-void
+
+ :sswitch_10
+ return-void
+
+ :sswitch_11
+ return-void
+
+ :sswitch_12
+ return-void
+
+ nop
+
+ :sswitch_data_14
+ .sparse-switch
+ 0xa -> :sswitch_7
+ 0xf -> :sswitch_9
+ 0x14 -> :sswitch_8
+ 0x63 -> :sswitch_a
+ .end sparse-switch
+
+ :sswitch_data_26
+ .sparse-switch
+ 0xa -> :sswitch_f
+ 0xf -> :sswitch_11
+ 0x14 -> :sswitch_10
+ 0x63 -> :sswitch_12
+ .end sparse-switch
+.end method
diff --git a/baksmali/src/test/resources/ParamListMethodNameTest/ParamListMethodName.smali b/baksmali/src/test/resources/ParamListMethodNameTest/ParamListMethodName.smali
new file mode 100644
index 00000000..85717155
--- /dev/null
+++ b/baksmali/src/test/resources/ParamListMethodNameTest/ParamListMethodName.smali
@@ -0,0 +1,5 @@
+.class Lblah;
+.super Ljava/lang/Object;
+
+.method public abstract II()V
+.end method \ No newline at end of file
diff --git a/baksmali/src/test/resources/SwitchTest/UnorderedSparseSwitchInput.smali b/baksmali/src/test/resources/SwitchTest/UnorderedSparseSwitchInput.smali
new file mode 100644
index 00000000..6e3d23d4
--- /dev/null
+++ b/baksmali/src/test/resources/SwitchTest/UnorderedSparseSwitchInput.smali
@@ -0,0 +1,35 @@
+.class public LUnorderedSparseSwitch;
+.super Ljava/lang/Object;
+
+.method public static test_sparse-switch()V
+ .registers 1
+
+ const v0, 13
+
+ sparse-switch v0, :SparseSwitch
+
+:Label10
+ return-void
+
+:Label20
+ return-void
+
+:Label15
+ return-void
+
+:Label13
+ return-void
+
+:Label99
+ return-void
+
+# Note: unordered keys
+:SparseSwitch
+ .sparse-switch
+ 10 -> :Label10
+ 20 -> :Label20
+ 15 -> :Label15
+ 99 -> :Label99
+ 13 -> :Label13
+ .end sparse-switch
+.end method \ No newline at end of file
diff --git a/baksmali/src/test/resources/SwitchTest/UnorderedSparseSwitchOutput.smali b/baksmali/src/test/resources/SwitchTest/UnorderedSparseSwitchOutput.smali
new file mode 100644
index 00000000..c4c455b6
--- /dev/null
+++ b/baksmali/src/test/resources/SwitchTest/UnorderedSparseSwitchOutput.smali
@@ -0,0 +1,28 @@
+.class public LUnorderedSparseSwitch;
+.super Ljava/lang/Object;
+.method public static test_sparse-switch()V
+.registers 1
+const v0, 0xd
+sparse-switch v0, :sswitch_data_c
+:sswitch_6
+return-void
+:sswitch_7
+return-void
+:sswitch_8
+return-void
+:sswitch_9
+return-void
+:sswitch_a
+return-void
+nop
+
+# Note: ordered keys
+:sswitch_data_c
+.sparse-switch
+0xa -> :sswitch_6
+0xd -> :sswitch_9
+0xf -> :sswitch_8
+0x14 -> :sswitch_7
+0x63 -> :sswitch_a
+.end sparse-switch
+.end method \ No newline at end of file