Add ability to converting MethodImplementation to MutableMethodImplementation

This commit is contained in:
Ben Gruver 2013-09-14 00:32:32 -07:00
parent 9a2de93a99
commit 6762350ca0
9 changed files with 1099 additions and 324 deletions

View File

@ -59,6 +59,7 @@ public abstract class BuilderInstruction implements Instruction {
return getFormat().size / 2; return getFormat().size / 2;
} }
@Nonnull
public MethodLocation getLocation() { public MethodLocation getLocation() {
if (location == null) { if (location == null) {
throw new IllegalStateException("Cannot get the location of an instruction that hasn't been added to a " + throw new IllegalStateException("Cannot get the location of an instruction that hasn't been added to a " +

View File

@ -4,16 +4,19 @@ import com.google.common.base.Function;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import org.jf.dexlib2.DebugItemType;
import org.jf.dexlib2.Opcode; import org.jf.dexlib2.Opcode;
import org.jf.dexlib2.builder.instruction.BuilderInstruction10x; import org.jf.dexlib2.builder.debug.*;
import org.jf.dexlib2.builder.instruction.BuilderInstruction20t; import org.jf.dexlib2.builder.instruction.*;
import org.jf.dexlib2.builder.instruction.BuilderInstruction30t;
import org.jf.dexlib2.iface.ExceptionHandler; import org.jf.dexlib2.iface.ExceptionHandler;
import org.jf.dexlib2.iface.MethodImplementation; import org.jf.dexlib2.iface.MethodImplementation;
import org.jf.dexlib2.iface.TryBlock; import org.jf.dexlib2.iface.TryBlock;
import org.jf.dexlib2.iface.debug.DebugItem; import org.jf.dexlib2.iface.debug.*;
import org.jf.dexlib2.iface.instruction.Instruction; import org.jf.dexlib2.iface.instruction.Instruction;
import org.jf.dexlib2.iface.instruction.SwitchElement;
import org.jf.dexlib2.iface.instruction.formats.*;
import org.jf.dexlib2.iface.reference.TypeReference; import org.jf.dexlib2.iface.reference.TypeReference;
import org.jf.util.ExceptionWithContext;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -26,7 +29,71 @@ public class MutableMethodImplementation implements MethodImplementation {
private boolean fixInstructions = true; private boolean fixInstructions = true;
public MutableMethodImplementation(@Nonnull MethodImplementation methodImplementation) { public MutableMethodImplementation(@Nonnull MethodImplementation methodImplementation) {
throw new UnsupportedOperationException("not implemented yet."); this.registerCount = methodImplementation.getRegisterCount();
int codeAddress = 0;
int index = 0;
for (Instruction instruction: methodImplementation.getInstructions()) {
codeAddress += instruction.getCodeUnits();
index++;
instructionList.add(new MethodLocation(null, codeAddress, index));
}
final int[] codeAddressToIndex = new int[codeAddress+1];
Arrays.fill(codeAddressToIndex, -1);
for (int i=0; i<instructionList.size(); i++) {
codeAddressToIndex[instructionList.get(i).codeAddress] = i;
}
List<Task> switchPayloadTasks = Lists.newArrayList();
index = 0;
for (final Instruction instruction: methodImplementation.getInstructions()) {
final MethodLocation location = instructionList.get(index);
final Opcode opcode = instruction.getOpcode();
if (opcode == Opcode.PACKED_SWITCH_PAYLOAD || opcode == Opcode.SPARSE_SWITCH_PAYLOAD) {
switchPayloadTasks.add(new Task() {
@Override public void perform() {
convertAndSetInstruction(location, codeAddressToIndex, instruction);
}
});
} else {
convertAndSetInstruction(location, codeAddressToIndex, instruction);
}
index++;
}
// the switch instructions must be converted last, so that any switch statements that refer to them have
// created the referring labels that we look for
for (Task switchPayloadTask: switchPayloadTasks) {
switchPayloadTask.perform();
}
for (DebugItem debugItem: methodImplementation.getDebugItems()) {
int debugCodeAddress = debugItem.getCodeAddress();
int locationIndex = mapCodeAddressToIndex(codeAddressToIndex, debugCodeAddress);
MethodLocation debugLocation = instructionList.get(locationIndex);
BuilderDebugItem builderDebugItem = convertDebugItem(debugLocation, debugItem);
debugLocation.getDebugItems().add(builderDebugItem);
builderDebugItem.location = debugLocation;
}
for (TryBlock<? extends ExceptionHandler> tryBlock: methodImplementation.getTryBlocks()) {
Label startLabel = newLabel(codeAddressToIndex, tryBlock.getStartCodeAddress());
Label endLabel = newLabel(codeAddressToIndex, tryBlock.getStartCodeAddress() + tryBlock.getCodeUnitCount());
for (ExceptionHandler exceptionHandler: tryBlock.getExceptionHandlers()) {
tryBlocks.add(new BuilderTryBlock(startLabel, endLabel,
exceptionHandler.getExceptionTypeReference(),
newLabel(codeAddressToIndex, exceptionHandler.getHandlerCodeAddress())));
}
}
}
private interface Task {
void perform();
} }
public MutableMethodImplementation(int registerCount) { public MutableMethodImplementation(int registerCount) {
@ -361,4 +428,485 @@ public class MutableMethodImplementation implements MethodImplementation {
fixInstructions = false; fixInstructions = false;
} }
private int mapCodeAddressToIndex(@Nonnull int[] codeAddressToIndex, int codeAddress) {
int index;
do {
index = codeAddressToIndex[codeAddress];
if (index < 0) {
codeAddress--;
} else {
return index;
}
} while (true);
}
@Nonnull
private Label newLabel(@Nonnull int[] codeAddressToIndex, int codeAddress) {
MethodLocation referent = instructionList.get(mapCodeAddressToIndex(codeAddressToIndex, codeAddress));
return referent.addNewLabel();
}
private static class SwitchPayloadReferenceLabel extends Label {
@Nonnull public MethodLocation switchLocation;
}
@Nonnull
public Label newSwitchPayloadReferenceLabel(@Nonnull int[] codeAddressToIndex, int codeAddress) {
MethodLocation referent = instructionList.get(mapCodeAddressToIndex(codeAddressToIndex, codeAddress));
Label label = new SwitchPayloadReferenceLabel();
referent.getLabels().add(label);
return label;
}
private void setInstruction(@Nonnull MethodLocation location, @Nonnull BuilderInstruction instruction) {
location.instruction = instruction;
instruction.location = location;
}
private void convertAndSetInstruction(@Nonnull MethodLocation location, int[] codeAddressToIndex,
@Nonnull Instruction instruction) {
switch (instruction.getOpcode().format) {
case Format10t:
setInstruction(location, newBuilderInstruction10t(location.codeAddress, codeAddressToIndex,
(Instruction10t)instruction));
return;
case Format10x:
setInstruction(location, newBuilderInstruction10x((Instruction10x)instruction));
return;
case Format11n:
setInstruction(location, newBuilderInstruction11n((Instruction11n)instruction));
return;
case Format11x:
setInstruction(location, newBuilderInstruction11x((Instruction11x)instruction));
return;
case Format12x:
setInstruction(location, newBuilderInstruction12x((Instruction12x)instruction));
return;
case Format20bc:
setInstruction(location, newBuilderInstruction20bc((Instruction20bc)instruction));
return;
case Format20t:
setInstruction(location, newBuilderInstruction20t(location.codeAddress, codeAddressToIndex,
(Instruction20t)instruction));
return;
case Format21c:
setInstruction(location, newBuilderInstruction21c((Instruction21c)instruction));
return;
case Format21ih:
setInstruction(location, newBuilderInstruction21ih((Instruction21ih)instruction));
return;
case Format21lh:
setInstruction(location, newBuilderInstruction10x((Instruction10x)instruction));
return;
case Format21s:
setInstruction(location, newBuilderInstruction21s((Instruction21s)instruction));
return;
case Format21t:
setInstruction(location, newBuilderInstruction21t(location.codeAddress, codeAddressToIndex,
(Instruction21t)instruction));
return;
case Format22b:
setInstruction(location, newBuilderInstruction22b((Instruction22b)instruction));
return;
case Format22c:
setInstruction(location, newBuilderInstruction22c((Instruction22c)instruction));
return;
case Format22s:
setInstruction(location, newBuilderInstruction22s((Instruction22s)instruction));
return;
case Format22t:
setInstruction(location, newBuilderInstruction22t(location.codeAddress, codeAddressToIndex,
(Instruction22t)instruction));
return;
case Format22x:
setInstruction(location, newBuilderInstruction22x((Instruction22x)instruction));
return;
case Format23x:
setInstruction(location, newBuilderInstruction23x((Instruction23x)instruction));
return;
case Format30t:
setInstruction(location, newBuilderInstruction30t(location.codeAddress, codeAddressToIndex,
(Instruction30t)instruction));
return;
case Format31c:
setInstruction(location, newBuilderInstruction31c((Instruction31c)instruction));
return;
case Format31i:
setInstruction(location, newBuilderInstruction31i((Instruction31i)instruction));
return;
case Format31t:
setInstruction(location, newBuilderInstruction31t(location.codeAddress, codeAddressToIndex,
(Instruction31t)instruction));
return;
case Format32x:
setInstruction(location, newBuilderInstruction32x((Instruction32x)instruction));
return;
case Format35c:
setInstruction(location, newBuilderInstruction35c((Instruction35c)instruction));
return;
case Format3rc:
setInstruction(location, newBuilderInstruction3rc((Instruction3rc)instruction));
return;
case Format51l:
setInstruction(location, newBuilderInstruction51l((Instruction51l)instruction));
return;
case PackedSwitchPayload:
setInstruction(location,
newBuilderPackedSwitchPayload(location, codeAddressToIndex, (PackedSwitchPayload)instruction));
return;
case SparseSwitchPayload:
setInstruction(location,
newBuilderSparseSwitchPayload(location, codeAddressToIndex, (SparseSwitchPayload)instruction));
case ArrayPayload:
setInstruction(location, newBuilderArrayPayload((ArrayPayload)instruction));
default:
throw new ExceptionWithContext("Instruction format %s not supported", instruction.getOpcode().format);
}
}
@Nonnull
private BuilderInstruction10t newBuilderInstruction10t(int codeAddress, int[] codeAddressToIndex,
@Nonnull Instruction10t instruction) {
return new BuilderInstruction10t(
instruction.getOpcode(),
newLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset()));
}
@Nonnull
private BuilderInstruction10x newBuilderInstruction10x(@Nonnull Instruction10x instruction) {
return new BuilderInstruction10x(
instruction.getOpcode());
}
@Nonnull
private BuilderInstruction11n newBuilderInstruction11n(@Nonnull Instruction11n instruction) {
return new BuilderInstruction11n(
instruction.getOpcode(),
instruction.getRegisterA(),
instruction.getNarrowLiteral());
}
@Nonnull
private BuilderInstruction11x newBuilderInstruction11x(@Nonnull Instruction11x instruction) {
return new BuilderInstruction11x(
instruction.getOpcode(),
instruction.getRegisterA());
}
@Nonnull
private BuilderInstruction12x newBuilderInstruction12x(@Nonnull Instruction12x instruction) {
return new BuilderInstruction12x(
instruction.getOpcode(),
instruction.getRegisterA(),
instruction.getRegisterB());
}
@Nonnull
private BuilderInstruction20bc newBuilderInstruction20bc(@Nonnull Instruction20bc instruction) {
return new BuilderInstruction20bc(
instruction.getOpcode(),
instruction.getVerificationError(),
instruction.getReference());
}
@Nonnull
private BuilderInstruction20t newBuilderInstruction20t(int codeAddress, int[] codeAddressToIndex,
@Nonnull Instruction20t instruction) {
return new BuilderInstruction20t(
instruction.getOpcode(),
newLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset()));
}
@Nonnull
private BuilderInstruction21c newBuilderInstruction21c(@Nonnull Instruction21c instruction) {
return new BuilderInstruction21c(
instruction.getOpcode(),
instruction.getRegisterA(),
instruction.getReference());
}
@Nonnull
private BuilderInstruction21ih newBuilderInstruction21ih(@Nonnull Instruction21ih instruction) {
return new BuilderInstruction21ih(
instruction.getOpcode(),
instruction.getRegisterA(),
instruction.getHatLiteral());
}
@Nonnull
private BuilderInstruction21lh newBuilderInstruction21lh(@Nonnull Instruction21lh instruction) {
return new BuilderInstruction21lh(
instruction.getOpcode(),
instruction.getRegisterA(),
instruction.getHatLiteral());
}
@Nonnull
private BuilderInstruction21s newBuilderInstruction21s(@Nonnull Instruction21s instruction) {
return new BuilderInstruction21s(
instruction.getOpcode(),
instruction.getRegisterA(),
instruction.getNarrowLiteral());
}
@Nonnull
private BuilderInstruction21t newBuilderInstruction21t(int codeAddress, int[] codeAddressToIndex,
@Nonnull Instruction21t instruction) {
return new BuilderInstruction21t(
instruction.getOpcode(),
instruction.getRegisterA(),
newLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset()));
}
@Nonnull
private BuilderInstruction22b newBuilderInstruction22b(@Nonnull Instruction22b instruction) {
return new BuilderInstruction22b(
instruction.getOpcode(),
instruction.getRegisterA(),
instruction.getRegisterB(),
instruction.getNarrowLiteral());
}
@Nonnull
private BuilderInstruction22c newBuilderInstruction22c(@Nonnull Instruction22c instruction) {
return new BuilderInstruction22c(
instruction.getOpcode(),
instruction.getRegisterA(),
instruction.getRegisterB(),
instruction.getReference());
}
@Nonnull
private BuilderInstruction22s newBuilderInstruction22s(@Nonnull Instruction22s instruction) {
return new BuilderInstruction22s(
instruction.getOpcode(),
instruction.getRegisterA(),
instruction.getRegisterB(),
instruction.getNarrowLiteral());
}
@Nonnull
private BuilderInstruction22t newBuilderInstruction22t(int codeAddress, int[] codeAddressToIndex,
@Nonnull Instruction22t instruction) {
return new BuilderInstruction22t(
instruction.getOpcode(),
instruction.getRegisterA(),
instruction.getRegisterB(),
newLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset()));
}
@Nonnull
private BuilderInstruction22x newBuilderInstruction22x(@Nonnull Instruction22x instruction) {
return new BuilderInstruction22x(
instruction.getOpcode(),
instruction.getRegisterA(),
instruction.getRegisterB());
}
@Nonnull
private BuilderInstruction23x newBuilderInstruction23x(@Nonnull Instruction23x instruction) {
return new BuilderInstruction23x(
instruction.getOpcode(),
instruction.getRegisterA(),
instruction.getRegisterB(),
instruction.getRegisterC());
}
@Nonnull
private BuilderInstruction30t newBuilderInstruction30t(int codeAddress, int[] codeAddressToIndex,
@Nonnull Instruction30t instruction) {
return new BuilderInstruction30t(
instruction.getOpcode(),
newLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset()));
}
@Nonnull
private BuilderInstruction31c newBuilderInstruction31c(@Nonnull Instruction31c instruction) {
return new BuilderInstruction31c(
instruction.getOpcode(),
instruction.getRegisterA(),
instruction.getReference());
}
@Nonnull
private BuilderInstruction31i newBuilderInstruction31i(@Nonnull Instruction31i instruction) {
return new BuilderInstruction31i(
instruction.getOpcode(),
instruction.getRegisterA(),
instruction.getNarrowLiteral());
}
@Nonnull
private BuilderInstruction31t newBuilderInstruction31t(int codeAddress, int[] codeAddressToIndex,
@Nonnull Instruction31t instruction) {
Label newLabel;
if (instruction.getOpcode() != Opcode.FILL_ARRAY_DATA) {
// if it's a sparse switch or packed switch
newLabel = newSwitchPayloadReferenceLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset());
} else {
newLabel = newLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset());
}
return new BuilderInstruction31t(
instruction.getOpcode(),
instruction.getRegisterA(),
newLabel);
}
@Nonnull
private BuilderInstruction32x newBuilderInstruction32x(@Nonnull Instruction32x instruction) {
return new BuilderInstruction32x(
instruction.getOpcode(),
instruction.getRegisterA(),
instruction.getRegisterB());
}
@Nonnull
private BuilderInstruction35c newBuilderInstruction35c(@Nonnull Instruction35c instruction) {
return new BuilderInstruction35c(
instruction.getOpcode(),
instruction.getRegisterCount(),
instruction.getRegisterC(),
instruction.getRegisterD(),
instruction.getRegisterE(),
instruction.getRegisterF(),
instruction.getRegisterG(),
instruction.getReference());
}
@Nonnull
private BuilderInstruction3rc newBuilderInstruction3rc(@Nonnull Instruction3rc instruction) {
return new BuilderInstruction3rc(
instruction.getOpcode(),
instruction.getStartRegister(),
instruction.getRegisterCount(),
instruction.getReference());
}
@Nonnull
private BuilderInstruction51l newBuilderInstruction51l(@Nonnull Instruction51l instruction) {
return new BuilderInstruction51l(
instruction.getOpcode(),
instruction.getRegisterA(),
instruction.getWideLiteral());
}
@Nullable
private MethodLocation findSwitchForPayload(@Nonnull MethodLocation payloadLocation) {
MethodLocation location = payloadLocation;
MethodLocation switchLocation = null;
do {
for (Label label: location.getLabels()) {
if (label instanceof SwitchPayloadReferenceLabel) {
if (switchLocation != null) {
throw new IllegalStateException("Multiple switch instructions refer to the same payload. " +
"This is not currently supported. Please file a bug :)");
}
switchLocation = ((SwitchPayloadReferenceLabel)label).switchLocation;
}
}
// A switch instruction can refer to the payload instruction itself, or to a nop before the payload
// instruction.
// We need to search for all occurrences of a switch reference, so we can detect when multiple switch
// statements refer to the same payload
// TODO: confirm that it could refer to the first NOP in a series of NOPs preceding the payload
if (location.index == 0) {
return switchLocation;
}
location = instructionList.get(location.index - 1);
if (location.instruction == null || location.instruction.getOpcode() != Opcode.NOP) {
return switchLocation;
}
} while (true);
}
@Nonnull
private BuilderPackedSwitchPayload newBuilderPackedSwitchPayload(@Nonnull MethodLocation location,
@Nonnull int[] codeAddressToIndex,
@Nonnull PackedSwitchPayload instruction) {
List<? extends SwitchElement> switchElements = instruction.getSwitchElements();
if (switchElements.size() == 0) {
return new BuilderPackedSwitchPayload(0, null);
}
MethodLocation switchLocation = findSwitchForPayload(location);
int baseAddress;
if (switchLocation == null) {
baseAddress = 0;
} else {
baseAddress = switchLocation.codeAddress;
}
List<Label> labels = Lists.newArrayList();
for (SwitchElement element: switchElements) {
labels.add(newLabel(codeAddressToIndex, element.getOffset() - baseAddress));
}
return new BuilderPackedSwitchPayload(switchElements.get(0).getKey(), labels);
}
@Nonnull
private BuilderSparseSwitchPayload newBuilderSparseSwitchPayload(@Nonnull MethodLocation location,
@Nonnull int[] codeAddressToIndex,
@Nonnull SparseSwitchPayload instruction) {
List<? extends SwitchElement> switchElements = instruction.getSwitchElements();
if (switchElements.size() == 0) {
return new BuilderSparseSwitchPayload(null);
}
MethodLocation switchLocation = findSwitchForPayload(location);
int baseAddress;
if (switchLocation == null) {
baseAddress = 0;
} else {
baseAddress = switchLocation.codeAddress;
}
List<SwitchLabelElement> labelElements = Lists.newArrayList();
for (SwitchElement element: switchElements) {
labelElements.add(new SwitchLabelElement(element.getKey(),
newLabel(codeAddressToIndex, element.getOffset() - baseAddress)));
}
return new BuilderSparseSwitchPayload(labelElements);
}
@Nonnull
private BuilderArrayPayload newBuilderArrayPayload(@Nonnull ArrayPayload instruction) {
return new BuilderArrayPayload(instruction.getElementWidth(), instruction.getArrayElements());
}
@Nonnull
private BuilderDebugItem convertDebugItem(@Nonnull MethodLocation location, @Nonnull DebugItem debugItem) {
switch (debugItem.getDebugItemType()) {
case DebugItemType.START_LOCAL: {
StartLocal startLocal = (StartLocal)debugItem;
return new BuilderStartLocal(location, startLocal.getRegister(), startLocal.getNameReference(),
startLocal.getTypeReference(), startLocal.getSignatureReference());
}
case DebugItemType.END_LOCAL: {
EndLocal endLocal = (EndLocal)debugItem;
return new BuilderEndLocal(location, endLocal.getRegister());
}
case DebugItemType.RESTART_LOCAL: {
RestartLocal restartLocal = (RestartLocal)debugItem;
return new BuilderRestartLocal(location, restartLocal.getRegister());
}
case DebugItemType.PROLOGUE_END:
return new BuilderPrologueEnd(location);
case DebugItemType.EPILOGUE_BEGIN:
return new BuilderEpilogueBegin(location);
case DebugItemType.LINE_NUMBER: {
LineNumber lineNumber = (LineNumber)debugItem;
return new BuilderLineNumber(location, lineNumber.getLineNumber());
}
case DebugItemType.SET_SOURCE_FILE: {
SetSourceFile setSourceFile = (SetSourceFile)debugItem;
return new BuilderSetSourceFile(location, setSourceFile.getSourceFileReference());
}
default:
throw new ExceptionWithContext("Invalid debug item type: " + debugItem.getDebugItemType());
}
}
} }

