[smali] update to smali 2b6

This commit is contained in:
Connor Tumbleson
2013-09-15 10:23:12 -05:00
parent 9c957b9308
commit 79c68ed6d9
81 changed files with 1394 additions and 2335 deletions

View File

@ -38,5 +38,20 @@ public final class ReferenceType {
public static final int METHOD = 3;
public static final int NONE = 4;
public static String toString(int referenceType) {
switch (referenceType) {
case STRING:
return "string";
case TYPE:
return "type";
case FIELD:
return "field";
case METHOD:
return "method";
default:
throw new IllegalArgumentException("Invalid reference type: " + referenceType);
}
}
private ReferenceType() {}
}

View File

@ -33,13 +33,28 @@ package org.jf.dexlib2.base;
import com.google.common.base.Objects;
import com.google.common.primitives.Ints;
import org.jf.dexlib2.base.reference.BaseTypeReference;
import org.jf.dexlib2.iface.ExceptionHandler;
import org.jf.dexlib2.iface.reference.TypeReference;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Comparator;
public abstract class BaseExceptionHandler implements ExceptionHandler {
@Nullable @Override public TypeReference getExceptionTypeReference() {
final String exceptionType = getExceptionType();
if (exceptionType == null) {
return null;
}
return new BaseTypeReference() {
@Nonnull @Override public String getType() {
return exceptionType;
}
};
}
@Override
public int hashCode() {
String exceptionType = getExceptionType();
@ -76,6 +91,8 @@ public abstract class BaseExceptionHandler implements ExceptionHandler {
return Ints.compare(getHandlerCodeAddress(), o.getHandlerCodeAddress());
}
public static final Comparator<ExceptionHandler> BY_EXCEPTION = new Comparator<ExceptionHandler>() {
@Override public int compare(ExceptionHandler o1, ExceptionHandler o2) {
String exceptionType1 = o1.getExceptionType();

View File

@ -90,6 +90,10 @@ public class BaseDexReader<T extends BaseDexBuffer> {
}
public int readSmallUleb128() {
return readUleb128(false);
}
private int readUleb128(boolean allowLarge) {
int end = offset;
int currentByteValue;
int result;
@ -113,10 +117,12 @@ public class BaseDexReader<T extends BaseDexBuffer> {
throw new ExceptionWithContext(
"Invalid uleb128 integer encountered at offset 0x%x", offset);
} else if ((currentByteValue & 0xf) > 0x07) {
// we assume most significant bit of the result will not be set, so that it can fit into
// a signed integer without wrapping
throw new ExceptionWithContext(
"Encountered valid uleb128 that is out of range at offset 0x%x", offset);
if (!allowLarge) {
// for non-large uleb128s, we assume most significant bit of the result will not be
// set, so that it can fit into a signed integer without wrapping
throw new ExceptionWithContext(
"Encountered valid uleb128 that is out of range at offset 0x%x", offset);
}
}
result |= currentByteValue << 28;
}
@ -128,6 +134,16 @@ public class BaseDexReader<T extends BaseDexBuffer> {
return result;
}
/**
* Reads a "large" uleb128. That is, one that may legitimately be greater than a signed int.
*
* The value is returned as if it were signed. i.e. a value of 0xFFFFFFFF would be returned as -1. It is up to the
* caller to handle the value appropriately.
*/
public int readLargeUleb128() {
return readUleb128(true);
}
/**
* Reads a "big" uleb128 that can legitimately be > 2^31. The value is returned as a signed integer, with the
* expected semantics of re-interpreting an unsigned value as a signed value.
@ -185,11 +201,6 @@ public class BaseDexReader<T extends BaseDexBuffer> {
if (currentByteValue < 0) {
throw new ExceptionWithContext(
"Invalid uleb128 integer encountered at offset 0x%x", offset);
} else if ((currentByteValue & 0xf) > 0x07) {
// we assume most significant bit of the result will not be set, so that it can fit into
// a signed integer without wrapping
throw new ExceptionWithContext(
"Encountered valid uleb128 that is out of range at offset 0x%x", offset);
}
}
}

View File

@ -163,42 +163,42 @@ public class DexBackedDexFile extends BaseDexBuffer implements DexFile {
public int getStringIdItemOffset(int stringIndex) {
if (stringIndex < 0 || stringIndex >= stringCount) {
throw new ExceptionWithContext("String index out of bounds: %d", stringIndex);
throw new InvalidItemIndex(stringIndex, "String index out of bounds: %d", stringIndex);
}
return stringStartOffset + stringIndex*StringIdItem.ITEM_SIZE;
}
public int getTypeIdItemOffset(int typeIndex) {
if (typeIndex < 0 || typeIndex >= typeCount) {
throw new ExceptionWithContext("Type index out of bounds: %d", typeIndex);
throw new InvalidItemIndex(typeIndex, "Type index out of bounds: %d", typeIndex);
}
return typeStartOffset + typeIndex*TypeIdItem.ITEM_SIZE;
}
public int getFieldIdItemOffset(int fieldIndex) {
if (fieldIndex < 0 || fieldIndex >= fieldCount) {
throw new ExceptionWithContext("Field index out of bounds: %d", fieldIndex);
throw new InvalidItemIndex(fieldIndex, "Field index out of bounds: %d", fieldIndex);
}
return fieldStartOffset + fieldIndex*FieldIdItem.ITEM_SIZE;
}
public int getMethodIdItemOffset(int methodIndex) {
if (methodIndex < 0 || methodIndex >= methodCount) {
throw new ExceptionWithContext("Method index out of bounds: %d", methodIndex);
throw new InvalidItemIndex(methodIndex, "Method findex out of bounds: %d", methodIndex);
}
return methodStartOffset + methodIndex*MethodIdItem.ITEM_SIZE;
}
public int getProtoIdItemOffset(int protoIndex) {
if (protoIndex < 0 || protoIndex >= protoCount) {
throw new ExceptionWithContext("Proto index out of bounds: %d", protoIndex);
throw new InvalidItemIndex(protoIndex, "Proto index out of bounds: %d", protoIndex);
}
return protoStartOffset + protoIndex*ProtoIdItem.ITEM_SIZE;
}
public int getClassDefItemOffset(int classIndex) {
if (classIndex < 0 || classIndex >= classCount) {
throw new ExceptionWithContext("Class index out of bounds: %d", classIndex);
throw new InvalidItemIndex(classIndex, "Class index out of bounds: %d", classIndex);
}
return classStartOffset + classIndex*ClassDefItem.ITEM_SIZE;
}
@ -261,4 +261,22 @@ public class DexBackedDexFile extends BaseDexBuffer implements DexFile {
super(message, cause);
}
}
public static class InvalidItemIndex extends ExceptionWithContext {
private final int itemIndex;
public InvalidItemIndex(int itemIndex) {
super("");
this.itemIndex = itemIndex;
}
public InvalidItemIndex(int itemIndex, String message, Object... formatArgs) {
super(message, formatArgs);
this.itemIndex = itemIndex;
}
public int getInvalidIndex() {
return itemIndex;
}
}
}

View File

@ -63,7 +63,9 @@ public class DexBackedField extends BaseFieldReference implements Field {
this.dexFile = reader.dexBuf;
this.classDef = classDef;
int fieldIndexDiff = reader.readSmallUleb128();
// large values may be used for the index delta, which cause the cumulative index to overflow upon
// addition, effectively allowing out of order entries.
int fieldIndexDiff = reader.readLargeUleb128();
this.fieldIndex = fieldIndexDiff + previousFieldIndex;
this.accessFlags = reader.readSmallUleb128();
@ -78,7 +80,9 @@ public class DexBackedField extends BaseFieldReference implements Field {
this.dexFile = reader.dexBuf;
this.classDef = classDef;
int fieldIndexDiff = reader.readSmallUleb128();
// large values may be used for the index delta, which cause the cumulative index to overflow upon
// addition, effectively allowing out of order entries.
int fieldIndexDiff = reader.readLargeUleb128();
this.fieldIndex = fieldIndexDiff + previousFieldIndex;
this.accessFlags = reader.readSmallUleb128();

View File

@ -73,7 +73,9 @@ public class DexBackedMethod extends BaseMethodReference implements Method {
this.dexFile = reader.dexBuf;
this.classDef = classDef;
int methodIndexDiff = reader.readSmallUleb128();
// large values may be used for the index delta, which cause the cumulative index to overflow upon
// addition, effectively allowing out of order entries.
int methodIndexDiff = reader.readLargeUleb128();
this.methodIndex = methodIndexDiff + previousMethodIndex;
this.accessFlags = reader.readSmallUleb128();
this.codeOffset = reader.readSmallUleb128();
@ -90,7 +92,9 @@ public class DexBackedMethod extends BaseMethodReference implements Method {
this.dexFile = reader.dexBuf;
this.classDef = classDef;
int methodIndexDiff = reader.readSmallUleb128();
// large values may be used for the index delta, which cause the cumulative index to overflow upon
// addition, effectively allowing out of order entries.
int methodIndexDiff = reader.readLargeUleb128();
this.methodIndex = methodIndexDiff + previousMethodIndex;
this.accessFlags = reader.readSmallUleb128();
this.codeOffset = reader.readSmallUleb128();

View File

@ -127,7 +127,9 @@ public class ClassDataItem {
private int annotateEncodedField(@Nonnull AnnotatedBytes out, @Nonnull RawDexFile dexFile,
@Nonnull DexReader reader, int previousIndex) {
int indexDelta = reader.readSmallUleb128();
// large values may be used for the index delta, which cause the cumulative index to overflow upon
// addition, effectively allowing out of order entries.
int indexDelta = reader.readLargeUleb128();
int fieldIndex = previousIndex + indexDelta;
out.annotateTo(reader.getOffset(), "field_idx_diff = %d: %s", indexDelta,
FieldIdItem.getReferenceAnnotation(dexFile, fieldIndex));
@ -141,7 +143,9 @@ public class ClassDataItem {
private int annotateEncodedMethod(@Nonnull AnnotatedBytes out, @Nonnull RawDexFile dexFile,
@Nonnull DexReader reader, int previousIndex) {
int indexDelta = reader.readSmallUleb128();
// large values may be used for the index delta, which cause the cumulative index to overflow upon
// addition, effectively allowing out of order entries.
int indexDelta = reader.readLargeUleb128();
int methodIndex = previousIndex + indexDelta;
out.annotateTo(reader.getOffset(), "method_idx_diff = %d: %s", indexDelta,
MethodIdItem.getReferenceAnnotation(dexFile, methodIndex));

View File

@ -53,6 +53,8 @@ public class DexAnnotator extends AnnotatedBytes {
static {
int[] sectionOrder = new int[] {
ItemType.MAP_LIST,
ItemType.HEADER_ITEM,
ItemType.STRING_ID_ITEM,
ItemType.TYPE_ID_ITEM,
@ -66,7 +68,6 @@ public class DexAnnotator extends AnnotatedBytes {
ItemType.CODE_ITEM,
ItemType.DEBUG_INFO_ITEM,
ItemType.MAP_LIST,
ItemType.TYPE_LIST,
ItemType.ANNOTATION_SET_REF_LIST,
ItemType.ANNOTATION_SET_ITEM,

View File

@ -31,6 +31,8 @@
package org.jf.dexlib2.iface;
import org.jf.dexlib2.iface.reference.TypeReference;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -45,6 +47,14 @@ public interface ExceptionHandler extends Comparable<ExceptionHandler> {
*/
@Nullable String getExceptionType();
/**
* Gets the type of exception that is handled by this handler.
*
* @return A TypeReference to the type of exception that is handled by this handler, or null if this is a
* catch-all handler.
*/
@Nullable TypeReference getExceptionTypeReference();
/**
* Gets the code offset of the handler.
*

View File

@ -31,8 +31,11 @@
package org.jf.dexlib2.iface.debug;
import org.jf.dexlib2.iface.reference.StringReference;
import javax.annotation.Nullable;
public interface SetSourceFile extends DebugItem {
@Nullable String getSourceFile();
@Nullable StringReference getSourceFileReference();
}

View File

@ -31,6 +31,15 @@
package org.jf.dexlib2.iface.debug;
import org.jf.dexlib2.iface.reference.StringReference;
import org.jf.dexlib2.iface.reference.TypeReference;
import javax.annotation.Nullable;
public interface StartLocal extends DebugItem, LocalInfo {
int getRegister();
@Nullable StringReference getNameReference();
@Nullable TypeReference getTypeReference();
@Nullable StringReference getSignatureReference();
}

View File

@ -32,7 +32,9 @@
package org.jf.dexlib2.immutable.debug;
import org.jf.dexlib2.DebugItemType;
import org.jf.dexlib2.base.reference.BaseStringReference;
import org.jf.dexlib2.iface.debug.SetSourceFile;
import org.jf.dexlib2.iface.reference.StringReference;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -58,5 +60,14 @@ public class ImmutableSetSourceFile extends ImmutableDebugItem implements SetSou
@Nullable @Override public String getSourceFile() { return sourceFile; }
@Nullable @Override public StringReference getSourceFileReference() {
return sourceFile==null?null:new BaseStringReference() {
@Nonnull @Override public String getString() {
return sourceFile;
}
};
}
@Override public int getDebugItemType() { return DebugItemType.SET_SOURCE_FILE; }
}

View File

@ -32,7 +32,11 @@
package org.jf.dexlib2.immutable.debug;
import org.jf.dexlib2.DebugItemType;
import org.jf.dexlib2.base.reference.BaseStringReference;
import org.jf.dexlib2.base.reference.BaseTypeReference;
import org.jf.dexlib2.iface.debug.StartLocal;
import org.jf.dexlib2.iface.reference.StringReference;
import org.jf.dexlib2.iface.reference.TypeReference;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -69,6 +73,31 @@ public class ImmutableStartLocal extends ImmutableDebugItem implements StartLoca
}
@Override public int getRegister() { return register; }
@Nullable @Override public StringReference getNameReference() {
return name==null?null:new BaseStringReference() {
@Nonnull @Override public String getString() {
return name;
}
};
}
@Nullable @Override public TypeReference getTypeReference() {
return type==null?null:new BaseTypeReference() {
@Nonnull @Override public String getType() {
return type;
}
};
}
@Nullable @Override public StringReference getSignatureReference() {
return signature==null?null:new BaseStringReference() {
@Nonnull @Override public String getString() {
return signature;
}
};
}
@Nullable @Override public String getName() { return name; }
@Nullable @Override public String getType() { return type; }
@Nullable @Override public String getSignature() { return signature; }

View File

@ -45,8 +45,8 @@ public abstract class ImmutableInstruction implements Instruction {
@Nonnull protected final Opcode opcode;
protected ImmutableInstruction(@Nonnull Opcode opcode) {
this.opcode = opcode;
Preconditions.checkFormat(opcode, getFormat());
this.opcode = opcode;
}
@Nonnull

View File

@ -46,7 +46,6 @@ public class ImmutableInstruction10t extends ImmutableInstruction implements Ins
public ImmutableInstruction10t(@Nonnull Opcode opcode,
int codeOffset) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.codeOffset = Preconditions.checkByteCodeOffset(codeOffset);
}

View File

@ -34,7 +34,6 @@ package org.jf.dexlib2.immutable.instruction;
import org.jf.dexlib2.Format;
import org.jf.dexlib2.Opcode;
import org.jf.dexlib2.iface.instruction.formats.Instruction10x;
import org.jf.dexlib2.util.Preconditions;
import javax.annotation.Nonnull;
@ -43,7 +42,6 @@ public class ImmutableInstruction10x extends ImmutableInstruction implements Ins
public ImmutableInstruction10x(@Nonnull Opcode opcode) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
}
public static ImmutableInstruction10x of(Instruction10x instruction) {

View File

@ -48,7 +48,6 @@ public class ImmutableInstruction11n extends ImmutableInstruction implements Ins
int registerA,
int literal) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerA = Preconditions.checkNibbleRegister(registerA);
this.literal = Preconditions.checkNibbleLiteral(literal);
}

View File

@ -46,7 +46,6 @@ public class ImmutableInstruction11x extends ImmutableInstruction implements Ins
public ImmutableInstruction11x(@Nonnull Opcode opcode,
int registerA) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerA = Preconditions.checkByteRegister(registerA);
}

View File

@ -48,7 +48,6 @@ public class ImmutableInstruction12x extends ImmutableInstruction implements Ins
int registerA,
int registerB) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerA = Preconditions.checkNibbleRegister(registerA);
this.registerB = Preconditions.checkNibbleRegister(registerB);
}

View File

@ -51,7 +51,6 @@ public class ImmutableInstruction20bc extends ImmutableInstruction implements In
int verificationError,
@Nonnull Reference reference) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.verificationError = Preconditions.checkVerificationError(verificationError);
this.reference = ImmutableReferenceFactory.of(opcode.referenceType, reference);
}

View File

@ -46,7 +46,6 @@ public class ImmutableInstruction20t extends ImmutableInstruction implements Ins
public ImmutableInstruction20t(@Nonnull Opcode opcode,
int codeOffset) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.codeOffset = Preconditions.checkShortCodeOffset(codeOffset);
}

View File

@ -51,7 +51,6 @@ public class ImmutableInstruction21c extends ImmutableInstruction implements Ins
int registerA,
@Nonnull Reference reference) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerA = Preconditions.checkByteRegister(registerA);
this.reference = ImmutableReferenceFactory.of(opcode.referenceType, reference);
}

View File

@ -48,7 +48,6 @@ public class ImmutableInstruction21ih extends ImmutableInstruction implements In
int registerA,
int literal) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerA = Preconditions.checkByteRegister(registerA);
this.literal = Preconditions.checkIntegerHatLiteral(literal);
}

View File

@ -48,7 +48,6 @@ public class ImmutableInstruction21lh extends ImmutableInstruction implements In
int registerA,
long literal) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerA = Preconditions.checkByteRegister(registerA);
this.literal = Preconditions.checkLongHatLiteral(literal);
}

View File

@ -48,7 +48,6 @@ public class ImmutableInstruction21s extends ImmutableInstruction implements Ins
int registerA,
int literal) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerA = Preconditions.checkByteRegister(registerA);
this.literal = Preconditions.checkShortLiteral(literal);
}

View File

@ -48,7 +48,6 @@ public class ImmutableInstruction21t extends ImmutableInstruction implements Ins
int registerA,
int codeOffset) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerA = Preconditions.checkByteRegister(registerA);
this.codeOffset = Preconditions.checkShortCodeOffset(codeOffset);
}

View File

@ -50,7 +50,6 @@ public class ImmutableInstruction22b extends ImmutableInstruction implements Ins
int registerB,
int literal) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerA = Preconditions.checkByteRegister(registerA);
this.registerB = Preconditions.checkByteRegister(registerB);
this.literal = Preconditions.checkByteLiteral(literal);

View File

@ -53,7 +53,6 @@ public class ImmutableInstruction22c extends ImmutableInstruction implements Ins
int registerB,
@Nonnull Reference reference) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerA = Preconditions.checkNibbleRegister(registerA);
this.registerB = Preconditions.checkNibbleRegister(registerB);
this.reference = ImmutableReferenceFactory.of(opcode.referenceType, reference);

View File

@ -43,14 +43,13 @@ public class ImmutableInstruction22cs extends ImmutableInstruction implements In
protected final int registerA;
protected final int registerB;
@Nonnull protected final int fieldOffset;
protected final int fieldOffset;
public ImmutableInstruction22cs(@Nonnull Opcode opcode,
int registerA,
int registerB,
int fieldOffset) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerA = Preconditions.checkNibbleRegister(registerA);
this.registerB = Preconditions.checkNibbleRegister(registerB);
this.fieldOffset = Preconditions.checkFieldOffset(fieldOffset);

View File

@ -50,7 +50,6 @@ public class ImmutableInstruction22s extends ImmutableInstruction implements Ins
int registerB,
int literal) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerA = Preconditions.checkNibbleRegister(registerA);
this.registerB = Preconditions.checkNibbleRegister(registerB);
this.literal = Preconditions.checkShortLiteral(literal);

View File

@ -50,7 +50,6 @@ public class ImmutableInstruction22t extends ImmutableInstruction implements Ins
int registerB,
int codeOffset) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerA = Preconditions.checkNibbleRegister(registerA);
this.registerB = Preconditions.checkNibbleRegister(registerB);
this.codeOffset = Preconditions.checkShortCodeOffset(codeOffset);

View File

@ -48,7 +48,6 @@ public class ImmutableInstruction22x extends ImmutableInstruction implements Ins
int registerA,
int registerB) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerA = Preconditions.checkByteRegister(registerA);
this.registerB = Preconditions.checkShortRegister(registerB);
}

View File

@ -50,7 +50,6 @@ public class ImmutableInstruction23x extends ImmutableInstruction implements Ins
int registerB,
int registerC) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerA = Preconditions.checkByteRegister(registerA);
this.registerB = Preconditions.checkByteRegister(registerB);
this.registerC = Preconditions.checkByteRegister(registerC);

View File

@ -34,7 +34,6 @@ package org.jf.dexlib2.immutable.instruction;
import org.jf.dexlib2.Format;
import org.jf.dexlib2.Opcode;
import org.jf.dexlib2.iface.instruction.formats.Instruction30t;
import org.jf.dexlib2.util.Preconditions;
import javax.annotation.Nonnull;
@ -46,7 +45,6 @@ public class ImmutableInstruction30t extends ImmutableInstruction implements Ins
public ImmutableInstruction30t(@Nonnull Opcode opcode,
int codeOffset) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.codeOffset = codeOffset;
}

View File

@ -51,7 +51,6 @@ public class ImmutableInstruction31c extends ImmutableInstruction implements Ins
int registerA,
@Nonnull Reference reference) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerA = Preconditions.checkByteRegister(registerA);
this.reference = ImmutableReferenceFactory.of(opcode.referenceType, reference);
}

View File

@ -48,7 +48,6 @@ public class ImmutableInstruction31i extends ImmutableInstruction implements Ins
int registerA,
int literal) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerA = Preconditions.checkByteRegister(registerA);
this.literal = literal;
}

View File

@ -48,7 +48,6 @@ public class ImmutableInstruction31t extends ImmutableInstruction implements Ins
int registerA,
int codeOffset) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerA = Preconditions.checkByteRegister(registerA);
this.codeOffset = codeOffset;
}

View File

@ -48,7 +48,6 @@ public class ImmutableInstruction32x extends ImmutableInstruction implements Ins
int registerA,
int registerB) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerA = Preconditions.checkShortRegister(registerA);
this.registerB = Preconditions.checkShortRegister(registerB);
}

View File

@ -61,7 +61,6 @@ public class ImmutableInstruction35c extends ImmutableInstruction implements Ins
int registerG,
@Nonnull Reference reference) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerCount = Preconditions.check35cRegisterCount(registerCount);
this.registerC = (registerCount>0) ? Preconditions.checkNibbleRegister(registerC) : 0;
this.registerD = (registerCount>1) ? Preconditions.checkNibbleRegister(registerD) : 0;

View File

@ -47,7 +47,7 @@ public class ImmutableInstruction35mi extends ImmutableInstruction implements In
protected final int registerE;
protected final int registerF;
protected final int registerG;
@Nonnull protected final int inlineIndex;
protected final int inlineIndex;
public ImmutableInstruction35mi(@Nonnull Opcode opcode,
int registerCount,
@ -58,7 +58,6 @@ public class ImmutableInstruction35mi extends ImmutableInstruction implements In
int registerG,
int inlineIndex) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerCount = Preconditions.check35cRegisterCount(registerCount);
this.registerC = (registerCount>0) ? Preconditions.checkNibbleRegister(registerC) : 0;
this.registerD = (registerCount>1) ? Preconditions.checkNibbleRegister(registerD) : 0;

View File

@ -47,7 +47,7 @@ public class ImmutableInstruction35ms extends ImmutableInstruction implements In
protected final int registerE;
protected final int registerF;
protected final int registerG;
@Nonnull protected final int vtableIndex;
protected final int vtableIndex;
public ImmutableInstruction35ms(@Nonnull Opcode opcode,
int registerCount,
@ -58,7 +58,6 @@ public class ImmutableInstruction35ms extends ImmutableInstruction implements In
int registerG,
int vtableIndex) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerCount = Preconditions.check35cRegisterCount(registerCount);
this.registerC = (registerCount>0) ? Preconditions.checkNibbleRegister(registerC) : 0;
this.registerD = (registerCount>1) ? Preconditions.checkNibbleRegister(registerD) : 0;

View File

@ -54,7 +54,6 @@ public class ImmutableInstruction3rc extends ImmutableInstruction implements Ins
int registerCount,
@Nonnull Reference reference) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.startRegister = Preconditions.checkShortRegister(startRegister);
this.registerCount = Preconditions.checkRegisterRangeCount(registerCount);
this.reference = ImmutableReferenceFactory.of(opcode.referenceType, reference);

View File

@ -43,7 +43,6 @@ public class ImmutableInstruction3rmi extends ImmutableInstruction implements In
protected final int startRegister;
protected final int registerCount;
protected final int inlineIndex;
public ImmutableInstruction3rmi(@Nonnull Opcode opcode,
@ -51,7 +50,6 @@ public class ImmutableInstruction3rmi extends ImmutableInstruction implements In
int registerCount,
int inlineIndex) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.startRegister = Preconditions.checkShortRegister(startRegister);
this.registerCount = Preconditions.checkRegisterRangeCount(registerCount);
this.inlineIndex = Preconditions.checkInlineIndex(inlineIndex);

View File

@ -43,7 +43,6 @@ public class ImmutableInstruction3rms extends ImmutableInstruction implements In
protected final int startRegister;
protected final int registerCount;
protected final int vtableIndex;
public ImmutableInstruction3rms(@Nonnull Opcode opcode,
@ -51,7 +50,6 @@ public class ImmutableInstruction3rms extends ImmutableInstruction implements In
int registerCount,
int vtableIndex) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.startRegister = Preconditions.checkShortRegister(startRegister);
this.registerCount = Preconditions.checkRegisterRangeCount(registerCount);
this.vtableIndex = Preconditions.checkVtableIndex(vtableIndex);

View File

@ -48,7 +48,6 @@ public class ImmutableInstruction51l extends ImmutableInstruction implements Ins
int registerA,
long literal) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerA = Preconditions.checkByteRegister(registerA);
this.literal = literal;
}

View File

@ -40,7 +40,7 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
public class ImmutableInstructionFactory implements InstructionFactory<ImmutableInstruction, Reference> {
public class ImmutableInstructionFactory implements InstructionFactory<Reference> {
public static final ImmutableInstructionFactory INSTANCE = new ImmutableInstructionFactory();
private ImmutableInstructionFactory() {

View File

@ -109,20 +109,20 @@ public class Preconditions {
return literal;
}
public static int checkByteCodeOffset(int register) {
if (register < -128 || register > 127) {
public static int checkByteCodeOffset(int offset) {
if (offset < -128 || offset > 127) {
throw new IllegalArgumentException(
String.format("Invalid code offset: %d. Must be between -128 and 127, inclusive.", register));
String.format("Invalid code offset: %d. Must be between -128 and 127, inclusive.", offset));
}
return register;
return offset;
}
public static int checkShortCodeOffset(int register) {
if (register < -32768 || register > 32768) {
public static int checkShortCodeOffset(int offset) {
if (offset < -32768 || offset > 32767) {
throw new IllegalArgumentException(
String.format("Invalid code offset: %d. Must be between -32768 and 32767, inclusive.", register));
String.format("Invalid code offset: %d. Must be between -32768 and 32767, inclusive.", offset));
}
return register;
return offset;
}
public static int check35cRegisterCount(int registerCount) {

View File

@ -31,7 +31,11 @@
package org.jf.dexlib2.writer;
import org.jf.dexlib2.builder.MutableMethodImplementation;
import org.jf.dexlib2.iface.ExceptionHandler;
import org.jf.dexlib2.iface.TryBlock;
import org.jf.dexlib2.iface.debug.DebugItem;
import org.jf.dexlib2.iface.instruction.Instruction;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -41,8 +45,7 @@ import java.util.List;
import java.util.Map;
public interface ClassSection<StringKey extends CharSequence, TypeKey extends CharSequence, TypeListKey, ClassKey,
FieldKey, MethodKey, AnnotationSetKey, EncodedValue, DebugItem, Insn,
ExceptionHandler extends org.jf.dexlib2.iface.ExceptionHandler> extends IndexSection<ClassKey> {
FieldKey, MethodKey, AnnotationSetKey, EncodedValue> extends IndexSection<ClassKey> {
@Nonnull Collection<? extends ClassKey> getSortedClasses();
@Nullable Map.Entry<? extends ClassKey, Integer> getClassEntryByType(@Nullable TypeKey key);
@ -73,9 +76,10 @@ public interface ClassSection<StringKey extends CharSequence, TypeKey extends Ch
@Nullable Iterable<? extends StringKey> getParameterNames(@Nonnull MethodKey key);
int getRegisterCount(@Nonnull MethodKey key);
@Nullable Iterable<? extends Insn> getInstructions(@Nonnull MethodKey key);
@Nullable Iterable<? extends Instruction> getInstructions(@Nonnull MethodKey key);
@Nonnull List<? extends TryBlock<? extends ExceptionHandler>> getTryBlocks(@Nonnull MethodKey key);
@Nullable TypeKey getExceptionType(@Nonnull ExceptionHandler handler);
@Nonnull MutableMethodImplementation makeMutableMethodImplementation(@Nonnull MethodKey key);
void setEncodedArrayOffset(@Nonnull ClassKey key, int offset);
int getEncodedArrayOffset(@Nonnull ClassKey key);
@ -89,8 +93,5 @@ public interface ClassSection<StringKey extends CharSequence, TypeKey extends Ch
void setCodeItemOffset(@Nonnull MethodKey key, int offset);
int getCodeItemOffset(@Nonnull MethodKey key);
void setDebugItemOffset(@Nonnull MethodKey key, int offset);
int getDebugItemOffset(@Nonnull MethodKey key);
void writeDebugItem(@Nonnull DebugWriter<StringKey, TypeKey> writer, DebugItem debugItem) throws IOException;
}

View File

@ -36,29 +36,40 @@ import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;
import org.jf.dexlib2.AccessFlags;
import org.jf.dexlib2.Opcode;
import org.jf.dexlib2.ReferenceType;
import org.jf.dexlib2.base.BaseAnnotation;
import org.jf.dexlib2.builder.MutableMethodImplementation;
import org.jf.dexlib2.builder.instruction.BuilderInstruction31c;
import org.jf.dexlib2.dexbacked.raw.*;
import org.jf.dexlib2.iface.Annotation;
import org.jf.dexlib2.iface.ExceptionHandler;
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.OneRegisterInstruction;
import org.jf.dexlib2.iface.instruction.ReferenceInstruction;
import org.jf.dexlib2.iface.instruction.formats.*;
import org.jf.dexlib2.iface.reference.*;
import org.jf.dexlib2.util.InstructionUtil;
import org.jf.dexlib2.util.MethodUtil;
import org.jf.dexlib2.writer.util.InstructionWriteUtil;
import org.jf.dexlib2.writer.io.DeferredOutputStream;
import org.jf.dexlib2.writer.io.DeferredOutputStreamFactory;
import org.jf.dexlib2.writer.io.DexDataStore;
import org.jf.dexlib2.writer.io.MemoryDeferredOutputStream;
import org.jf.dexlib2.writer.util.TryListBuilder;
import org.jf.util.CollectionUtils;
import org.jf.util.ExceptionWithContext;
import org.jf.util.RandomAccessFileOutputStream;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
@ -74,9 +85,7 @@ public abstract class DexWriter<
AnnotationKey extends Annotation, AnnotationSetKey,
TypeListKey,
FieldKey, MethodKey,
EncodedValue, AnnotationElement,
DebugItem extends org.jf.dexlib2.iface.debug.DebugItem,
Insn extends Instruction, ExceptionHandler extends org.jf.dexlib2.iface.ExceptionHandler> {
EncodedValue, AnnotationElement> {
public static final int NO_INDEX = -1;
public static final int NO_OFFSET = 0;
@ -108,7 +117,7 @@ public abstract class DexWriter<
protected int numCodeItemItems = 0;
protected int numClassDataItems = 0;
protected final InstructionFactory<? extends Insn, BaseReference> instructionFactory;
protected final InstructionFactory<BaseReference> instructionFactory;
protected final StringSection<StringKey, StringRef> stringSection;
protected final TypeSection<StringKey, TypeKey, TypeRef> typeSection;
@ -116,21 +125,21 @@ public abstract class DexWriter<
protected final FieldSection<StringKey, TypeKey, FieldRefKey, FieldKey> fieldSection;
protected final MethodSection<StringKey, TypeKey, ProtoKey, MethodRefKey, MethodKey> methodSection;
protected final ClassSection<StringKey, TypeKey, TypeListKey, ClassKey, FieldKey, MethodKey, AnnotationSetKey,
EncodedValue, DebugItem, Insn, ExceptionHandler> classSection;
EncodedValue> classSection;
protected final TypeListSection<TypeKey, TypeListKey> typeListSection;
protected final AnnotationSection<StringKey, TypeKey, AnnotationKey, AnnotationElement, EncodedValue> annotationSection;
protected final AnnotationSetSection<AnnotationKey, AnnotationSetKey> annotationSetSection;
protected DexWriter(int api,
InstructionFactory<? extends Insn, BaseReference> instructionFactory,
InstructionFactory<BaseReference> instructionFactory,
StringSection<StringKey, StringRef> stringSection,
TypeSection<StringKey, TypeKey, TypeRef> typeSection,
ProtoSection<StringKey, TypeKey, ProtoKey, TypeListKey> protoSection,
FieldSection<StringKey, TypeKey, FieldRefKey, FieldKey> fieldSection,
MethodSection<StringKey, TypeKey, ProtoKey, MethodRefKey, MethodKey> methodSection,
ClassSection<StringKey, TypeKey, TypeListKey, ClassKey, FieldKey, MethodKey, AnnotationSetKey,
EncodedValue, DebugItem, Insn, ExceptionHandler> classSection,
EncodedValue> classSection,
TypeListSection<TypeKey, TypeListKey> typeListSection,
AnnotationSection<StringKey, TypeKey, AnnotationKey, AnnotationElement,
EncodedValue> annotationSection,
@ -187,14 +196,17 @@ public abstract class DexWriter<
classSection.getItems().size() * ClassDefItem.ITEM_SIZE;
}
public void writeTo(String path) throws IOException {
RandomAccessFile raf = new RandomAccessFile(path, "rw");
raf.setLength(0);
public void writeTo(@Nonnull DexDataStore dest) throws IOException {
this.writeTo(dest, MemoryDeferredOutputStream.getFactory());
}
public void writeTo(@Nonnull DexDataStore dest,
@Nonnull DeferredOutputStreamFactory tempFactory) throws IOException {
try {
int dataSectionOffset = getDataSectionOffset();
DexDataWriter headerWriter = outputAt(raf, 0);
DexDataWriter indexWriter = outputAt(raf, HeaderItem.ITEM_SIZE);
DexDataWriter offsetWriter = outputAt(raf, dataSectionOffset);
DexDataWriter headerWriter = outputAt(dest, 0);
DexDataWriter indexWriter = outputAt(dest, HeaderItem.ITEM_SIZE);
DexDataWriter offsetWriter = outputAt(dest, dataSectionOffset);
try {
writeStrings(indexWriter, offsetWriter);
writeTypes(indexWriter);
@ -207,8 +219,7 @@ public abstract class DexWriter<
writeAnnotationSets(offsetWriter);
writeAnnotationSetRefs(offsetWriter);
writeAnnotationDirectories(offsetWriter);
writeDebugItems(offsetWriter);
writeCodeItems(offsetWriter);
writeDebugAndCodeItems(offsetWriter, tempFactory.makeDeferredOutputStream());
writeClasses(indexWriter, offsetWriter);
writeMapItem(offsetWriter);
writeHeader(headerWriter, dataSectionOffset, offsetWriter.getPosition());
@ -217,15 +228,14 @@ public abstract class DexWriter<
indexWriter.close();
offsetWriter.close();
}
FileChannel fileChannel = raf.getChannel();
updateSignature(fileChannel);
updateChecksum(fileChannel);
updateSignature(dest);
updateChecksum(dest);
} finally {
raf.close();
dest.close();
}
}
private void updateSignature(FileChannel fileChannel) throws IOException {
private void updateSignature(@Nonnull DexDataStore dataStore) throws IOException {
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-1");
@ -233,14 +243,12 @@ public abstract class DexWriter<
throw new RuntimeException(ex);
}
ByteBuffer buffer = ByteBuffer.allocate(128 * 1024);
fileChannel.position(HeaderItem.HEADER_SIZE_OFFSET);
int bytesRead = fileChannel.read(buffer);
byte[] buffer = new byte[4 * 1024];
InputStream input = dataStore.readAt(HeaderItem.HEADER_SIZE_OFFSET);
int bytesRead = input.read(buffer);
while (bytesRead >= 0) {
buffer.rewind();
md.update(buffer);
buffer.clear();
bytesRead = fileChannel.read(buffer);
md.update(buffer, 0, bytesRead);
bytesRead = input.read(buffer);
}
byte[] signature = md.digest();
@ -249,38 +257,30 @@ public abstract class DexWriter<
}
// write signature
fileChannel.position(HeaderItem.SIGNATURE_OFFSET);
fileChannel.write(ByteBuffer.wrap(signature));
// flush
fileChannel.force(false);
OutputStream output = dataStore.outputAt(HeaderItem.SIGNATURE_OFFSET);
output.write(signature);
output.close();
}
private void updateChecksum(FileChannel fileChannel) throws IOException {
private void updateChecksum(@Nonnull DexDataStore dataStore) throws IOException {
Adler32 a32 = new Adler32();
ByteBuffer buffer = ByteBuffer.allocate(128 * 1024);
fileChannel.position(HeaderItem.SIGNATURE_OFFSET);
int bytesRead = fileChannel.read(buffer);
byte[] buffer = new byte[4 * 1024];
InputStream input = dataStore.readAt(HeaderItem.SIGNATURE_OFFSET);
int bytesRead = input.read(buffer);
while (bytesRead >= 0) {
a32.update(buffer.array(), 0, bytesRead);
buffer.clear();
bytesRead = fileChannel.read(buffer);
a32.update(buffer, 0, bytesRead);
bytesRead = input.read(buffer);
}
// write checksum, utilizing logic in DexWriter to write the integer value properly
fileChannel.position(HeaderItem.CHECKSUM_OFFSET);
int checksum = (int) a32.getValue();
ByteArrayOutputStream checksumBuf = new ByteArrayOutputStream();
DexDataWriter.writeInt(checksumBuf, checksum);
fileChannel.write(ByteBuffer.wrap(checksumBuf.toByteArray()));
// flush
fileChannel.force(false);
OutputStream output = dataStore.outputAt(HeaderItem.CHECKSUM_OFFSET);
DexDataWriter.writeInt(output, (int)a32.getValue());
output.close();
}
private static DexDataWriter outputAt(RandomAccessFile raf, int filePosition) throws IOException {
return new DexDataWriter(new RandomAccessFileOutputStream(raf, filePosition), filePosition);
private static DexDataWriter outputAt(DexDataStore dataStore, int filePosition) throws IOException {
return new DexDataWriter(dataStore.outputAt(filePosition), filePosition);
}
private void writeStrings(@Nonnull DexDataWriter indexWriter, @Nonnull DexDataWriter offsetWriter) throws IOException {
@ -462,7 +462,7 @@ public abstract class DexWriter<
int prevIndex = 0;
for (FieldKey key: fields) {
int index = fieldSection.getFieldIndex(key);
writer.writeUleb128(index-prevIndex);
writer.writeUleb128(index - prevIndex);
writer.writeUleb128(classSection.getFieldAccessFlags(key));
prevIndex = index;
}
@ -706,10 +706,26 @@ public abstract class DexWriter<
}
}
private void writeDebugItems(@Nonnull DexDataWriter writer) throws IOException {
debugSectionOffset = writer.getPosition();
private static class CodeItemOffset<MethodKey> {
@Nonnull MethodKey method;
int codeOffset;
private CodeItemOffset(@Nonnull MethodKey method, int codeOffset) {
this.codeOffset = codeOffset;
this.method = method;
}
}
private void writeDebugAndCodeItems(@Nonnull DexDataWriter offsetWriter,
@Nonnull DeferredOutputStream temp) throws IOException {
ByteArrayOutputStream ehBuf = new ByteArrayOutputStream();
debugSectionOffset = offsetWriter.getPosition();
DebugWriter<StringKey, TypeKey> debugWriter =
new DebugWriter<StringKey, TypeKey>(stringSection, typeSection, writer);
new DebugWriter<StringKey, TypeKey>(stringSection, typeSection, offsetWriter);
DexDataWriter codeWriter = new DexDataWriter(temp, 0);
List<CodeItemOffset<MethodKey>> codeOffsets = Lists.newArrayList();
for (ClassKey classKey: classSection.getSortedClasses()) {
Collection<? extends MethodKey> directMethods = classSection.getSortedDirectMethods(classKey);
@ -718,284 +734,350 @@ public abstract class DexWriter<
Iterable<MethodKey> methods = Iterables.concat(directMethods, virtualMethods);
for (MethodKey methodKey: methods) {
List<? extends TryBlock<? extends ExceptionHandler>> tryBlocks =
classSection.getTryBlocks(methodKey);
Iterable<? extends Instruction> instructions = classSection.getInstructions(methodKey);
Iterable<? extends DebugItem> debugItems = classSection.getDebugItems(methodKey);
Iterable<? extends StringKey> parameterNames = classSection.getParameterNames(methodKey);
int parameterCount = 0;
if (parameterNames != null) {
int index = 0;
for (StringKey parameterName: parameterNames) {
index++;
if (parameterName != null) {
parameterCount = index;
if (instructions != null && stringSection.hasJumboIndexes()) {
boolean needsFix = false;
for (Instruction instruction: instructions) {
if (instruction.getOpcode() == Opcode.CONST_STRING) {
if (stringSection.getItemIndex(
(StringRef)((ReferenceInstruction)instruction).getReference()) >= 65536) {
needsFix = true;
break;
}
}
}
}
if (debugItems == null && parameterCount == 0) {
continue;
}
if (needsFix) {
MutableMethodImplementation mutableMethodImplementation =
classSection.makeMutableMethodImplementation(methodKey);
fixInstructions(mutableMethodImplementation);
numDebugInfoItems++;
classSection.setDebugItemOffset(methodKey, writer.getPosition());
int startingLineNumber = 0;
if (debugItems != null) {
for (org.jf.dexlib2.iface.debug.DebugItem debugItem: debugItems) {
if (debugItem instanceof LineNumber) {
startingLineNumber = ((LineNumber)debugItem).getLineNumber();
break;
}
}
}
writer.writeUleb128(startingLineNumber);
writer.writeUleb128(parameterCount);
if (parameterNames != null) {
int index = 0;
for (StringKey parameterName: parameterNames) {
if (index == parameterCount) {
break;
}
index++;
writer.writeUleb128(stringSection.getNullableItemIndex(parameterName) + 1);
instructions = mutableMethodImplementation.getInstructions();
tryBlocks = mutableMethodImplementation.getTryBlocks();
debugItems = mutableMethodImplementation.getDebugItems();
}
}
if (debugItems != null) {
debugWriter.reset(startingLineNumber);
int debugItemOffset = writeDebugItem(offsetWriter, debugWriter,
classSection.getParameterNames(methodKey), debugItems);
int codeItemOffset = writeCodeItem(codeWriter, ehBuf, methodKey, tryBlocks, instructions, debugItemOffset);
for (DebugItem debugItem: debugItems) {
classSection.writeDebugItem(debugWriter, debugItem);
}
if (codeItemOffset != -1) {
codeOffsets.add(new CodeItemOffset<MethodKey>(methodKey, codeItemOffset));
}
}
}
offsetWriter.align();
codeSectionOffset = offsetWriter.getPosition();
codeWriter.close();
temp.writeTo(offsetWriter);
temp.close();
for (CodeItemOffset<MethodKey> codeOffset: codeOffsets) {
classSection.setCodeItemOffset(codeOffset.method, codeSectionOffset + codeOffset.codeOffset);
}
}
private void fixInstructions(@Nonnull MutableMethodImplementation methodImplementation) {
List<Instruction> instructions = methodImplementation.getInstructions();
for (int i=0; i<instructions.size(); i++) {
Instruction instruction = instructions.get(i);
if (instruction.getOpcode() == Opcode.CONST_STRING) {
if (stringSection.getItemIndex(
(StringRef)((ReferenceInstruction)instruction).getReference()) >= 65536) {
methodImplementation.replaceInstruction(i, new BuilderInstruction31c(Opcode.CONST_STRING_JUMBO,
((OneRegisterInstruction)instruction).getRegisterA(),
((ReferenceInstruction)instruction).getReference()));
}
// write an END_SEQUENCE opcode, to end the debug item
writer.write(0);
}
}
}
private void writeCodeItems(@Nonnull DexDataWriter writer) throws IOException {
ByteArrayOutputStream ehBuf = new ByteArrayOutputStream();
writer.align();
codeSectionOffset = writer.getPosition();
for (ClassKey classKey: classSection.getSortedClasses()) {
Collection<? extends MethodKey> directMethods = classSection.getSortedDirectMethods(classKey);
Collection<? extends MethodKey> virtualMethods = classSection.getSortedVirtualMethods(classKey);
Iterable<MethodKey> methods = Iterables.concat(directMethods, virtualMethods);
for (MethodKey methodKey: methods) {
Iterable<? extends Insn> instructions = classSection.getInstructions(methodKey);
int debugItemOffset = classSection.getDebugItemOffset(methodKey);
if (instructions == null && debugItemOffset == NO_OFFSET) {
continue;
}
numCodeItemItems++;
writer.align();
classSection.setCodeItemOffset(methodKey, writer.getPosition());
writer.writeUshort(classSection.getRegisterCount(methodKey));
boolean isStatic = AccessFlags.STATIC.isSet(classSection.getMethodAccessFlags(methodKey));
Collection<? extends TypeKey> parameters = typeListSection.getTypes(
protoSection.getParameters(methodSection.getPrototype(methodKey)));
List<? extends TryBlock<? extends ExceptionHandler>> tryBlocks = classSection.getTryBlocks(methodKey);
writer.writeUshort(MethodUtil.getParameterRegisterCount(parameters, isStatic));
if (instructions != null) {
tryBlocks = TryListBuilder.massageTryBlocks(tryBlocks);
InstructionWriteUtil<Insn, StringRef, BaseReference> instrWriteUtil =
new InstructionWriteUtil<Insn, StringRef, BaseReference>(instructions, stringSection, instructionFactory);
writer.writeUshort(instrWriteUtil.getOutParamCount());
writer.writeUshort(tryBlocks.size());
writer.writeInt(debugItemOffset);
InstructionWriter instructionWriter =
InstructionWriter.makeInstructionWriter(writer, stringSection, typeSection, fieldSection,
methodSection);
writer.writeInt(instrWriteUtil.getCodeUnitCount());
for (Insn instruction: instrWriteUtil.getInstructions()) {
switch (instruction.getOpcode().format) {
case Format10t:
instructionWriter.write((Instruction10t)instruction);
break;
case Format10x:
instructionWriter.write((Instruction10x)instruction);
break;
case Format11n:
instructionWriter.write((Instruction11n)instruction);
break;
case Format11x:
instructionWriter.write((Instruction11x)instruction);
break;
case Format12x:
instructionWriter.write((Instruction12x)instruction);
break;
case Format20bc:
instructionWriter.write((Instruction20bc)instruction);
break;
case Format20t:
instructionWriter.write((Instruction20t)instruction);
break;
case Format21c:
instructionWriter.write((Instruction21c)instruction);
break;
case Format21ih:
instructionWriter.write((Instruction21ih)instruction);
break;
case Format21lh:
instructionWriter.write((Instruction21lh)instruction);
break;
case Format21s:
instructionWriter.write((Instruction21s)instruction);
break;
case Format21t:
instructionWriter.write((Instruction21t)instruction);
break;
case Format22b:
instructionWriter.write((Instruction22b)instruction);
break;
case Format22c:
instructionWriter.write((Instruction22c)instruction);
break;
case Format22s:
instructionWriter.write((Instruction22s)instruction);
break;
case Format22t:
instructionWriter.write((Instruction22t)instruction);
break;
case Format22x:
instructionWriter.write((Instruction22x)instruction);
break;
case Format23x:
instructionWriter.write((Instruction23x)instruction);
break;
case Format30t:
instructionWriter.write((Instruction30t)instruction);
break;
case Format31c:
instructionWriter.write((Instruction31c)instruction);
break;
case Format31i:
instructionWriter.write((Instruction31i)instruction);
break;
case Format31t:
instructionWriter.write((Instruction31t)instruction);
break;
case Format32x:
instructionWriter.write((Instruction32x)instruction);
break;
case Format35c:
instructionWriter.write((Instruction35c)instruction);
break;
case Format3rc:
instructionWriter.write((Instruction3rc)instruction);
break;
case Format51l:
instructionWriter.write((Instruction51l)instruction);
break;
case ArrayPayload:
instructionWriter.write((ArrayPayload)instruction);
break;
case PackedSwitchPayload:
instructionWriter.write((PackedSwitchPayload)instruction);
break;
case SparseSwitchPayload:
instructionWriter.write((SparseSwitchPayload)instruction);
break;
default:
throw new ExceptionWithContext("Unsupported instruction format: %s",
instruction.getOpcode().format);
}
}
if (tryBlocks.size() > 0) {
writer.align();
// filter out unique lists of exception handlers
Map<List<? extends ExceptionHandler>, Integer> exceptionHandlerOffsetMap = Maps.newHashMap();
for (TryBlock<? extends ExceptionHandler> tryBlock: tryBlocks) {
exceptionHandlerOffsetMap.put(tryBlock.getExceptionHandlers(), 0);
}
DexDataWriter.writeUleb128(ehBuf, exceptionHandlerOffsetMap.size());
for (TryBlock<? extends ExceptionHandler> tryBlock: tryBlocks) {
int startAddress = tryBlock.getStartCodeAddress();
int endAddress = startAddress + tryBlock.getCodeUnitCount();
startAddress += instrWriteUtil.codeOffsetShift(startAddress);
endAddress += instrWriteUtil.codeOffsetShift(endAddress);
int tbCodeUnitCount = endAddress - startAddress;
writer.writeInt(startAddress);
writer.writeUshort(tbCodeUnitCount);
if (tryBlock.getExceptionHandlers().size() == 0) {
throw new ExceptionWithContext("No exception handlers for the try block!");
}
Integer offset = exceptionHandlerOffsetMap.get(tryBlock.getExceptionHandlers());
if (offset != 0) {
// exception handler has already been written out, just use it
writer.writeUshort(offset);
} else {
// if offset has not been set yet, we are about to write out a new exception handler
offset = ehBuf.size();
writer.writeUshort(offset);
exceptionHandlerOffsetMap.put(tryBlock.getExceptionHandlers(), offset);
// check if the last exception handler is a catch-all and adjust the size accordingly
int ehSize = tryBlock.getExceptionHandlers().size();
ExceptionHandler ehLast = tryBlock.getExceptionHandlers().get(ehSize-1);
if (ehLast.getExceptionType() == null) {
ehSize = ehSize * (-1) + 1;
}
// now let's layout the exception handlers, assuming that catch-all is always last
DexDataWriter.writeSleb128(ehBuf, ehSize);
for (ExceptionHandler eh : tryBlock.getExceptionHandlers()) {
TypeKey exceptionTypeKey = classSection.getExceptionType(eh);
int codeAddress = eh.getHandlerCodeAddress();
codeAddress += instrWriteUtil.codeOffsetShift(codeAddress);
if (exceptionTypeKey != null) {
//regular exception handling
DexDataWriter.writeUleb128(ehBuf, typeSection.getItemIndex(exceptionTypeKey));
DexDataWriter.writeUleb128(ehBuf, codeAddress);
} else {
//catch-all
DexDataWriter.writeUleb128(ehBuf, codeAddress);
}
}
}
}
if (ehBuf.size() > 0) {
ehBuf.writeTo(writer);
ehBuf.reset();
}
}
} else {
// no instructions, all we have is the debug item offset
writer.writeUshort(0);
writer.writeUshort(0);
writer.writeInt(debugItemOffset);
writer.writeInt(0);
private int writeDebugItem(@Nonnull DexDataWriter writer,
@Nonnull DebugWriter<StringKey, TypeKey> debugWriter,
@Nullable Iterable<? extends StringKey> parameterNames,
@Nullable Iterable<? extends DebugItem> debugItems) throws IOException {
int parameterCount = 0;
if (parameterNames != null) {
int index = 0;
for (StringKey parameterName: parameterNames) {
index++;
if (parameterName != null) {
parameterCount = index;
}
}
}
if (debugItems == null && parameterCount == 0) {
return NO_OFFSET;
}
numDebugInfoItems++;
int debugItemOffset = writer.getPosition();
int startingLineNumber = 0;
if (debugItems != null) {
for (org.jf.dexlib2.iface.debug.DebugItem debugItem: debugItems) {
if (debugItem instanceof LineNumber) {
startingLineNumber = ((LineNumber)debugItem).getLineNumber();
break;
}
}
}
writer.writeUleb128(startingLineNumber);
writer.writeUleb128(parameterCount);
if (parameterNames != null) {
int index = 0;
for (StringKey parameterName: parameterNames) {
if (index == parameterCount) {
break;
}
index++;
writer.writeUleb128(stringSection.getNullableItemIndex(parameterName) + 1);
}
}
if (debugItems != null) {
debugWriter.reset(startingLineNumber);
for (DebugItem debugItem: debugItems) {
classSection.writeDebugItem(debugWriter, debugItem);
}
}
// write an END_SEQUENCE opcode, to end the debug item
writer.write(0);
return debugItemOffset;
}
private int writeCodeItem(@Nonnull DexDataWriter writer,
@Nonnull ByteArrayOutputStream ehBuf,
@Nonnull MethodKey methodKey,
@Nonnull List<? extends TryBlock<? extends ExceptionHandler>> tryBlocks,
@Nullable Iterable<? extends Instruction> instructions,
int debugItemOffset) throws IOException {
if (instructions == null && debugItemOffset == NO_OFFSET) {
return -1;
}
numCodeItemItems++;
writer.align();
int codeItemOffset = writer.getPosition();
writer.writeUshort(classSection.getRegisterCount(methodKey));
boolean isStatic = AccessFlags.STATIC.isSet(classSection.getMethodAccessFlags(methodKey));
Collection<? extends TypeKey> parameters = typeListSection.getTypes(
protoSection.getParameters(methodSection.getPrototype(methodKey)));
writer.writeUshort(MethodUtil.getParameterRegisterCount(parameters, isStatic));
if (instructions != null) {
tryBlocks = TryListBuilder.massageTryBlocks(tryBlocks);
int outParamCount = 0;
int codeUnitCount = 0;
for (Instruction instruction: instructions) {
codeUnitCount += instruction.getCodeUnits();
if (instruction.getOpcode().referenceType == ReferenceType.METHOD) {
ReferenceInstruction refInsn = (ReferenceInstruction)instruction;
MethodReference methodRef = (MethodReference)refInsn.getReference();
int paramCount = MethodUtil.getParameterRegisterCount(methodRef, InstructionUtil.isInvokeStatic(instruction.getOpcode()));
if (paramCount > outParamCount) {
outParamCount = paramCount;
}
}
}
writer.writeUshort(outParamCount);
writer.writeUshort(tryBlocks.size());
writer.writeInt(debugItemOffset);
InstructionWriter instructionWriter =
InstructionWriter.makeInstructionWriter(writer, stringSection, typeSection, fieldSection,
methodSection);
writer.writeInt(codeUnitCount);
for (Instruction instruction: instructions) {
switch (instruction.getOpcode().format) {
case Format10t:
instructionWriter.write((Instruction10t)instruction);
break;
case Format10x:
instructionWriter.write((Instruction10x)instruction);
break;
case Format11n:
instructionWriter.write((Instruction11n)instruction);
break;
case Format11x:
instructionWriter.write((Instruction11x)instruction);
break;
case Format12x:
instructionWriter.write((Instruction12x)instruction);
break;
case Format20bc:
instructionWriter.write((Instruction20bc)instruction);
break;
case Format20t:
instructionWriter.write((Instruction20t)instruction);
break;
case Format21c:
instructionWriter.write((Instruction21c)instruction);
break;
case Format21ih:
instructionWriter.write((Instruction21ih)instruction);
break;
case Format21lh:
instructionWriter.write((Instruction21lh)instruction);
break;
case Format21s:
instructionWriter.write((Instruction21s)instruction);
break;
case Format21t:
instructionWriter.write((Instruction21t)instruction);
break;
case Format22b:
instructionWriter.write((Instruction22b)instruction);
break;
case Format22c:
instructionWriter.write((Instruction22c)instruction);
break;
case Format22s:
instructionWriter.write((Instruction22s)instruction);
break;
case Format22t:
instructionWriter.write((Instruction22t)instruction);
break;
case Format22x:
instructionWriter.write((Instruction22x)instruction);
break;
case Format23x:
instructionWriter.write((Instruction23x)instruction);
break;
case Format30t:
instructionWriter.write((Instruction30t)instruction);
break;
case Format31c:
instructionWriter.write((Instruction31c)instruction);
break;
case Format31i:
instructionWriter.write((Instruction31i)instruction);
break;
case Format31t:
instructionWriter.write((Instruction31t)instruction);
break;
case Format32x:
instructionWriter.write((Instruction32x)instruction);
break;
case Format35c:
instructionWriter.write((Instruction35c)instruction);
break;
case Format3rc:
instructionWriter.write((Instruction3rc)instruction);
break;
case Format51l:
instructionWriter.write((Instruction51l)instruction);
break;
case ArrayPayload:
instructionWriter.write((ArrayPayload)instruction);
break;
case PackedSwitchPayload:
instructionWriter.write((PackedSwitchPayload)instruction);
break;
case SparseSwitchPayload:
instructionWriter.write((SparseSwitchPayload)instruction);
break;
default:
throw new ExceptionWithContext("Unsupported instruction format: %s",
instruction.getOpcode().format);
}
}
if (tryBlocks.size() > 0) {
writer.align();
// filter out unique lists of exception handlers
Map<List<? extends ExceptionHandler>, Integer> exceptionHandlerOffsetMap = Maps.newHashMap();
for (TryBlock<? extends ExceptionHandler> tryBlock: tryBlocks) {
exceptionHandlerOffsetMap.put(tryBlock.getExceptionHandlers(), 0);
}
DexDataWriter.writeUleb128(ehBuf, exceptionHandlerOffsetMap.size());
for (TryBlock<? extends ExceptionHandler> tryBlock: tryBlocks) {
int startAddress = tryBlock.getStartCodeAddress();
int endAddress = startAddress + tryBlock.getCodeUnitCount();
int tbCodeUnitCount = endAddress - startAddress;
writer.writeInt(startAddress);
writer.writeUshort(tbCodeUnitCount);
if (tryBlock.getExceptionHandlers().size() == 0) {
throw new ExceptionWithContext("No exception handlers for the try block!");
}
Integer offset = exceptionHandlerOffsetMap.get(tryBlock.getExceptionHandlers());
if (offset != 0) {
// exception handler has already been written out, just use it
writer.writeUshort(offset);
} else {
// if offset has not been set yet, we are about to write out a new exception handler
offset = ehBuf.size();
writer.writeUshort(offset);
exceptionHandlerOffsetMap.put(tryBlock.getExceptionHandlers(), offset);
// check if the last exception handler is a catch-all and adjust the size accordingly
int ehSize = tryBlock.getExceptionHandlers().size();
ExceptionHandler ehLast = tryBlock.getExceptionHandlers().get(ehSize-1);
if (ehLast.getExceptionType() == null) {
ehSize = ehSize * (-1) + 1;
}
// now let's layout the exception handlers, assuming that catch-all is always last
DexDataWriter.writeSleb128(ehBuf, ehSize);
for (ExceptionHandler eh : tryBlock.getExceptionHandlers()) {
TypeKey exceptionTypeKey = classSection.getExceptionType(eh);
int codeAddress = eh.getHandlerCodeAddress();
if (exceptionTypeKey != null) {
//regular exception handling
DexDataWriter.writeUleb128(ehBuf, typeSection.getItemIndex(exceptionTypeKey));
DexDataWriter.writeUleb128(ehBuf, codeAddress);
} else {
//catch-all
DexDataWriter.writeUleb128(ehBuf, codeAddress);
}
}
}
}
if (ehBuf.size() > 0) {
ehBuf.writeTo(writer);
ehBuf.reset();
}
}
} else {
// no instructions, all we have is the debug item offset
writer.writeUshort(0);
writer.writeUshort(0);
writer.writeInt(debugItemOffset);
writer.writeInt(0);
}
return codeItemOffset;
}
private int calcNumItems() {

View File

@ -41,36 +41,36 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
public interface InstructionFactory<Insn extends Instruction, Ref extends Reference> {
Insn makeInstruction10t(@Nonnull Opcode opcode, int codeOffset);
Insn makeInstruction10x(@Nonnull Opcode opcode);
Insn makeInstruction11n(@Nonnull Opcode opcode, int registerA, int literal);
Insn makeInstruction11x(@Nonnull Opcode opcode, int registerA);
Insn makeInstruction12x(@Nonnull Opcode opcode, int registerA, int registerB);
Insn makeInstruction20bc(@Nonnull Opcode opcode, int verificationError, @Nonnull Ref reference);
Insn makeInstruction20t(@Nonnull Opcode opcode, int codeOffset);
Insn makeInstruction21c(@Nonnull Opcode opcode, int registerA, @Nonnull Ref reference);
Insn makeInstruction21ih(@Nonnull Opcode opcode, int registerA, int literal);
Insn makeInstruction21lh(@Nonnull Opcode opcode, int registerA, long literal);
Insn makeInstruction21s(@Nonnull Opcode opcode, int registerA, int literal);
Insn makeInstruction21t(@Nonnull Opcode opcode, int registerA, int codeOffset);
Insn makeInstruction22b(@Nonnull Opcode opcode, int registerA, int registerB, int literal);
Insn makeInstruction22c(@Nonnull Opcode opcode, int registerA, int registerB, @Nonnull Ref reference);
Insn makeInstruction22s(@Nonnull Opcode opcode, int registerA, int registerB, int literal);
Insn makeInstruction22t(@Nonnull Opcode opcode, int registerA, int registerB, int codeOffset);
Insn makeInstruction22x(@Nonnull Opcode opcode, int registerA, int registerB);
Insn makeInstruction23x(@Nonnull Opcode opcode, int registerA, int registerB, int registerC);
Insn makeInstruction30t(@Nonnull Opcode opcode, int codeOffset);
Insn makeInstruction31c(@Nonnull Opcode opcode, int registerA, @Nonnull Ref reference);
Insn makeInstruction31i(@Nonnull Opcode opcode, int registerA, int literal);
Insn makeInstruction31t(@Nonnull Opcode opcode, int registerA, int codeOffset);
Insn makeInstruction32x(@Nonnull Opcode opcode, int registerA, int registerB);
Insn makeInstruction35c(@Nonnull Opcode opcode, int registerCount, int registerC, int registerD, int registerE,
public interface InstructionFactory<Ref extends Reference> {
Instruction makeInstruction10t(@Nonnull Opcode opcode, int codeOffset);
Instruction makeInstruction10x(@Nonnull Opcode opcode);
Instruction makeInstruction11n(@Nonnull Opcode opcode, int registerA, int literal);
Instruction makeInstruction11x(@Nonnull Opcode opcode, int registerA);
Instruction makeInstruction12x(@Nonnull Opcode opcode, int registerA, int registerB);
Instruction makeInstruction20bc(@Nonnull Opcode opcode, int verificationError, @Nonnull Ref reference);
Instruction makeInstruction20t(@Nonnull Opcode opcode, int codeOffset);
Instruction makeInstruction21c(@Nonnull Opcode opcode, int registerA, @Nonnull Ref reference);
Instruction makeInstruction21ih(@Nonnull Opcode opcode, int registerA, int literal);
Instruction makeInstruction21lh(@Nonnull Opcode opcode, int registerA, long literal);
Instruction makeInstruction21s(@Nonnull Opcode opcode, int registerA, int literal);
Instruction makeInstruction21t(@Nonnull Opcode opcode, int registerA, int codeOffset);
Instruction makeInstruction22b(@Nonnull Opcode opcode, int registerA, int registerB, int literal);
Instruction makeInstruction22c(@Nonnull Opcode opcode, int registerA, int registerB, @Nonnull Ref reference);
Instruction makeInstruction22s(@Nonnull Opcode opcode, int registerA, int registerB, int literal);
Instruction makeInstruction22t(@Nonnull Opcode opcode, int registerA, int registerB, int codeOffset);
Instruction makeInstruction22x(@Nonnull Opcode opcode, int registerA, int registerB);
Instruction makeInstruction23x(@Nonnull Opcode opcode, int registerA, int registerB, int registerC);
Instruction makeInstruction30t(@Nonnull Opcode opcode, int codeOffset);
Instruction makeInstruction31c(@Nonnull Opcode opcode, int registerA, @Nonnull Ref reference);
Instruction makeInstruction31i(@Nonnull Opcode opcode, int registerA, int literal);
Instruction makeInstruction31t(@Nonnull Opcode opcode, int registerA, int codeOffset);
Instruction makeInstruction32x(@Nonnull Opcode opcode, int registerA, int registerB);
Instruction makeInstruction35c(@Nonnull Opcode opcode, int registerCount, int registerC, int registerD, int registerE,
int registerF, int registerG, @Nonnull Ref reference);
Insn makeInstruction3rc(@Nonnull Opcode opcode, int startRegister, int registerCount,
Instruction makeInstruction3rc(@Nonnull Opcode opcode, int startRegister, int registerCount,
@Nonnull Ref reference);
Insn makeInstruction51l(@Nonnull Opcode opcode, int registerA, long literal);
Insn makeSparseSwitchPayload(@Nullable List<? extends SwitchElement> switchElements);
Insn makePackedSwitchPayload(@Nullable List<? extends SwitchElement> switchElements);
Insn makeArrayPayload(int elementWidth, @Nullable List<Number> arrayElements);
Instruction makeInstruction51l(@Nonnull Opcode opcode, int registerA, long literal);
Instruction makeSparseSwitchPayload(@Nullable List<? extends SwitchElement> switchElements);
Instruction makePackedSwitchPayload(@Nullable List<? extends SwitchElement> switchElements);
Instruction makeArrayPayload(int elementWidth, @Nullable List<Number> arrayElements);
}

View File

@ -32,8 +32,10 @@
package org.jf.dexlib2.writer;
import org.jf.dexlib2.iface.reference.StringReference;
import org.jf.dexlib2.writer.util.InstructionWriteUtil;
public interface StringSection<StringKey, StringRef extends StringReference> extends NullableIndexSection<StringKey>,
InstructionWriteUtil.StringIndexProvider<StringRef> {
import javax.annotation.Nonnull;
public interface StringSection<StringKey, StringRef extends StringReference> extends NullableIndexSection<StringKey> {
int getItemIndex(@Nonnull StringRef key);
boolean hasJumboIndexes();
}

View File

@ -73,10 +73,20 @@ public class BuilderClassDef extends BaseTypeReference implements ClassDef {
this.interfaces = interfaces;
this.sourceFile = sourceFile;
this.annotations = annotations;
this.staticFields = ImmutableSortedSet.copyOf(Iterables.filter(fields, FieldUtil.FIELD_IS_STATIC));
this.instanceFields = ImmutableSortedSet.copyOf(Iterables.filter(fields, FieldUtil.FIELD_IS_INSTANCE));
this.directMethods = ImmutableSortedSet.copyOf(Iterables.filter(methods, MethodUtil.METHOD_IS_DIRECT));
this.virtualMethods = ImmutableSortedSet.copyOf(Iterables.filter(methods, MethodUtil.METHOD_IS_VIRTUAL));
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(); }

View File

@ -35,17 +35,19 @@ import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.*;
import org.jf.dexlib2.DebugItemType;
import org.jf.dexlib2.builder.MutableMethodImplementation;
import org.jf.dexlib2.iface.ExceptionHandler;
import org.jf.dexlib2.iface.Field;
import org.jf.dexlib2.iface.MethodImplementation;
import org.jf.dexlib2.iface.TryBlock;
import org.jf.dexlib2.iface.debug.EndLocal;
import org.jf.dexlib2.iface.debug.LineNumber;
import org.jf.dexlib2.iface.debug.RestartLocal;
import org.jf.dexlib2.iface.debug.*;
import org.jf.dexlib2.iface.instruction.Instruction;
import org.jf.dexlib2.iface.reference.StringReference;
import org.jf.dexlib2.iface.reference.TypeReference;
import org.jf.dexlib2.iface.value.EncodedValue;
import org.jf.dexlib2.util.EncodedValueUtils;
import org.jf.dexlib2.writer.ClassSection;
import org.jf.dexlib2.writer.DebugWriter;
import org.jf.dexlib2.writer.builder.BuilderDebugItem.BuilderSetSourceFile;
import org.jf.dexlib2.writer.builder.BuilderDebugItem.BuilderStartLocal;
import org.jf.dexlib2.writer.builder.BuilderEncodedValues.BuilderEncodedValue;
import org.jf.util.AbstractForwardSequentialList;
import org.jf.util.CollectionUtils;
@ -59,8 +61,7 @@ import java.util.Map.Entry;
import java.util.concurrent.ConcurrentMap;
public class BuilderClassPool implements ClassSection<BuilderStringReference, BuilderTypeReference, BuilderTypeList,
BuilderClassDef, BuilderField, BuilderMethod, BuilderAnnotationSet, BuilderEncodedValue, BuilderDebugItem,
BuilderInstruction, BuilderExceptionHandler> {
BuilderClassDef, BuilderField, BuilderMethod, BuilderAnnotationSet, BuilderEncodedValue> {
@Nonnull private final ConcurrentMap<String, BuilderClassDef> internedItems =
Maps.newConcurrentMap();
@ -266,8 +267,8 @@ public class BuilderClassPool implements ClassSection<BuilderStringReference, Bu
}
@Nullable @Override
public Iterable<? extends BuilderDebugItem> getDebugItems(@Nonnull BuilderMethod builderMethod) {
BuilderMethodImplementation impl = builderMethod.getImplementation();
public Iterable<? extends DebugItem> getDebugItems(@Nonnull BuilderMethod builderMethod) {
MethodImplementation impl = builderMethod.getImplementation();
if (impl == null) {
return null;
}
@ -284,16 +285,16 @@ public class BuilderClassPool implements ClassSection<BuilderStringReference, Bu
}
@Override public int getRegisterCount(@Nonnull BuilderMethod builderMethod) {
BuilderMethodImplementation impl = builderMethod.getImplementation();
MethodImplementation impl = builderMethod.getImplementation();
if (impl == null) {
return 0;
}
return impl.registerCount;
return impl.getRegisterCount();
}
@Nullable @Override
public Iterable<? extends BuilderInstruction> getInstructions(@Nonnull BuilderMethod builderMethod) {
BuilderMethodImplementation impl = builderMethod.getImplementation();
public Iterable<? extends Instruction> getInstructions(@Nonnull BuilderMethod builderMethod) {
MethodImplementation impl = builderMethod.getImplementation();
if (impl == null) {
return null;
}
@ -301,16 +302,25 @@ public class BuilderClassPool implements ClassSection<BuilderStringReference, Bu
}
@Nonnull @Override
public List<? extends TryBlock<? extends BuilderExceptionHandler>> getTryBlocks(@Nonnull BuilderMethod builderMethod) {
BuilderMethodImplementation impl = builderMethod.getImplementation();
public List<? extends TryBlock<? extends ExceptionHandler>> getTryBlocks(@Nonnull BuilderMethod builderMethod) {
MethodImplementation impl = builderMethod.getImplementation();
if (impl == null) {
return ImmutableList.of();
}
return impl.getTryBlocks();
}
@Nullable @Override public BuilderTypeReference getExceptionType(@Nonnull BuilderExceptionHandler handler) {
return handler.exceptionType;
@Nullable @Override public BuilderTypeReference getExceptionType(@Nonnull ExceptionHandler handler) {
return checkTypeReference(handler.getExceptionTypeReference());
}
@Nonnull @Override
public MutableMethodImplementation makeMutableMethodImplementation(@Nonnull BuilderMethod builderMethod) {
MethodImplementation impl = builderMethod.getImplementation();
if (impl instanceof MutableMethodImplementation) {
return (MutableMethodImplementation)impl;
}
return new MutableMethodImplementation(impl);
}
@Override public void setEncodedArrayOffset(@Nonnull BuilderClassDef builderClassDef, int offset) {
@ -345,25 +355,41 @@ public class BuilderClassPool implements ClassSection<BuilderStringReference, Bu
return builderMethod.codeItemOffset;
}
@Override public void setDebugItemOffset(@Nonnull BuilderMethod builderMethod, int offset) {
builderMethod.debugInfoOffset = offset;
@Nullable private BuilderStringReference checkStringReference(@Nullable StringReference stringReference) {
if (stringReference == null) {
return null;
}
try {
return (BuilderStringReference)stringReference;
} catch (ClassCastException ex) {
throw new IllegalStateException("Only StringReference instances returned by " +
"DexBuilder.internStringReference or DexBuilder.internNullableStringReference may be used.");
}
}
@Override public int getDebugItemOffset(@Nonnull BuilderMethod builderMethod) {
return builderMethod.debugInfoOffset;
@Nullable private BuilderTypeReference checkTypeReference(@Nullable TypeReference typeReference) {
if (typeReference == null) {
return null;
}
try {
return (BuilderTypeReference)typeReference;
} catch (ClassCastException ex) {
throw new IllegalStateException("Only TypeReference instances returned by " +
"DexBuilder.internTypeReference or DexBuilder.internNullableTypeReference may be used.");
}
}
@Override
public void writeDebugItem(@Nonnull DebugWriter<BuilderStringReference, BuilderTypeReference> writer,
BuilderDebugItem debugItem) throws IOException {
DebugItem debugItem) throws IOException {
switch (debugItem.getDebugItemType()) {
case DebugItemType.START_LOCAL: {
BuilderStartLocal startLocal = (BuilderStartLocal)debugItem;
StartLocal startLocal = (StartLocal)debugItem;
writer.writeStartLocal(startLocal.getCodeAddress(),
startLocal.register,
startLocal.name,
startLocal.type,
startLocal.signature);
startLocal.getRegister(),
checkStringReference(startLocal.getNameReference()),
checkTypeReference(startLocal.getTypeReference()),
checkStringReference(startLocal.getSignatureReference()));
break;
}
case DebugItemType.END_LOCAL: {
@ -390,8 +416,9 @@ public class BuilderClassPool implements ClassSection<BuilderStringReference, Bu
break;
}
case DebugItemType.SET_SOURCE_FILE: {
BuilderSetSourceFile setSourceFile = (BuilderSetSourceFile)debugItem;
writer.writeSetSourceFile(setSourceFile.getCodeAddress(), setSourceFile.sourceFile);
SetSourceFile setSourceFile = (SetSourceFile)debugItem;
writer.writeSetSourceFile(setSourceFile.getCodeAddress(),
checkStringReference(setSourceFile.getSourceFileReference()));
}
default:
throw new ExceptionWithContext("Unexpected debug item type: %d", debugItem.getDebugItemType());

View File

@ -1,168 +0,0 @@
/*
* 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.writer.builder;
import org.jf.dexlib2.DebugItemType;
import org.jf.dexlib2.iface.debug.*;
import org.jf.dexlib2.immutable.debug.ImmutableEpilogueBegin;
import org.jf.dexlib2.immutable.debug.ImmutableLineNumber;
import org.jf.dexlib2.immutable.debug.ImmutablePrologueEnd;
import javax.annotation.Nullable;
public abstract interface BuilderDebugItem extends DebugItem {
abstract static class BaseBuilderDebugItem implements BuilderDebugItem {
final int codeAddress;
public BaseBuilderDebugItem(int codeAddress) {
this.codeAddress = codeAddress;
}
@Override public int getCodeAddress() { return codeAddress; }
}
public static class BuilderStartLocal extends BaseBuilderDebugItem implements StartLocal {
final int register;
@Nullable final BuilderStringReference name;
@Nullable final BuilderTypeReference type;
@Nullable final BuilderStringReference signature;
BuilderStartLocal(int codeAddress,
int register,
@Nullable BuilderStringReference name,
@Nullable BuilderTypeReference type,
@Nullable BuilderStringReference signature) {
super(codeAddress);
this.register = register;
this.name = name;
this.type = type;
this.signature = signature;
}
@Override public int getRegister() { return register; }
@Nullable @Override public String getName() { return name==null?null:name.getString(); }
@Nullable @Override public String getType() { return type==null?null:type.getType(); }
@Nullable @Override public String getSignature() { return signature==null?null:signature.getString(); }
@Override public int getDebugItemType() { return DebugItemType.START_LOCAL; }
}
public static class BuilderEndLocal extends BaseBuilderDebugItem implements EndLocal {
private final int register;
BuilderEndLocal(int codeAddress, int register) {
super(codeAddress);
this.register = register;
}
@Override public int getRegister() {
return register;
}
@Override public int getDebugItemType() {
return DebugItemType.END_LOCAL;
}
@Nullable @Override public String getName() {
return null;
}
@Nullable @Override public String getType() {
return null;
}
@Nullable @Override public String getSignature() {
return null;
}
}
public static class BuilderRestartLocal extends BaseBuilderDebugItem implements RestartLocal {
private final int register;
BuilderRestartLocal(int codeAddress, int register) {
super(codeAddress);
this.register = register;
}
@Override public int getRegister() {
return register;
}
@Override public int getDebugItemType() {
return DebugItemType.RESTART_LOCAL;
}
@Nullable @Override public String getName() {
return null;
}
@Nullable @Override public String getType() {
return null;
}
@Nullable @Override public String getSignature() {
return null;
}
}
public static class BuilderPrologueEnd extends ImmutablePrologueEnd implements BuilderDebugItem {
BuilderPrologueEnd(int codeAddress) {
super(codeAddress);
}
}
public static class BuilderEpilogueBegin extends ImmutableEpilogueBegin implements BuilderDebugItem {
BuilderEpilogueBegin(int codeAddress) {
super(codeAddress);
}
}
public static class BuilderLineNumber extends ImmutableLineNumber implements BuilderDebugItem {
BuilderLineNumber(int codeAddress, int lineNumber) {
super(codeAddress, lineNumber);
}
}
public static class BuilderSetSourceFile extends BaseBuilderDebugItem implements SetSourceFile {
@Nullable final BuilderStringReference sourceFile;
BuilderSetSourceFile(int codeAddress,
@Nullable BuilderStringReference sourceFile) {
super(codeAddress);
this.sourceFile = sourceFile;
}
@Nullable @Override public String getSourceFile() { return sourceFile==null?null:sourceFile.getString(); }
@Override public int getDebugItemType() { return DebugItemType.SET_SOURCE_FILE; }
}
}

View File

@ -49,6 +49,7 @@ public interface BuilderInstruction extends Instruction {
@Nonnull protected final Opcode opcode;
public BaseBuilderInstruction(@Nonnull Opcode opcode) {
Preconditions.checkFormat(opcode, getFormat());
this.opcode = opcode;
}
@ -103,7 +104,6 @@ public interface BuilderInstruction extends Instruction {
int verificationError,
@Nonnull BuilderReference reference) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.verificationError = Preconditions.checkVerificationError(verificationError);
this.reference = Preconditions.checkReference(opcode.referenceType, reference);
}
@ -130,7 +130,6 @@ public interface BuilderInstruction extends Instruction {
int registerA,
@Nonnull BuilderReference reference) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerA = Preconditions.checkByteRegister(registerA);
this.reference = Preconditions.checkReference(opcode.referenceType, reference);
}
@ -183,7 +182,6 @@ public interface BuilderInstruction extends Instruction {
int registerB,
@Nonnull BuilderReference reference) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerA = Preconditions.checkNibbleRegister(registerA);
this.registerB = Preconditions.checkNibbleRegister(registerB);
this.reference = Preconditions.checkReference(opcode.referenceType, reference);
@ -236,7 +234,6 @@ public interface BuilderInstruction extends Instruction {
int registerA,
@Nonnull BuilderReference reference) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerA = Preconditions.checkByteRegister(registerA);
this.reference = Preconditions.checkReference(opcode.referenceType, reference);
}
@ -285,7 +282,6 @@ public interface BuilderInstruction extends Instruction {
int registerG,
@Nonnull BuilderReference reference) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.registerCount = Preconditions.check35cRegisterCount(registerCount);
this.registerC = (registerCount>0) ? Preconditions.checkNibbleRegister(registerC) : 0;
this.registerD = (registerCount>1) ? Preconditions.checkNibbleRegister(registerD) : 0;
@ -319,8 +315,6 @@ public interface BuilderInstruction extends Instruction {
int registerCount,
@Nonnull BuilderReference reference) {
super(opcode);
Preconditions.checkFormat(opcode, FORMAT);
this.startRegister = Preconditions.checkShortRegister(startRegister);
this.registerCount = Preconditions.checkRegisterRangeCount(registerCount);
this.reference = Preconditions.checkReference(opcode.referenceType, reference);

View File

@ -40,7 +40,7 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
public class BuilderInstructionFactory implements InstructionFactory<BuilderInstruction, BuilderReference> {
public class BuilderInstructionFactory implements InstructionFactory<BuilderReference> {
public static final BuilderInstructionFactory INSTANCE = new BuilderInstructionFactory();
private BuilderInstructionFactory() {

View File

@ -33,6 +33,7 @@ package org.jf.dexlib2.writer.builder;
import org.jf.dexlib2.base.reference.BaseMethodReference;
import org.jf.dexlib2.iface.Method;
import org.jf.dexlib2.iface.MethodImplementation;
import org.jf.dexlib2.writer.DexWriter;
import javax.annotation.Nonnull;
@ -44,17 +45,16 @@ public class BuilderMethod extends BaseMethodReference implements Method {
@Nonnull final List<? extends BuilderMethodParameter> parameters;
final int accessFlags;
@Nonnull final BuilderAnnotationSet annotations;
@Nullable final BuilderMethodImplementation methodImplementation;
@Nullable final MethodImplementation methodImplementation;
int annotationSetRefListOffset = DexWriter.NO_OFFSET;
int codeItemOffset = DexWriter.NO_OFFSET;
int debugInfoOffset = DexWriter.NO_OFFSET;
BuilderMethod(@Nonnull BuilderMethodReference methodReference,
@Nonnull List<? extends BuilderMethodParameter> parameters,
int accessFlags,
@Nonnull BuilderAnnotationSet annotations,
@Nullable BuilderMethodImplementation methodImplementation) {
@Nullable MethodImplementation methodImplementation) {
this.methodReference = methodReference;
this.parameters = parameters;
this.accessFlags = accessFlags;
@ -69,5 +69,5 @@ public class BuilderMethod extends BaseMethodReference implements Method {
@Override @Nonnull public List<? extends BuilderMethodParameter> getParameters() { return parameters; }
@Override public int getAccessFlags() { return accessFlags; }
@Override @Nonnull public BuilderAnnotationSet getAnnotations() { return annotations; }
@Override @Nullable public BuilderMethodImplementation getImplementation() { return methodImplementation; }
@Override @Nullable public MethodImplementation getImplementation() { return methodImplementation; }
}

View File

@ -1,59 +0,0 @@
/*
* 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.writer.builder;
import org.jf.dexlib2.iface.MethodImplementation;
import javax.annotation.Nonnull;
import java.util.List;
public class BuilderMethodImplementation implements MethodImplementation {
protected final int registerCount;
@Nonnull protected final List<? extends BuilderInstruction> instructions;
@Nonnull protected final List<? extends BuilderTryBlock> tryBlocks;
@Nonnull protected final List<? extends BuilderDebugItem> debugItems;
public BuilderMethodImplementation(int registerCount,
@Nonnull List<? extends BuilderInstruction> instructions,
@Nonnull List<? extends BuilderTryBlock> tryBlocks,
@Nonnull List<? extends BuilderDebugItem> debugItems) {
this.registerCount = registerCount;
this.instructions = instructions;
this.tryBlocks = tryBlocks;
this.debugItems = debugItems;
}
@Override public int getRegisterCount() { return registerCount; }
@Nonnull @Override public List<? extends BuilderInstruction> getInstructions() { return instructions; }
@Nonnull @Override public List<? extends BuilderTryBlock> getTryBlocks() { return tryBlocks; }
@Nonnull @Override public List<? extends BuilderDebugItem> getDebugItems() { return debugItems; }
}

View File

@ -69,6 +69,10 @@ class BuilderStringPool implements StringSection<BuilderStringReference, Builder
return key.index;
}
@Override public boolean hasJumboIndexes() {
return internedItems.size() > 65536;
}
@Nonnull @Override public Collection<? extends Entry<? extends BuilderStringReference, Integer>> getItems() {
return new BuilderMapEntryCollection<BuilderStringReference>(internedItems.values()) {
@Override protected int getValue(@Nonnull BuilderStringReference key) {

View File

@ -37,11 +37,11 @@ import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import org.jf.dexlib2.ValueType;
import org.jf.dexlib2.iface.Annotation;
import org.jf.dexlib2.iface.MethodImplementation;
import org.jf.dexlib2.iface.MethodParameter;
import org.jf.dexlib2.iface.reference.*;
import org.jf.dexlib2.iface.value.*;
import org.jf.dexlib2.writer.DexWriter;
import org.jf.dexlib2.writer.builder.BuilderDebugItem.*;
import org.jf.dexlib2.writer.builder.BuilderEncodedValues.*;
import org.jf.util.ExceptionWithContext;
@ -56,7 +56,7 @@ import java.util.Set;
public class DexBuilder extends DexWriter<BuilderStringReference, BuilderStringReference, BuilderTypeReference,
BuilderTypeReference, BuilderProtoReference, BuilderFieldReference, BuilderMethodReference, BuilderReference,
BuilderClassDef, BuilderAnnotation, BuilderAnnotationSet, BuilderTypeList, BuilderField, BuilderMethod,
BuilderEncodedValue, BuilderAnnotationElement, BuilderDebugItem, BuilderInstruction, BuilderExceptionHandler> {
BuilderEncodedValue, BuilderAnnotationElement> {
private final BuilderContext context;
@ -95,7 +95,7 @@ public class DexBuilder extends DexWriter<BuilderStringReference, BuilderStringR
@Nonnull String returnType,
int accessFlags,
@Nonnull Set<? extends Annotation> annotations,
@Nullable BuilderMethodImplementation methodImplementation) {
@Nullable MethodImplementation methodImplementation) {
if (parameters == null) {
parameters = ImmutableList.of();
}
@ -106,26 +106,6 @@ public class DexBuilder extends DexWriter<BuilderStringReference, BuilderStringR
methodImplementation);
}
@Nonnull public BuilderMethodImplementation internMethodImplementation(
int registerCount,
@Nullable List<? extends BuilderInstruction> instructions,
@Nullable List<? extends BuilderTryBlock> tryBlocks,
@Nullable List<? extends BuilderDebugItem> debugItems) {
if (instructions == null) {
instructions = ImmutableList.of();
}
if (tryBlocks == null) {
tryBlocks = ImmutableList.of();
}
if (debugItems == null) {
debugItems = ImmutableList.of();
}
return new BuilderMethodImplementation(registerCount, instructions, tryBlocks, debugItems);
}
@Nonnull public BuilderClassDef internClassDef(@Nonnull String type,
int accessFlags,
@Nullable String superclass,
@ -164,10 +144,24 @@ public class DexBuilder extends DexWriter<BuilderStringReference, BuilderStringR
return context.stringPool.internString(string);
}
@Nullable public BuilderStringReference internNullableStringReference(@Nullable String string) {
if (string != null) {
return internStringReference(string);
}
return null;
}
@Nonnull public BuilderTypeReference internTypeReference(@Nonnull String type) {
return context.typePool.internType(type);
}
@Nullable public BuilderTypeReference internNullableTypeReference(@Nullable String type) {
if (type != null) {
return internTypeReference(type);
}
return null;
}
@Nonnull public BuilderFieldReference internFieldReference(@Nonnull FieldReference field) {
return context.fieldPool.internField(field);
}
@ -191,7 +185,7 @@ public class DexBuilder extends DexWriter<BuilderStringReference, BuilderStringR
}
throw new IllegalArgumentException("Could not determine type of reference");
}
@Nonnull private List<BuilderMethodParameter> internMethodParameters(
@Nullable List<? extends MethodParameter> methodParameters) {
if (methodParameters == null) {
@ -212,46 +206,6 @@ public class DexBuilder extends DexWriter<BuilderStringReference, BuilderStringR
context.annotationSetPool.internAnnotationSet(methodParameter.getAnnotations()));
}
@Nonnull public BuilderExceptionHandler internExceptionHandler(@Nullable String exceptionType,
int handlerCodeAddress) {
return new BuilderExceptionHandler(context.typePool.internNullableType(exceptionType),
handlerCodeAddress);
}
@Nonnull public BuilderStartLocal internStartLocal(int codeAddress, int register, @Nullable String name,
@Nullable String type, @Nullable String signature) {
return new BuilderStartLocal(codeAddress,
register,
context.stringPool.internNullableString(name),
context.typePool.internNullableType(type),
context.stringPool.internNullableString(signature));
}
@Nonnull public BuilderSetSourceFile internSetSourceFile(int codeAddress, @Nullable String sourceFile) {
return new BuilderSetSourceFile(codeAddress,
context.stringPool.internNullableString(sourceFile));
}
@Nonnull public BuilderEndLocal internEndLocal(int codeAddress, int register) {
return new BuilderEndLocal(codeAddress, register);
}
@Nonnull public BuilderRestartLocal internRestartLocal(int codeAddress, int register) {
return new BuilderRestartLocal(codeAddress, register);
}
@Nonnull public BuilderPrologueEnd internPrologueEnd(int codeAddress) {
return new BuilderPrologueEnd(codeAddress);
}
@Nonnull public BuilderEpilogueBegin internEpilogueBegin(int codeAddress) {
return new BuilderEpilogueBegin(codeAddress);
}
@Nonnull public BuilderLineNumber internLineNumber(int codeAddress, int lineNumber) {
return new BuilderLineNumber(codeAddress, lineNumber);
}
@Override protected void writeEncodedValue(@Nonnull InternalEncodedValueWriter writer,
@Nonnull BuilderEncodedValue encodedValue) throws IOException {
switch (encodedValue.getValueType()) {

View File

@ -36,6 +36,7 @@ import com.google.common.base.Predicate;
import com.google.common.collect.*;
import org.jf.dexlib2.DebugItemType;
import org.jf.dexlib2.ReferenceType;
import org.jf.dexlib2.builder.MutableMethodImplementation;
import org.jf.dexlib2.iface.*;
import org.jf.dexlib2.iface.debug.*;
import org.jf.dexlib2.iface.instruction.Instruction;
@ -59,8 +60,7 @@ import java.util.Map.Entry;
public class ClassPool implements ClassSection<CharSequence, CharSequence,
TypeListPool.Key<? extends Collection<? extends CharSequence>>, PoolClassDef, Field, PoolMethod,
Set<? extends Annotation>,
EncodedValue, DebugItem, Instruction, ExceptionHandler> {
Set<? extends Annotation>, EncodedValue> {
@Nonnull private HashMap<String, PoolClassDef> internedItems = Maps.newHashMap();
@Nonnull private final StringPool stringPool;
@ -434,6 +434,11 @@ public class ClassPool implements ClassSection<CharSequence, CharSequence,
return handler.getExceptionType();
}
@Nonnull @Override
public MutableMethodImplementation makeMutableMethodImplementation(@Nonnull PoolMethod poolMethod) {
return new MutableMethodImplementation(poolMethod.getImplementation());
}
@Override public void setEncodedArrayOffset(@Nonnull PoolClassDef classDef, int offset) {
classDef.encodedArrayOffset = offset;
}
@ -466,14 +471,6 @@ public class ClassPool implements ClassSection<CharSequence, CharSequence,
return method.codeItemOffset;
}
@Override public void setDebugItemOffset(@Nonnull PoolMethod method, int offset) {
method.debugInfoOffset = offset;
}
@Override public int getDebugItemOffset(@Nonnull PoolMethod method) {
return method.debugInfoOffset;
}
@Override public void writeDebugItem(@Nonnull DebugWriter<CharSequence, CharSequence> writer,
DebugItem debugItem) throws IOException {
switch (debugItem.getDebugItemType()) {

View File

@ -32,17 +32,20 @@
package org.jf.dexlib2.writer.pool;
import org.jf.dexlib2.ValueType;
import org.jf.dexlib2.iface.*;
import org.jf.dexlib2.iface.debug.DebugItem;
import org.jf.dexlib2.iface.instruction.Instruction;
import org.jf.dexlib2.iface.Annotation;
import org.jf.dexlib2.iface.AnnotationElement;
import org.jf.dexlib2.iface.ClassDef;
import org.jf.dexlib2.iface.Field;
import org.jf.dexlib2.iface.reference.*;
import org.jf.dexlib2.iface.value.*;
import org.jf.dexlib2.immutable.instruction.ImmutableInstructionFactory;
import org.jf.dexlib2.writer.DexWriter;
import org.jf.dexlib2.writer.io.FileDataStore;
import org.jf.dexlib2.writer.pool.ProtoPool.Key;
import org.jf.util.ExceptionWithContext;
import javax.annotation.Nonnull;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Set;
@ -51,7 +54,7 @@ public class DexPool extends DexWriter<CharSequence, StringReference, CharSequen
FieldReference, MethodReference, Reference, PoolClassDef,
Annotation, Set<? extends Annotation>,
TypeListPool.Key<? extends Collection<? extends CharSequence>>, Field, PoolMethod,
EncodedValue, AnnotationElement, DebugItem, Instruction, ExceptionHandler> {
EncodedValue, AnnotationElement> {
public static DexPool makeDexPool() {
return makeDexPool(15);
@ -85,7 +88,7 @@ public class DexPool extends DexWriter<CharSequence, StringReference, CharSequen
for (ClassDef classDef: input.getClasses()) {
((ClassPool)dexPool.classSection).intern(classDef);
}
dexPool.writeTo(path);
dexPool.writeTo(new FileDataStore(new File(path)));
}
@Override protected void writeEncodedValue(@Nonnull InternalEncodedValueWriter writer,

View File

@ -48,7 +48,6 @@ class PoolMethod extends BaseMethodReference implements Method {
@Nonnull private final Method method;
protected int annotationSetRefListOffset = DexPool.NO_OFFSET;
protected int codeItemOffset = DexPool.NO_OFFSET;
protected int debugInfoOffset = DexPool.NO_OFFSET;
public static final Function<Method, PoolMethod> TRANSFORM = new Function<Method, PoolMethod>() {
@Override public PoolMethod apply(Method method) {

View File

@ -33,6 +33,7 @@ package org.jf.dexlib2.writer.pool;
import org.jf.dexlib2.iface.reference.StringReference;
import org.jf.dexlib2.writer.StringSection;
import org.jf.util.ExceptionWithContext;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -49,6 +50,14 @@ public class StringPool extends StringTypeBasePool implements StringSection<Char
}
@Override public int getItemIndex(@Nonnull StringReference key) {
return getItemIndex(key.getString());
Integer index = internedItems.get(key.toString());
if (index == null) {
throw new ExceptionWithContext("Item not found.: %s", key.toString());
}
return index;
}
@Override public boolean hasJumboIndexes() {
return internedItems.size() > 65536;
}
}

View File

@ -1,401 +0,0 @@
/*
* 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.writer.util;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.jf.dexlib2.Format;
import org.jf.dexlib2.Opcode;
import org.jf.dexlib2.ReferenceType;
import org.jf.dexlib2.iface.instruction.Instruction;
import org.jf.dexlib2.iface.instruction.ReferenceInstruction;
import org.jf.dexlib2.iface.instruction.SwitchElement;
import org.jf.dexlib2.iface.instruction.SwitchPayload;
import org.jf.dexlib2.iface.instruction.formats.*;
import org.jf.dexlib2.iface.reference.*;
import org.jf.dexlib2.immutable.instruction.*;
import org.jf.dexlib2.util.InstructionUtil;
import org.jf.dexlib2.util.MethodUtil;
import org.jf.dexlib2.writer.InstructionFactory;
import org.jf.util.ExceptionWithContext;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class InstructionWriteUtil<Insn extends Instruction, StringRef extends StringReference,
BaseReference extends Reference> {
private final StringIndexProvider<StringRef> stringIndexProvider;
private final InstructionFactory<? extends Insn, BaseReference> instructionFactory;
private final Iterable<? extends Insn> originalInstructions;
private List<Insn> instructions;
private ArrayList<Integer> codeOffsetShifts;
private HashMap<Integer,Format> offsetToNewInstructionMap;
private int codeUnitCount;
private int outParamCount;
public static interface StringIndexProvider<StringRef extends StringReference> {
int getItemIndex(@Nonnull StringRef reference);
}
public InstructionWriteUtil(@Nonnull Iterable<? extends Insn> instructions,
@Nonnull StringIndexProvider<StringRef> stringIndexProvider,
@Nonnull InstructionFactory<? extends Insn, BaseReference> instructionFactory) {
this.stringIndexProvider = stringIndexProvider;
this.instructionFactory = instructionFactory;
this.originalInstructions = instructions;
calculateMaxOutParamCount();
findCodeOffsetShifts();
modifyInstructions();
}
private void calculateMaxOutParamCount() {
for (Insn instruction: originalInstructions) {
codeUnitCount += instruction.getCodeUnits();
if (instruction.getOpcode().referenceType == ReferenceType.METHOD) {
ReferenceInstruction refInsn = (ReferenceInstruction)instruction;
MethodReference methodRef = (MethodReference)refInsn.getReference();
int paramCount = MethodUtil.getParameterRegisterCount(methodRef, InstructionUtil.isInvokeStatic(instruction.getOpcode()));
if (paramCount > outParamCount) {
outParamCount = paramCount;
}
}
}
}
public Iterable<? extends Insn> getInstructions() {
if (instructions != null) {
return instructions;
} else {
return originalInstructions;
}
}
public int getCodeUnitCount() {
return codeUnitCount;
}
public int getOutParamCount() {
return outParamCount;
}
private int targetOffsetShift(int instrOffset, int targetOffset) {
int targetOffsetShift = 0;
if (codeOffsetShifts != null) {
int instrShift = codeOffsetShift(instrOffset);
int targetShift = codeOffsetShift(instrOffset+targetOffset);
targetOffsetShift = targetShift - instrShift;
}
return targetOffsetShift;
}
public int codeOffsetShift(int offset) {
int shift = 0;
if (codeOffsetShifts != null) {
int numCodeOffsetShifts = codeOffsetShifts.size();
if (numCodeOffsetShifts > 0) {
if (offset >= codeOffsetShifts.get(numCodeOffsetShifts-1)) {
shift = numCodeOffsetShifts;
} else if (numCodeOffsetShifts>1) {
for (int i=1;i<numCodeOffsetShifts;i++) {
if (offset >= codeOffsetShifts.get(i-1) && offset < codeOffsetShifts.get(i)) {
shift = i;
break;
}
}
}
}
}
return shift;
}
/*
* This method creates a list of code offsets of instructions, whose (and subsequent instructions')
* code offset will get shifted by one code unit with respect to previous instruction(s).
* This happens when the previous instruction has to be changed to a larger sized one
* to fit the new value or payload instruction has to be prepended by nop to ensure alignment.
*/
private void findCodeOffsetShifts() {
// first, process const-string to const-string/jumbo conversions
int currentCodeOffset = 0;
codeOffsetShifts = Lists.newArrayList();
offsetToNewInstructionMap = Maps.newHashMap();
for (Instruction instruction: originalInstructions) {
if (instruction.getOpcode().equals(Opcode.CONST_STRING)) {
ReferenceInstruction refInstr = (ReferenceInstruction) instruction;
int referenceIndex = stringIndexProvider.getItemIndex((StringRef)refInstr.getReference());
if (referenceIndex > 0xFFFF) {
codeOffsetShifts.add(currentCodeOffset+instruction.getCodeUnits());
offsetToNewInstructionMap.put(currentCodeOffset, Opcode.CONST_STRING_JUMBO.format);
}
}
currentCodeOffset += instruction.getCodeUnits();
}
// next, let's check if this caused any conversions in goto instructions due to changes in offset values
// since code offset delta is equivalent to the position of instruction's code offset in the shift list,
// we use it as a position here
// we also check if we will have to insert nops to ensure 4-byte alignment for switch statements and packed arrays
boolean shiftsInserted;
do {
currentCodeOffset = 0;
shiftsInserted = false;
for (Instruction instruction: originalInstructions) {
if (instruction.getOpcode().format.equals(Format.Format10t) && !offsetToNewInstructionMap.containsKey(currentCodeOffset)) {
int targetOffset = ((Instruction10t)instruction).getCodeOffset();
int codeOffsetDelta = codeOffsetShift(currentCodeOffset);
int newTargetOffset = targetOffset + targetOffsetShift(currentCodeOffset, targetOffset);
if ((byte)newTargetOffset != newTargetOffset) {
if ((short)newTargetOffset != newTargetOffset) {
// handling very small (negligible) possibility of goto becoming goto/32
// we insert extra 1 code unit shift referring to the same position
// this will cause subsequent code offsets to be shifted by 2 code units
codeOffsetShifts.add(codeOffsetDelta, currentCodeOffset+instruction.getCodeUnits());
offsetToNewInstructionMap.put(currentCodeOffset, Format.Format30t);
} else {
offsetToNewInstructionMap.put(currentCodeOffset, Format.Format20t);
}
codeOffsetShifts.add(codeOffsetDelta, currentCodeOffset+instruction.getCodeUnits());
shiftsInserted = true;
}
} else if (instruction.getOpcode().format.equals(Format.Format20t) && !offsetToNewInstructionMap.containsKey(currentCodeOffset)) {
int targetOffset = ((Instruction20t)instruction).getCodeOffset();
int codeOffsetDelta = codeOffsetShift(currentCodeOffset);
int newTargetOffset = targetOffset + targetOffsetShift(currentCodeOffset, targetOffset);
if ((short)newTargetOffset != newTargetOffset) {
codeOffsetShifts.add(codeOffsetDelta, currentCodeOffset+instruction.getCodeUnits());
offsetToNewInstructionMap.put(currentCodeOffset, Format.Format30t);
shiftsInserted = true;
}
} else if (instruction.getOpcode().format.equals(Format.ArrayPayload)
|| instruction.getOpcode().format.equals(Format.SparseSwitchPayload)
|| instruction.getOpcode().format.equals(Format.PackedSwitchPayload)) {
int codeOffsetDelta = codeOffsetShift(currentCodeOffset);
if ((currentCodeOffset+codeOffsetDelta)%2 != 0) {
if (codeOffsetShifts.contains(currentCodeOffset)) {
codeOffsetShifts.remove(codeOffsetDelta-1);
offsetToNewInstructionMap.remove(currentCodeOffset);
} else {
codeOffsetShifts.add(codeOffsetDelta, currentCodeOffset);
offsetToNewInstructionMap.put(currentCodeOffset, Format.Format10x);
shiftsInserted = true;
}
}
}
currentCodeOffset += instruction.getCodeUnits();
}
} while (shiftsInserted);
codeUnitCount += codeOffsetShifts.size();
}
private void modifyInstructions() {
if (codeOffsetShifts == null) {
return;
}
instructions = Lists.newArrayList();
int currentCodeOffset = 0;
for (Insn instruction: originalInstructions) {
Insn modifiedInstruction = null;
switch (instruction.getOpcode().format) {
case Format10t: {
Instruction10t instr = (Instruction10t)instruction;
int targetOffset = instr.getCodeOffset();
int newTargetOffset = targetOffset + targetOffsetShift(currentCodeOffset, targetOffset);
Format newInstructionFormat = offsetToNewInstructionMap.get(currentCodeOffset);
if (newInstructionFormat != null) {
if (newInstructionFormat.equals(Format.Format30t)) {
modifiedInstruction = instructionFactory.makeInstruction30t(Opcode.GOTO_32, newTargetOffset);
} else if (newInstructionFormat.equals(Format.Format20t)) {
modifiedInstruction = instructionFactory.makeInstruction20t(Opcode.GOTO_16, newTargetOffset);
}
} else if (newTargetOffset != targetOffset) {
modifiedInstruction = instructionFactory.makeInstruction10t(instr.getOpcode(), newTargetOffset);
}
break;
}
case Format20t: {
Instruction20t instr = (Instruction20t)instruction;
int targetOffset = instr.getCodeOffset();
int newTargetOffset = targetOffset + targetOffsetShift(currentCodeOffset, targetOffset);
Format newInstructionFormat = offsetToNewInstructionMap.get(currentCodeOffset);
if (newInstructionFormat != null && newInstructionFormat.equals(Format.Format30t)) {
modifiedInstruction = instructionFactory.makeInstruction30t(Opcode.GOTO_32, newTargetOffset);
} else if (newTargetOffset != targetOffset) {
modifiedInstruction = instructionFactory.makeInstruction20t(Opcode.GOTO_16, newTargetOffset);
}
break;
}
case Format21c: {
Instruction21c instr = (Instruction21c)instruction;
if (instr.getOpcode().equals(Opcode.CONST_STRING)) {
int referenceIndex = stringIndexProvider.getItemIndex((StringRef)instr.getReference());
if (referenceIndex > 0xFFFF) {
modifiedInstruction = instructionFactory.makeInstruction31c(Opcode.CONST_STRING_JUMBO,
instr.getRegisterA(), (BaseReference)instr.getReference());
}
}
break;
}
case Format21t: {
Instruction21t instr = (Instruction21t)instruction;
int targetOffset = instr.getCodeOffset();
int newTargetOffset = targetOffset + targetOffsetShift(currentCodeOffset, targetOffset);
if (newTargetOffset != targetOffset) {
modifiedInstruction = instructionFactory.makeInstruction21t(instr.getOpcode(),
instr.getRegisterA(), newTargetOffset);
}
break;
}
case Format22t: {
Instruction22t instr = (Instruction22t)instruction;
int targetOffset = instr.getCodeOffset();
int newTargetOffset = targetOffset + targetOffsetShift(currentCodeOffset, targetOffset);
if (newTargetOffset != targetOffset) {
modifiedInstruction = instructionFactory.makeInstruction22t(instr.getOpcode(),
instr.getRegisterA(), instr.getRegisterB(), newTargetOffset);
}
break;
}
case Format30t: {
Instruction30t instr = (Instruction30t)instruction;
int targetOffset = instr.getCodeOffset();
int newTargetOffset = targetOffset + targetOffsetShift(currentCodeOffset, targetOffset);
if (newTargetOffset != targetOffset) {
modifiedInstruction = instructionFactory.makeInstruction30t(instr.getOpcode(), newTargetOffset);
}
break;
}
case Format31t: {
Instruction31t instr = (Instruction31t)instruction;
int targetOffset = instr.getCodeOffset();
int newTargetOffset = targetOffset + targetOffsetShift(currentCodeOffset, targetOffset);
if (newTargetOffset != targetOffset) {
modifiedInstruction = instructionFactory.makeInstruction31t(instr.getOpcode(),
instr.getRegisterA(), newTargetOffset);
}
break;
}
case SparseSwitchPayload: {
alignPayload(currentCodeOffset);
int switchInstructionOffset = findSwitchInstructionOffset(currentCodeOffset);
SwitchPayload payload = (SwitchPayload)instruction;
if (isSwitchTargetOffsetChanged(payload, switchInstructionOffset)) {
List<SwitchElement> newSwitchElements = modifySwitchElements(payload, switchInstructionOffset);
modifiedInstruction = instructionFactory.makeSparseSwitchPayload(newSwitchElements);
}
break;
}
case PackedSwitchPayload: {
alignPayload(currentCodeOffset);
int switchInstructionOffset = findSwitchInstructionOffset(currentCodeOffset);
SwitchPayload payload = (SwitchPayload)instruction;
if (isSwitchTargetOffsetChanged(payload, switchInstructionOffset)) {
List<SwitchElement> newSwitchElements = modifySwitchElements(payload, switchInstructionOffset);
modifiedInstruction = instructionFactory.makePackedSwitchPayload(newSwitchElements);
}
break;
}
case ArrayPayload: {
alignPayload(currentCodeOffset);
break;
}
}
if (modifiedInstruction != null) {
instructions.add(modifiedInstruction);
} else {
instructions.add(instruction);
}
currentCodeOffset += instruction.getCodeUnits();
}
}
private void alignPayload(int codeOffset) {
Format newInstructionFormat = offsetToNewInstructionMap.get(codeOffset);
if (newInstructionFormat != null && newInstructionFormat.equals(Format.Format10x)) {
instructions.add(instructionFactory.makeInstruction10x(Opcode.NOP));
}
}
private int findSwitchInstructionOffset(int payloadOffset) {
int currentCodeOffset = 0;
int switchInstructionOffset = -1;
for (Instruction instruction: originalInstructions) {
if (instruction.getOpcode().equals(Opcode.PACKED_SWITCH)
|| instruction.getOpcode().equals(Opcode.SPARSE_SWITCH)) {
int targetOffset = currentCodeOffset + ((Instruction31t)instruction).getCodeOffset();
if (targetOffset == payloadOffset) {
if (switchInstructionOffset < 0) {
switchInstructionOffset = currentCodeOffset;
} else {
throw new ExceptionWithContext("Multiple switch instructions refer to the same switch payload!");
}
}
}
currentCodeOffset += instruction.getCodeUnits();
}
return switchInstructionOffset;
}
private boolean isSwitchTargetOffsetChanged(SwitchPayload payload, int switchInstructionOffset) {
for (SwitchElement switchElement: payload.getSwitchElements()) {
if (targetOffsetShift(switchInstructionOffset, switchElement.getOffset()) != 0) {
return true;
}
}
return false;
}
private ArrayList<SwitchElement> modifySwitchElements(SwitchPayload payload, int switchInstructionOffset) {
ArrayList<SwitchElement> switchElements = Lists.newArrayList();
for (SwitchElement switchElement: payload.getSwitchElements()) {
int targetOffset = switchElement.getOffset();
int newTargetOffset = targetOffset + targetOffsetShift(switchInstructionOffset, targetOffset);
if (newTargetOffset != targetOffset) {
ImmutableSwitchElement immuSwitchElement = new ImmutableSwitchElement(switchElement.getKey(), newTargetOffset);
switchElements.add(immuSwitchElement);
} else {
switchElements.add(switchElement);
}
}
return switchElements;
}
}

View File

@ -31,351 +31,185 @@
package org.jf.dexlib2.writer;
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 junit.framework.Assert;
import org.jf.dexlib2.Opcode;
import org.jf.dexlib2.iface.MethodImplementation;
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.SwitchElement;
import org.jf.dexlib2.iface.instruction.formats.*;
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.ImmutableMethodImplementation;
import org.jf.dexlib2.immutable.instruction.*;
import org.jf.dexlib2.immutable.reference.ImmutableStringReference;
import org.jf.dexlib2.writer.util.InstructionWriteUtil;
import org.junit.Before;
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;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.io.IOException;
import java.util.List;
public class JumboStringConversionTest {
private static final int MIN_NUM_JUMBO_STRINGS = 2;
@Test
public void testJumboStringConversion() throws IOException {
DexBuilder dexBuilder = DexBuilder.makeDexBuilder(15);
private MockStringIndexProvider mockStringIndexProvider;
ArrayList<String> mJumboStrings;
private class InsnWriteUtil extends InstructionWriteUtil<Instruction, StringReference, Reference> {
public InsnWriteUtil(@Nonnull MethodImplementation implementation) {
super(implementation.getInstructions(), mockStringIndexProvider, ImmutableInstructionFactory.INSTANCE);
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());
}
@Test
public void testJumboStringConversion_NonMethodBuilder() throws IOException {
DexBuilder dexBuilder = DexBuilder.makeDexBuilder(15);
final List<Instruction> instructions = Lists.newArrayList();
for (int i=0; i<66000; i++) {
final StringReference ref = dexBuilder.internStringReference(String.format("%08d", i));
instructions.add(new Instruction21c() {
@Override public int getRegisterA() {
return 0;
}
}
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))));
ImmutableMethodImplementation methodImplementation =
new ImmutableMethodImplementation(1, instructions, null, null);
InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation);
for (Instruction instr: writeUtil.getInstructions()) {
Assert.assertEquals("Jumbo string conversion was not performed!",
instr.getOpcode(), Opcode.CONST_STRING_JUMBO);
}
}
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));
ArrayList<SwitchElement> switchElements = Lists.newArrayList();
switchElements.add(new ImmutableSwitchElement(0, 5));
instructions.add(new ImmutablePackedSwitchPayload(switchElements));
instructions.add(new ImmutableSparseSwitchPayload(switchElements));
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;
}
}
}
@Test
public void testInstruction20tSimple() {
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
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);
@Nonnull @Override public Reference getReference() {
return ref;
}
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);
@Override public Opcode getOpcode() {
return Opcode.CONST_STRING;
}
break;
@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;
}
}
}
@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;
@Nonnull @Override public Iterable<? extends Instruction> getInstructions() {
return instructions;
}
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;
@Nonnull @Override public List<? extends TryBlock<? extends ExceptionHandler>> getTryBlocks() {
return ImmutableList.of();
}
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;
@Nonnull @Override public Iterable<? extends DebugItem> getDebugItems() {
return ImmutableList.of();
}
codeOffset += instr.getCodeUnits();
};
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)));
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());
}
}
@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();
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());
}
}

