mirror of
https://github.com/revanced/Apktool.git
synced 2024-11-19 10:59:28 +01:00
Duplicate a switch payload that is refered to multiple times
This commit is contained in:
parent
395043667a
commit
1c084171ed
@ -130,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;
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
package org.jf.baksmali;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.antlr.runtime.RecognitionException;
|
||||
@ -40,7 +41,9 @@ 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 {
|
||||
@ -50,24 +53,9 @@ public class BaksmaliTestUtils {
|
||||
ClassDef classDef = SmaliTestUtils.compileSmali(source, options.apiLevel,
|
||||
options.experimental);
|
||||
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
IndentingWriter writer = new IndentingWriter(stringWriter);
|
||||
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
|
||||
classDefinition.writeTo(writer);
|
||||
writer.close();
|
||||
|
||||
// Remove unnecessary whitespace and optionally strip all comments from smali file
|
||||
String normalizedExpected = expected;
|
||||
if (stripComments) {
|
||||
normalizedExpected = TextUtils.stripComments(normalizedExpected);
|
||||
}
|
||||
normalizedExpected = TextUtils.normalizeWhitespace(normalizedExpected);
|
||||
|
||||
String normalizedActual = stringWriter.toString();
|
||||
if (stripComments) {
|
||||
normalizedActual = TextUtils.stripComments(normalizedActual);
|
||||
}
|
||||
normalizedActual = TextUtils.normalizeWhitespace(normalizedActual);
|
||||
String normalizedActual = getNormalizedSmali(classDef, options, stripComments);
|
||||
String normalizedExpected = normalizeSmali(expected, stripComments);
|
||||
|
||||
// Assert that normalized strings are now equal
|
||||
Assert.assertEquals(normalizedExpected, normalizedActual);
|
||||
@ -84,6 +72,48 @@ public class BaksmaliTestUtils {
|
||||
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(); }
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
@ -31,14 +31,12 @@
|
||||
|
||||
package org.jf.baksmali;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
import org.antlr.runtime.RecognitionException;
|
||||
import org.junit.Assert;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* A base test class for performing a roundtrip assembly/disassembly
|
||||
@ -78,12 +76,12 @@ public abstract class RoundtripTest {
|
||||
try {
|
||||
// Load file from resources as a stream
|
||||
String inputFilename = getInputFilename(testName);
|
||||
String input = readResourceFully(getInputFilename(testName));
|
||||
String input = BaksmaliTestUtils.readResourceFully(getInputFilename(testName));
|
||||
String output;
|
||||
if (getOutputFilename(testName).equals(inputFilename)) {
|
||||
output = input;
|
||||
} else {
|
||||
output = readResourceFully(getOutputFilename(testName));
|
||||
output = BaksmaliTestUtils.readResourceFully(getOutputFilename(testName));
|
||||
}
|
||||
|
||||
// Run smali, baksmali, and then compare strings are equal (minus comments/whitespace)
|
||||
@ -94,21 +92,4 @@ public abstract class RoundtripTest {
|
||||
Assert.fail();
|
||||
}
|
||||
}
|
||||
|
||||
@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 {
|
||||
InputStream smaliStream = RoundtripTest.class.getClassLoader().
|
||||
getResourceAsStream(fileName);
|
||||
if (smaliStream == null) {
|
||||
Assert.fail("Could not load " + fileName);
|
||||
}
|
||||
|
||||
return new String(ByteStreams.toByteArray(smaliStream), encoding);
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
@ -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
|
@ -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
|
Loading…
Reference in New Issue
Block a user