View File

@ -792,7 +792,7 @@ public abstract class DexWriter<
if (instruction.getOpcode() == Opcode.CONST_STRING) { if (instruction.getOpcode() == Opcode.CONST_STRING) {
if (stringSection.getItemIndex( if (stringSection.getItemIndex(
(StringRef)((ReferenceInstruction)instruction).getReference()) > 65536) { (StringRef)((ReferenceInstruction)instruction).getReference()) >= 65536) {
methodImplementation.replaceInstruction(i, new BuilderInstruction31c(Opcode.CONST_STRING_JUMBO, methodImplementation.replaceInstruction(i, new BuilderInstruction31c(Opcode.CONST_STRING_JUMBO,
((OneRegisterInstruction)instruction).getRegisterA(), ((OneRegisterInstruction)instruction).getRegisterA(),
((ReferenceInstruction)instruction).getReference())); ((ReferenceInstruction)instruction).getReference()));

View File

@ -73,11 +73,21 @@ public class BuilderClassDef extends BaseTypeReference implements ClassDef {
this.interfaces = interfaces; this.interfaces = interfaces;
this.sourceFile = sourceFile; this.sourceFile = sourceFile;
this.annotations = annotations; this.annotations = annotations;
if (fields == null) {
this.staticFields = ImmutableSortedSet.of();
this.instanceFields = ImmutableSortedSet.of();
} else {
this.staticFields = ImmutableSortedSet.copyOf(Iterables.filter(fields, FieldUtil.FIELD_IS_STATIC)); this.staticFields = ImmutableSortedSet.copyOf(Iterables.filter(fields, FieldUtil.FIELD_IS_STATIC));
this.instanceFields = ImmutableSortedSet.copyOf(Iterables.filter(fields, FieldUtil.FIELD_IS_INSTANCE)); this.instanceFields = ImmutableSortedSet.copyOf(Iterables.filter(fields, FieldUtil.FIELD_IS_INSTANCE));
}
if (methods == null) {
this.directMethods = ImmutableSortedSet.of();
this.virtualMethods = ImmutableSortedSet.of();
} else {
this.directMethods = ImmutableSortedSet.copyOf(Iterables.filter(methods, MethodUtil.METHOD_IS_DIRECT)); this.directMethods = ImmutableSortedSet.copyOf(Iterables.filter(methods, MethodUtil.METHOD_IS_DIRECT));
this.virtualMethods = ImmutableSortedSet.copyOf(Iterables.filter(methods, MethodUtil.METHOD_IS_VIRTUAL)); this.virtualMethods = ImmutableSortedSet.copyOf(Iterables.filter(methods, MethodUtil.METHOD_IS_VIRTUAL));
} }
}
@Nonnull @Override public String getType() { return type.getType(); } @Nonnull @Override public String getType() { return type.getType(); }
@Override public int getAccessFlags() { return accessFlags; } @Override public int getAccessFlags() { return accessFlags; }

View File

@ -0,0 +1,105 @@
package org.jf.dexlib2.writer.io;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
public class MemoryDataStore implements DexDataStore {
private byte[] buf;
public MemoryDataStore() {
this(1024 * 1024);
}
public MemoryDataStore(int initialCapacity) {
buf = new byte[initialCapacity];
}
public byte[] getData() {
return buf;
}
@Nonnull @Override public OutputStream outputAt(final int offset) {
return new OutputStream() {
private int position = offset;
@Override public void write(int b) throws IOException {
growBufferIfNeeded(position);
buf[position++] = (byte)b;
}
@Override public void write(byte[] b) throws IOException {
growBufferIfNeeded(position + b.length);
System.arraycopy(b, 0, buf, position, b.length);
position += b.length;
}
@Override public void write(byte[] b, int off, int len) throws IOException {
growBufferIfNeeded(position + len);
System.arraycopy(b, off, buf, position, len);
position += len;
}
};
}
private void growBufferIfNeeded(int index) {
if (index < buf.length) {
return;
}
buf = Arrays.copyOf(buf, (int)((index + 1) * 1.2));
}
@Nonnull @Override public InputStream readAt(final int offset) {
return new InputStream() {
private int position = offset;
@Override public int read() throws IOException {
if (position >= buf.length) {
return -1;
}
return buf[position++];
}
@Override public int read(byte[] b) throws IOException {
int readLength = Math.min(b.length, buf.length - position);
if (readLength <= 0) {
if (position >= buf.length) {
return -1;
}
return 0;
}
System.arraycopy(buf, position, b, 0, readLength);
position += readLength;
return readLength;
}
@Override public int read(byte[] b, int off, int len) throws IOException {
int readLength = Math.min(len, buf.length - position);
if (readLength <= 0) {
if (position >= buf.length) {
return -1;
}
return 0;
}
System.arraycopy(buf, position, b, 0, readLength);
position += readLength;
return readLength;
}
@Override public long skip(long n) throws IOException {
int skipLength = (int)Math.min(n, buf.length - position);
position += skipLength;
return skipLength;
}
@Override public int available() throws IOException {
return buf.length - position;
}
};
}
@Override public void close() throws IOException {
// no-op
}
}

View File

@ -0,0 +1,122 @@
package org.jf.dexlib2.builder;
import com.google.common.collect.Lists;
import junit.framework.Assert;
import org.jf.dexlib2.Opcode;
import org.jf.dexlib2.builder.instruction.BuilderInstruction10t;
import org.jf.dexlib2.builder.instruction.BuilderInstruction10x;
import org.jf.dexlib2.builder.instruction.BuilderInstruction20t;
import org.jf.dexlib2.iface.MethodImplementation;
import org.jf.dexlib2.iface.instruction.Instruction;
import org.jf.dexlib2.iface.instruction.OffsetInstruction;
import org.junit.Test;
import java.util.List;
public class FixGotoTest {
@Test
public void testFixGotoToGoto16() {
MethodImplementationBuilder builder = new MethodImplementationBuilder(1);
Label gotoTarget = builder.getLabel("gotoTarget");
builder.addInstruction(new BuilderInstruction10t(Opcode.GOTO, gotoTarget));
for (int i=0; i<500; i++) {
builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
}
builder.addLabel("gotoTarget");
builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID));
MethodImplementation impl = builder.getMethodImplementation();
List<? extends Instruction> instructions = Lists.newArrayList(impl.getInstructions());
Assert.assertEquals(502, instructions.size());
Assert.assertEquals(Opcode.GOTO_16, instructions.get(0).getOpcode());
Assert.assertEquals(502, ((OffsetInstruction)instructions.get(0)).getCodeOffset());
}
@Test
public void testFixGotoToGoto32() {
MethodImplementationBuilder builder = new MethodImplementationBuilder(1);
Label gotoTarget = builder.getLabel("gotoTarget");
builder.addInstruction(new BuilderInstruction10t(Opcode.GOTO, gotoTarget));
for (int i=0; i<70000; i++) {
builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
}
builder.addLabel("gotoTarget");
builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID));
MethodImplementation impl = builder.getMethodImplementation();
List<? extends Instruction> instructions = Lists.newArrayList(impl.getInstructions());
Assert.assertEquals(70002, instructions.size());
Assert.assertEquals(Opcode.GOTO_32, instructions.get(0).getOpcode());
Assert.assertEquals(70003, ((OffsetInstruction)instructions.get(0)).getCodeOffset());
}
@Test
public void testFixGoto16ToGoto32() {
MethodImplementationBuilder builder = new MethodImplementationBuilder(1);
Label gotoTarget = builder.getLabel("gotoTarget");
builder.addInstruction(new BuilderInstruction20t(Opcode.GOTO_16, gotoTarget));
for (int i=0; i<70000; i++) {
builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
}
builder.addLabel("gotoTarget");
builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID));
MethodImplementation impl = builder.getMethodImplementation();
List<? extends Instruction> instructions = Lists.newArrayList(impl.getInstructions());
Assert.assertEquals(70002, instructions.size());
Assert.assertEquals(Opcode.GOTO_32, instructions.get(0).getOpcode());
Assert.assertEquals(70003, ((OffsetInstruction)instructions.get(0)).getCodeOffset());
}
@Test
public void testFixGotoCascading() {
MethodImplementationBuilder builder = new MethodImplementationBuilder(1);
Label goto16Target = builder.getLabel("goto16Target");
builder.addInstruction(new BuilderInstruction20t(Opcode.GOTO_16, goto16Target));
for (int i=0; i<1000; i++) {
builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
}
Label gotoTarget = builder.getLabel("gotoTarget");
builder.addInstruction(new BuilderInstruction10t(Opcode.GOTO, gotoTarget));
for (int i=0; i<499; i++) {
builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
}
builder.addLabel("gotoTarget");
for (int i=0; i<31265; i++) {
builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
}
builder.addLabel("goto16Target");
builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID));
MethodImplementation impl = builder.getMethodImplementation();
List<? extends Instruction> instructions = Lists.newArrayList(impl.getInstructions());
Assert.assertEquals(32767, instructions.size());
Assert.assertEquals(Opcode.GOTO_32, instructions.get(0).getOpcode());
Assert.assertEquals(32769, ((OffsetInstruction)instructions.get(0)).getCodeOffset());
}
}

