mirror of
https://github.com/revanced/smali.git
synced 2025-05-09 10:54:29 +02:00
Add ability to converting MethodImplementation to MutableMethodImplementation
This commit is contained in:
parent
9a2de93a99
commit
6762350ca0
@ -59,6 +59,7 @@ public abstract class BuilderInstruction implements Instruction {
|
||||
return getFormat().size / 2;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public MethodLocation getLocation() {
|
||||
if (location == null) {
|
||||
throw new IllegalStateException("Cannot get the location of an instruction that hasn't been added to a " +
|
||||
|
@ -4,16 +4,19 @@ import com.google.common.base.Function;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.jf.dexlib2.DebugItemType;
|
||||
import org.jf.dexlib2.Opcode;
|
||||
import org.jf.dexlib2.builder.instruction.BuilderInstruction10x;
|
||||
import org.jf.dexlib2.builder.instruction.BuilderInstruction20t;
|
||||
import org.jf.dexlib2.builder.instruction.BuilderInstruction30t;
|
||||
import org.jf.dexlib2.builder.debug.*;
|
||||
import org.jf.dexlib2.builder.instruction.*;
|
||||
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.*;
|
||||
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.util.ExceptionWithContext;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
@ -26,7 +29,71 @@ public class MutableMethodImplementation implements MethodImplementation {
|
||||
private boolean fixInstructions = true;
|
||||
|
||||
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) {
|
||||
@ -361,4 +428,485 @@ public class MutableMethodImplementation implements MethodImplementation {
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -792,7 +792,7 @@ public abstract class DexWriter<
|
||||
|
||||
if (instruction.getOpcode() == Opcode.CONST_STRING) {
|
||||
if (stringSection.getItemIndex(
|
||||
(StringRef)((ReferenceInstruction)instruction).getReference()) > 65536) {
|
||||
(StringRef)((ReferenceInstruction)instruction).getReference()) >= 65536) {
|
||||
methodImplementation.replaceInstruction(i, new BuilderInstruction31c(Opcode.CONST_STRING_JUMBO,
|
||||
((OneRegisterInstruction)instruction).getRegisterA(),
|
||||
((ReferenceInstruction)instruction).getReference()));
|
||||
|
@ -73,11 +73,21 @@ public class BuilderClassDef extends BaseTypeReference implements ClassDef {
|
||||
this.interfaces = interfaces;
|
||||
this.sourceFile = sourceFile;
|
||||
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.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.virtualMethods = ImmutableSortedSet.copyOf(Iterables.filter(methods, MethodUtil.METHOD_IS_VIRTUAL));
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull @Override public String getType() { return type.getType(); }
|
||||
@Override public int getAccessFlags() { return accessFlags; }
|
||||
|
@ -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
|
||||
}
|
||||
}
|
122
dexlib2/src/test/java/org/jf/dexlib2/builder/FixGotoTest.java
Normal file
122
dexlib2/src/test/java/org/jf/dexlib2/builder/FixGotoTest.java
Normal 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());
|
||||
|
||||
}
|
||||
}
|
140
dexlib2/src/test/java/org/jf/dexlib2/builder/FixOffsetsTest.java
Normal file
140
dexlib2/src/test/java/org/jf/dexlib2/builder/FixOffsetsTest.java
Normal 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());
|
||||
}
|
||||
}
|
@ -29,13 +29,10 @@
|
||||
* 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 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.iface.instruction.Instruction;
|
||||
import org.jf.dexlib2.iface.instruction.OffsetInstruction;
|
@ -31,333 +31,185 @@
|
||||
|
||||
package org.jf.dexlib2.writer;
|
||||
|
||||
// TODO: uncomment/reimplement
|
||||
/*public class JumboStringConversionTest {
|
||||
private static final int MIN_NUM_JUMBO_STRINGS = 2;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
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;
|
||||
ArrayList<String> mJumboStrings;
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
private class InsnWriteUtil extends InstructionWriteUtil<StringReference, Reference> {
|
||||
public InsnWriteUtil(@Nonnull MethodImplementation implementation) {
|
||||
super(implementation.getInstructions(), mockStringIndexProvider, ImmutableInstructionFactory.INSTANCE);
|
||||
public class JumboStringConversionTest {
|
||||
@Test
|
||||
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
|
||||
public void testInstruction21c() {
|
||||
ArrayList<ImmutableInstruction> instructions = Lists.newArrayList();
|
||||
instructions.add(new ImmutableInstruction21c(Opcode.CONST_STRING, 0,
|
||||
new ImmutableStringReference(mJumboStrings.get(0))));
|
||||
public void testJumboStringConversion_NonMethodBuilder() throws IOException {
|
||||
DexBuilder dexBuilder = DexBuilder.makeDexBuilder(15);
|
||||
|
||||
ImmutableMethodImplementation methodImplementation =
|
||||
new ImmutableMethodImplementation(1, instructions, null, null);
|
||||
InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation);
|
||||
final List<Instruction> instructions = Lists.newArrayList();
|
||||
for (int i=0; i<66000; i++) {
|
||||
final StringReference ref = dexBuilder.internStringReference(String.format("%08d", i));
|
||||
|
||||
for (Instruction instr: writeUtil.getInstructions()) {
|
||||
Assert.assertEquals("Jumbo string conversion was not performed!",
|
||||
instr.getOpcode(), Opcode.CONST_STRING_JUMBO);
|
||||
}
|
||||
instructions.add(new Instruction21c() {
|
||||
@Override public int getRegisterA() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private ArrayList<ImmutableInstruction> createSimpleInstructionList() {
|
||||
ArrayList<ImmutableInstruction> instructions = Lists.newArrayList();
|
||||
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));
|
||||
@Nonnull @Override public Reference getReference() {
|
||||
return ref;
|
||||
}
|
||||
|
||||
ArrayList<SwitchElement> switchElements = Lists.newArrayList();
|
||||
switchElements.add(new ImmutableSwitchElement(0, 5));
|
||||
instructions.add(new ImmutablePackedSwitchPayload(switchElements));
|
||||
instructions.add(new ImmutableSparseSwitchPayload(switchElements));
|
||||
@Override public Opcode getOpcode() {
|
||||
return Opcode.CONST_STRING;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstruction10tSimple() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@Nonnull @Override public List<? extends TryBlock<? extends ExceptionHandler>> getTryBlocks() {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstruction20tSimple() {
|
||||
ArrayList<ImmutableInstruction> instructions = createSimpleInstructionList();
|
||||
instructions.add(1, new ImmutableInstruction20t(Opcode.GOTO_16, 4));
|
||||
@Nonnull @Override public Iterable<? extends DebugItem> getDebugItems() {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
};
|
||||
|
||||
ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null);
|
||||
InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation);
|
||||
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(),
|
||||
methodImpl)));
|
||||
|
||||
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;
|
||||
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> 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 testInstruction30t() {
|
||||
ArrayList<ImmutableInstruction> instructions = createSimpleInstructionList();
|
||||
instructions.add(1, new ImmutableInstruction30t(Opcode.GOTO_32, 5));
|
||||
|
||||
ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null);
|
||||
InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation);
|
||||
|
||||
for (Instruction instr: writeUtil.getInstructions()) {
|
||||
if (instr instanceof Instruction30t) {
|
||||
Instruction30t instruction = (Instruction30t) instr;
|
||||
Assert.assertEquals("goto/32 (Format30t) target was not modified properly", instruction.getCodeOffset(), 6);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user