View File

@ -1,55 +0,0 @@
/*
* Copyright 2012, 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.writer;
import com.google.common.collect.Maps;
import org.jf.dexlib2.iface.reference.StringReference;
import org.jf.dexlib2.writer.util.InstructionWriteUtil.StringIndexProvider;
import javax.annotation.Nonnull;
import java.util.HashMap;
public class MockStringIndexProvider implements StringIndexProvider<StringReference> {
private HashMap<String, Integer> internedItems = Maps.newHashMap();
public void intern(@Nonnull CharSequence string, int index) {
internedItems.put(string.toString(), index);
}
@Override public int getItemIndex(@Nonnull StringReference reference) {
return internedItems.get(reference.getString());
}
public int getNumItems() {
return internedItems.size();
}
}

View File

@ -1,131 +0,0 @@
/*
* Copyright 2012, 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.writer;
import com.google.common.collect.Lists;
import junit.framework.Assert;
import org.jf.dexlib2.Opcode;
import org.jf.dexlib2.iface.MethodImplementation;
import org.jf.dexlib2.iface.instruction.Instruction;
import org.jf.dexlib2.iface.instruction.SwitchElement;
import org.jf.dexlib2.iface.reference.Reference;
import org.jf.dexlib2.iface.reference.StringReference;
import org.jf.dexlib2.immutable.ImmutableMethodImplementation;
import org.jf.dexlib2.immutable.instruction.*;
import org.jf.dexlib2.writer.util.InstructionWriteUtil;
import org.junit.Before;
import org.junit.Test;
import javax.annotation.Nonnull;
import java.util.ArrayList;
public class PayloadAlignmentTest {
private MockStringIndexProvider mockStringIndexProvider;
private class InsnWriteUtil extends InstructionWriteUtil<Instruction, StringReference, Reference> {
public InsnWriteUtil(@Nonnull MethodImplementation implementation) {
super(implementation.getInstructions(), mockStringIndexProvider, ImmutableInstructionFactory.INSTANCE);
}
}
@Before
public void setup() {
mockStringIndexProvider = new MockStringIndexProvider();
}
@Test
public void testArrayPayloadAlignment() {
ArrayList<ImmutableInstruction> instructions = Lists.newArrayList();
// 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 (instr.getOpcode().equals(Opcode.ARRAY_PAYLOAD)) {
Assert.assertEquals("array payload was not aligned properly", codeOffset%2, 0);
break;
}
codeOffset += instr.getCodeUnits();
}
}
@Test
public void testPackedSwitchAlignment() {
ArrayList<ImmutableInstruction> instructions = Lists.newArrayList();
// add misaligned packed switch payload
ArrayList<SwitchElement> switchElements = Lists.newArrayList();
switchElements.add(new ImmutableSwitchElement(0, 5));
instructions.add(new ImmutableInstruction10x(Opcode.NOP));
instructions.add(new ImmutablePackedSwitchPayload(switchElements));
ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null);
InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation);
int codeOffset = 0;
for (Instruction instr: writeUtil.getInstructions()) {
if (instr.getOpcode().equals(Opcode.PACKED_SWITCH_PAYLOAD)) {
Assert.assertEquals("packed switch payload was not aligned properly", codeOffset%2, 0);
break;
}
codeOffset += instr.getCodeUnits();
}
}
@Test
public void testSparseSwitchAlignment() {
ArrayList<ImmutableInstruction> instructions = Lists.newArrayList();
// add misaligned sparse switch payload
ArrayList<SwitchElement> switchElements = Lists.newArrayList();
switchElements.add(new ImmutableSwitchElement(0, 5));
instructions.add(new ImmutableInstruction10x(Opcode.NOP));
instructions.add(new ImmutableSparseSwitchPayload(switchElements));
ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null);
InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation);
int codeOffset = 0;
for (Instruction instr: writeUtil.getInstructions()) {
if (instr.getOpcode().equals(Opcode.SPARSE_SWITCH_PAYLOAD)) {
Assert.assertEquals("packed switch payload was not aligned properly", codeOffset%2, 0);
break;
}
codeOffset += instr.getCodeUnits();
}
}
}