View File

@ -0,0 +1,140 @@
/*
* Copyright 2013, 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.dexlib2.builder;
import com.google.common.collect.Lists;
import junit.framework.Assert;
import org.jf.dexlib2.Opcode;
import org.jf.dexlib2.builder.instruction.BuilderInstruction10t;
import org.jf.dexlib2.builder.instruction.BuilderInstruction10x;
import org.jf.dexlib2.iface.ExceptionHandler;
import org.jf.dexlib2.iface.MethodImplementation;
import org.jf.dexlib2.iface.TryBlock;
import org.jf.dexlib2.iface.debug.DebugItem;
import org.jf.dexlib2.iface.debug.LineNumber;
import org.jf.dexlib2.iface.instruction.Instruction;
import org.jf.dexlib2.iface.instruction.OffsetInstruction;
import org.junit.Test;
import java.util.List;
public class FixOffsetsTest {
@Test
public void testFixOffsets() {
MethodImplementationBuilder builder = new MethodImplementationBuilder(1);
Label firstGotoTarget = builder.getLabel("firstGotoTarget");
builder.addInstruction(new BuilderInstruction10t(Opcode.GOTO, firstGotoTarget));
builder.addLineNumber(1);
for (int i=0; i<250; i++) {
builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
}
builder.addLabel("tryStart");
builder.addLineNumber(2);
for (int i=0; i<250; i++) {
builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
}
builder.addLineNumber(3);
Label secondGotoTarget = builder.getLabel("secondGotoTarget");
builder.addInstruction(new BuilderInstruction10t(Opcode.GOTO, secondGotoTarget));
builder.addLineNumber(4);
builder.addLabel("handler");
for (int i=0; i<500; i++) {
builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
}
builder.addLineNumber(5);
builder.addLabel("tryEnd");
builder.addLabel("firstGotoTarget");
builder.addLabel("secondGotoTarget");
builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID));
Label tryStart = builder.getLabel("tryStart");
Label tryEnd = builder.getLabel("tryEnd");
Label handler = builder.getLabel("handler");
builder.addCatch(tryStart, tryEnd, handler);
MethodImplementation impl = builder.getMethodImplementation();
List<? extends Instruction> instructions = Lists.newArrayList(impl.getInstructions());
Assert.assertEquals(1003, instructions.size());
Assert.assertEquals(Opcode.GOTO_16, instructions.get(0).getOpcode());
Assert.assertEquals(1004, ((OffsetInstruction)instructions.get(0)).getCodeOffset());
Assert.assertEquals(Opcode.GOTO_16, instructions.get(501).getOpcode());
Assert.assertEquals(502, ((OffsetInstruction)instructions.get(501)).getCodeOffset());
List<? extends TryBlock<? extends ExceptionHandler>> exceptionHandlers = impl.getTryBlocks();
Assert.assertEquals(1, exceptionHandlers.size());
Assert.assertEquals(252, exceptionHandlers.get(0).getStartCodeAddress());
Assert.assertEquals(752, exceptionHandlers.get(0).getCodeUnitCount());
Assert.assertEquals(1, exceptionHandlers.get(0).getExceptionHandlers().size());
ExceptionHandler exceptionHandler = exceptionHandlers.get(0).getExceptionHandlers().get(0);
Assert.assertEquals(504, exceptionHandler.getHandlerCodeAddress());
List<DebugItem> debugItems = Lists.newArrayList(impl.getDebugItems());
Assert.assertEquals(5, debugItems.size());
Assert.assertEquals(1, ((LineNumber)debugItems.get(0)).getLineNumber());
Assert.assertEquals(2, debugItems.get(0).getCodeAddress());
Assert.assertEquals(2, ((LineNumber)debugItems.get(1)).getLineNumber());
Assert.assertEquals(252, debugItems.get(1).getCodeAddress());
Assert.assertEquals(3, ((LineNumber)debugItems.get(2)).getLineNumber());
Assert.assertEquals(502, debugItems.get(2).getCodeAddress());
Assert.assertEquals(4, ((LineNumber)debugItems.get(3)).getLineNumber());
Assert.assertEquals(504, debugItems.get(3).getCodeAddress());
Assert.assertEquals(5, ((LineNumber)debugItems.get(4)).getLineNumber());
Assert.assertEquals(1004, debugItems.get(4).getCodeAddress());
}
}

View File

@ -29,13 +29,10 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package org.jf.dexlib2.writer; package org.jf.dexlib2.builder;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import org.jf.dexlib2.Opcode; import org.jf.dexlib2.Opcode;
import org.jf.dexlib2.builder.Label;
import org.jf.dexlib2.builder.MethodImplementationBuilder;
import org.jf.dexlib2.builder.SwitchLabelElement;
import org.jf.dexlib2.builder.instruction.*; import org.jf.dexlib2.builder.instruction.*;
import org.jf.dexlib2.iface.instruction.Instruction; import org.jf.dexlib2.iface.instruction.Instruction;
import org.jf.dexlib2.iface.instruction.OffsetInstruction; import org.jf.dexlib2.iface.instruction.OffsetInstruction;

View File

@ -31,333 +31,185 @@
package org.jf.dexlib2.writer; package org.jf.dexlib2.writer;
// TODO: uncomment/reimplement import com.google.common.collect.ImmutableList;
/*public class JumboStringConversionTest { import com.google.common.collect.ImmutableSet;
private static final int MIN_NUM_JUMBO_STRINGS = 2; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import org.jf.dexlib2.Opcode;
import org.jf.dexlib2.Opcodes;
import org.jf.dexlib2.builder.MethodImplementationBuilder;
import org.jf.dexlib2.builder.instruction.BuilderInstruction10x;
import org.jf.dexlib2.builder.instruction.BuilderInstruction21c;
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
import org.jf.dexlib2.iface.*;
import org.jf.dexlib2.iface.debug.DebugItem;
import org.jf.dexlib2.iface.instruction.Instruction;
import org.jf.dexlib2.iface.instruction.ReferenceInstruction;
import org.jf.dexlib2.iface.instruction.formats.Instruction21c;
import org.jf.dexlib2.iface.reference.Reference;
import org.jf.dexlib2.iface.reference.StringReference;
import org.jf.dexlib2.immutable.instruction.ImmutableInstruction10x;
import org.jf.dexlib2.writer.builder.DexBuilder;
import org.jf.dexlib2.writer.io.MemoryDataStore;
import org.junit.Assert;
import org.junit.Test;
private MockStringIndexProvider mockStringIndexProvider; import javax.annotation.Nonnull;
ArrayList<String> mJumboStrings; import java.io.IOException;
import java.util.List;
private class InsnWriteUtil extends InstructionWriteUtil<StringReference, Reference> { public class JumboStringConversionTest {
public InsnWriteUtil(@Nonnull MethodImplementation implementation) { @Test
super(implementation.getInstructions(), mockStringIndexProvider, ImmutableInstructionFactory.INSTANCE); public void testJumboStringConversion() throws IOException {
DexBuilder dexBuilder = DexBuilder.makeDexBuilder(15);
MethodImplementationBuilder methodBuilder = new MethodImplementationBuilder(1);
for (int i=0; i<66000; i++) {
methodBuilder.addInstruction(new BuilderInstruction21c(Opcode.CONST_STRING, 0,
dexBuilder.internStringReference(String.format("%08d", i))));
} }
methodBuilder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID));
dexBuilder.internClassDef(
"Ltest;",
0,
"Ljava/lang/Object;",
null,
null,
ImmutableSet.<Annotation>of(),
null,
ImmutableList.of(
dexBuilder.internMethod(
"Ltest;",
"test",
null,
"V",
0,
ImmutableSet.<Annotation>of(),
methodBuilder.getMethodImplementation())));
MemoryDataStore dexStore = new MemoryDataStore();
dexBuilder.writeTo(dexStore);
DexBackedDexFile dexFile = new DexBackedDexFile(new Opcodes(15), dexStore.getData());
ClassDef classDef = Iterables.getFirst(dexFile.getClasses(), null);
Assert.assertNotNull(classDef);
Method method = Iterables.getFirst(classDef.getMethods(), null);
Assert.assertNotNull(method);
MethodImplementation impl = method.getImplementation();
Assert.assertNotNull(impl);
List<? extends Instruction> instructions = Lists.newArrayList(impl.getInstructions());
Assert.assertEquals(66001, instructions.size());
for (int i=0; i<65536; i++) {
Assert.assertEquals(Opcode.CONST_STRING, instructions.get(i).getOpcode());
Assert.assertEquals(String.format("%08d", i),
((StringReference)((ReferenceInstruction)instructions.get(i)).getReference()).getString());
}
for (int i=65536; i<66000; i++) {
Assert.assertEquals(Opcode.CONST_STRING_JUMBO, instructions.get(i).getOpcode());
Assert.assertEquals(String.format("%08d", i),
((StringReference)((ReferenceInstruction)instructions.get(i)).getReference()).getString());
}
Assert.assertEquals(Opcode.RETURN_VOID, instructions.get(66000).getOpcode());
} }
@Before
public void setup() {
mockStringIndexProvider = new MockStringIndexProvider();
StringBuilder stringBuilder = new StringBuilder("a");
mJumboStrings = Lists.newArrayList();
int index = 0;
// populate StringPool, make sure there are more than 64k+MIN_NUM_JUMBO_STRINGS strings
while (mJumboStrings.size()<MIN_NUM_JUMBO_STRINGS) {
for (int pos=stringBuilder.length()-1;pos>=0;pos--) {
for (char ch='a';ch<='z';ch++) {
stringBuilder.setCharAt(pos, ch);
mockStringIndexProvider.intern(stringBuilder.toString(), index++);
if (mockStringIndexProvider.getNumItems()>0xFFFF) {
mJumboStrings.add(stringBuilder.toString());
}
}
}
stringBuilder.setLength(stringBuilder.length()+1);
for (int pos=0;pos<stringBuilder.length();pos++) {
stringBuilder.setCharAt(pos, 'a');
}
}
}
@Test @Test
public void testInstruction21c() { public void testJumboStringConversion_NonMethodBuilder() throws IOException {
ArrayList<ImmutableInstruction> instructions = Lists.newArrayList(); DexBuilder dexBuilder = DexBuilder.makeDexBuilder(15);
instructions.add(new ImmutableInstruction21c(Opcode.CONST_STRING, 0,
new ImmutableStringReference(mJumboStrings.get(0))));
ImmutableMethodImplementation methodImplementation = final List<Instruction> instructions = Lists.newArrayList();
new ImmutableMethodImplementation(1, instructions, null, null); for (int i=0; i<66000; i++) {
InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation); final StringReference ref = dexBuilder.internStringReference(String.format("%08d", i));
for (Instruction instr: writeUtil.getInstructions()) { instructions.add(new Instruction21c() {
Assert.assertEquals("Jumbo string conversion was not performed!", @Override public int getRegisterA() {
instr.getOpcode(), Opcode.CONST_STRING_JUMBO); return 0;
}
} }
private ArrayList<ImmutableInstruction> createSimpleInstructionList() { @Nonnull @Override public Reference getReference() {
ArrayList<ImmutableInstruction> instructions = Lists.newArrayList(); return ref;
instructions.add(new ImmutableInstruction21c(Opcode.CONST_STRING, 0, new ImmutableStringReference(mJumboStrings.get(0)))); }
instructions.add(new ImmutableInstruction21c(Opcode.CONST_STRING, 0, new ImmutableStringReference(mJumboStrings.get(1))));
instructions.add(new ImmutableInstruction10x(Opcode.NOP));
ArrayList<SwitchElement> switchElements = Lists.newArrayList(); @Override public Opcode getOpcode() {
switchElements.add(new ImmutableSwitchElement(0, 5)); return Opcode.CONST_STRING;
instructions.add(new ImmutablePackedSwitchPayload(switchElements)); }
instructions.add(new ImmutableSparseSwitchPayload(switchElements));
@Override public int getCodeUnits() {
return getOpcode().format.size / 2;
}
});
}
instructions.add(new ImmutableInstruction10x(Opcode.RETURN_VOID));
MethodImplementation methodImpl = new MethodImplementation() {
@Override public int getRegisterCount() {
return 1;
}
@Nonnull @Override public Iterable<? extends Instruction> getInstructions() {
return instructions; return instructions;
} }
@Test @Nonnull @Override public List<? extends TryBlock<? extends ExceptionHandler>> getTryBlocks() {
public void testInstruction10tSimple() { return ImmutableList.of();
ArrayList<ImmutableInstruction> instructions = createSimpleInstructionList();
instructions.add(1, new ImmutableInstruction10t(Opcode.GOTO, 3));
ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null);
InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation);
for (Instruction instr: writeUtil.getInstructions()) {
if (instr instanceof Instruction10t) {
Instruction10t instruction = (Instruction10t) instr;
Assert.assertEquals("goto (Format10t) target was not modified properly", instruction.getCodeOffset(), 4);
break;
}
}
} }
@Test @Nonnull @Override public Iterable<? extends DebugItem> getDebugItems() {
public void testInstruction20tSimple() { return ImmutableList.of();
ArrayList<ImmutableInstruction> instructions = createSimpleInstructionList();
instructions.add(1, new ImmutableInstruction20t(Opcode.GOTO_16, 4));
ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null);
InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation);
for (Instruction instr: writeUtil.getInstructions()) {
if (instr instanceof Instruction20t) {
Instruction20t instruction = (Instruction20t) instr;
Assert.assertEquals("goto/16 (Format20t) target was not modified properly", instruction.getCodeOffset(), 5);
break;
}
}
} }
};
@Test dexBuilder.internClassDef(
public void testInstruction30t() { "Ltest;",
ArrayList<ImmutableInstruction> instructions = createSimpleInstructionList(); 0,
instructions.add(1, new ImmutableInstruction30t(Opcode.GOTO_32, 5)); "Ljava/lang/Object;",
null,
null,
ImmutableSet.<Annotation>of(),
null,
ImmutableList.of(
dexBuilder.internMethod(
"Ltest;",
"test",
null,
"V",
0,
ImmutableSet.<Annotation>of(),
methodImpl)));
ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); MemoryDataStore dexStore = new MemoryDataStore();
InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation); dexBuilder.writeTo(dexStore);
for (Instruction instr: writeUtil.getInstructions()) { DexBackedDexFile dexFile = new DexBackedDexFile(new Opcodes(15), dexStore.getData());
if (instr instanceof Instruction30t) {
Instruction30t instruction = (Instruction30t) instr; ClassDef classDef = Iterables.getFirst(dexFile.getClasses(), null);
Assert.assertEquals("goto/32 (Format30t) target was not modified properly", instruction.getCodeOffset(), 6); Assert.assertNotNull(classDef);
break;
Method method = Iterables.getFirst(classDef.getMethods(), null);
Assert.assertNotNull(method);
MethodImplementation impl = method.getImplementation();
Assert.assertNotNull(impl);
List<? extends Instruction> actualInstructions = Lists.newArrayList(impl.getInstructions());
Assert.assertEquals(66001, actualInstructions.size());
for (int i=0; i<65536; i++) {
Assert.assertEquals(Opcode.CONST_STRING, actualInstructions.get(i).getOpcode());
Assert.assertEquals(String.format("%08d", i),
((StringReference)((ReferenceInstruction)actualInstructions.get(i)).getReference()).getString());
} }
for (int i=65536; i<66000; i++) {
Assert.assertEquals(Opcode.CONST_STRING_JUMBO, actualInstructions.get(i).getOpcode());
Assert.assertEquals(String.format("%08d", i),
((StringReference)((ReferenceInstruction)actualInstructions.get(i)).getReference()).getString());
} }
Assert.assertEquals(Opcode.RETURN_VOID, actualInstructions.get(66000).getOpcode());
} }
}
@Test
public void testInstruction21t() {
ArrayList<ImmutableInstruction> instructions = createSimpleInstructionList();
instructions.add(1, new ImmutableInstruction21t(Opcode.IF_EQZ, 0, 4));
ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null);
InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation);
for (Instruction instr: writeUtil.getInstructions()) {
if (instr instanceof Instruction21t) {
Instruction21t instruction = (Instruction21t) instr;
Assert.assertEquals("branch instruction (Format21t) target was not modified properly", instruction.getCodeOffset(), 5);
break;
}
}
}
@Test
public void testInstruction22t() {
ArrayList<ImmutableInstruction> instructions = createSimpleInstructionList();
instructions.add(1, new ImmutableInstruction22t(Opcode.IF_EQ, 0, 1, 4));
ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null);
InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation);
for (Instruction instr: writeUtil.getInstructions()) {
if (instr instanceof Instruction22t) {
Instruction22t instruction = (Instruction22t) instr;
Assert.assertEquals("branch instruction (Format22t) target was not modified properly", instruction.getCodeOffset(), 5);
break;
}
}
}
@Test
public void testInstruction31t() {
ArrayList<ImmutableInstruction> instructions = createSimpleInstructionList();
instructions.add(1, new ImmutableInstruction31t(Opcode.PACKED_SWITCH, 0, 5));
ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null);
InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation);
for (Instruction instr: writeUtil.getInstructions()) {
if (instr instanceof Instruction31t) {
Instruction31t instruction = (Instruction31t) instr;
Assert.assertEquals("branch instruction (Format31t) target was not modified properly", instruction.getCodeOffset(), 6);
break;
}
}
}
@Test
public void testPackedSwitchPayload() {
ArrayList<ImmutableInstruction> instructions = createSimpleInstructionList();
instructions.add(1, new ImmutableInstruction31t(Opcode.PACKED_SWITCH, 0, 6));
ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null);
InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation);
for (Instruction instr: writeUtil.getInstructions()) {
if (instr instanceof PackedSwitchPayload) {
PackedSwitchPayload instruction = (PackedSwitchPayload) instr;
for (SwitchElement switchElement: instruction.getSwitchElements()) {
Assert.assertEquals("packed switch payload offset was not modified properly", switchElement.getOffset(), 6);
}
break;
}
}
}
@Test
public void testSparseSwitchPayload() {
ArrayList<ImmutableInstruction> instructions = createSimpleInstructionList();
instructions.add(1, new ImmutableInstruction31t(Opcode.SPARSE_SWITCH, 0, 12));
ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null);
InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation);
for (Instruction instr: writeUtil.getInstructions()) {
if (instr instanceof SparseSwitchPayload) {
SparseSwitchPayload instruction = (SparseSwitchPayload) instr;
for (SwitchElement switchElement: instruction.getSwitchElements()) {
Assert.assertEquals("packed switch payload offset was not modified properly", switchElement.getOffset(), 6);
}
break;
}
}
}
@Test
public void testArrayPayloadAlignment() {
ArrayList<ImmutableInstruction> instructions = createSimpleInstructionList();
// add misaligned array payload
instructions.add(new ImmutableInstruction10x(Opcode.NOP));
instructions.add(new ImmutableArrayPayload(4, null));
ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null);
InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation);
int codeOffset = 0;
for (Instruction instr: writeUtil.getInstructions()) {
if (codeOffset == 21) {
Assert.assertEquals("array payload was not aligned properly", instr.getOpcode(), Opcode.NOP);
break;
}
codeOffset += instr.getCodeUnits();
}
}
@Test
public void testPackedSwitchAlignment() {
ArrayList<ImmutableInstruction> instructions = createSimpleInstructionList();
// packed switch instruction is already misaligned
ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null);
InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation);
int codeOffset = 0;
for (Instruction instr: writeUtil.getInstructions()) {
if (codeOffset == 7) {
Assert.assertEquals("packed switch payload was not aligned properly", instr.getOpcode(), Opcode.NOP);
break;
}
codeOffset += instr.getCodeUnits();
}
}
@Test
public void testSparseSwitchAlignment() {
ArrayList<ImmutableInstruction> instructions = createSimpleInstructionList();
// insert a nop to mis-align sparse switch payload
instructions.add(4, new ImmutableInstruction10x(Opcode.NOP));
ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null);
InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation);
int codeOffset = 0;
for (Instruction instr: writeUtil.getInstructions()) {
if (codeOffset == 15) {
Assert.assertEquals("packed switch payload was not aligned properly", instr.getOpcode(), Opcode.NOP);
break;
}
codeOffset += instr.getCodeUnits();
}
}
@Test
public void testGotoToGoto16() {
ArrayList<ImmutableInstruction> instructions = Lists.newArrayList();
instructions.add(new ImmutableInstruction10t(Opcode.GOTO, 127));
instructions.add(new ImmutableInstruction21c(Opcode.CONST_STRING, 0, new ImmutableStringReference(mJumboStrings.get(0))));
for (int i=0;i<127;i++) {
instructions.add(new ImmutableInstruction10x(Opcode.NOP));
}
ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null);
InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation);
Instruction instr = writeUtil.getInstructions().iterator().next();
Assert.assertEquals("goto was not converted to goto/16 properly", instr.getOpcode(), Opcode.GOTO_16);
}
@Test
public void testGoto16ToGoto32() {
ArrayList<ImmutableInstruction> instructions = Lists.newArrayList();
instructions.add(new ImmutableInstruction20t(Opcode.GOTO_16, Short.MAX_VALUE));
instructions.add(new ImmutableInstruction21c(Opcode.CONST_STRING, 0, new ImmutableStringReference(mJumboStrings.get(0))));
for (int i=0;i<Short.MAX_VALUE;i++) {
instructions.add(new ImmutableInstruction10x(Opcode.NOP));
}
ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null);
InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation);
Instruction instr = writeUtil.getInstructions().iterator().next();
Assert.assertEquals("goto/16 was not converted to goto/32 properly", instr.getOpcode(), Opcode.GOTO_32);
}
@Test
public void testGotoIterative() {
ArrayList<ImmutableInstruction> instructions = Lists.newArrayList();
instructions.add(new ImmutableInstruction10t(Opcode.GOTO, 126));
instructions.add(new ImmutableInstruction10t(Opcode.GOTO, 127));
instructions.add(new ImmutableInstruction21c(Opcode.CONST_STRING, 0, new ImmutableStringReference(mJumboStrings.get(0))));
for (int i=0;i<122;i++) {
instructions.add(new ImmutableInstruction10x(Opcode.NOP));
}
instructions.add(new ImmutableInstruction21c(Opcode.CONST_STRING, 0, new ImmutableStringReference(mJumboStrings.get(1))));
instructions.add(new ImmutableInstruction10x(Opcode.NOP));
// this misaligned array payload will cause nop insertion on the first pass and its removal on the second pass
instructions.add(new ImmutableInstruction10x(Opcode.NOP));
instructions.add(new ImmutableArrayPayload(4, null));
ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null);
InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation);
Instruction instr = writeUtil.getInstructions().iterator().next();
Assert.assertEquals("goto was not converted to goto/16 properly", instr.getOpcode(), Opcode.GOTO_16);
int codeOffset = 0;
for (Instruction instruction: writeUtil.getInstructions()) {
if (instruction instanceof ArrayPayload) {
Assert.assertEquals("packed switch payload was not aligned properly", codeOffset%2, 0);
}
codeOffset += instruction.getCodeUnits();
}
}
}*/