[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

@ -22,6 +22,7 @@ v2.0.0 (TBA)
-Fixed (issue #448) - Merge smali2 into Apktool
-Fixed (issue #496) - Fixes Windows builds caused by java.nio problems
-Fixed (issue #510) - Any error output is sent stderr instead of stdout
-Fixed (issue #426) - Filename too long (JesusFreke)
-Updated known bytes for configurations to 38 (from addition of layout direction)
-Fixed NPE when handling odex apks even with --no-src specified. (Thanks Rodrigo Chiossi)

View File

@ -32,10 +32,13 @@ import org.jf.baksmali.Adaptors.MethodDefinition;
import org.jf.baksmali.Adaptors.MethodItem;
import org.jf.baksmali.Adaptors.ReferenceFormatter;
import org.jf.baksmali.Renderers.LongRenderer;
import org.jf.dexlib2.ReferenceType;
import org.jf.dexlib2.VerificationError;
import org.jf.dexlib2.dexbacked.DexBackedDexFile.InvalidItemIndex;
import org.jf.dexlib2.iface.instruction.*;
import org.jf.dexlib2.iface.instruction.formats.Instruction20bc;
import org.jf.dexlib2.iface.instruction.formats.UnknownInstruction;
import org.jf.dexlib2.iface.reference.Reference;
import org.jf.util.IndentingWriter;
import javax.annotation.Nonnull;
@ -58,6 +61,21 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
@Override
public boolean writeTo(IndentingWriter writer) throws IOException {
boolean invalidReference = false;
if (instruction instanceof ReferenceInstruction) {
try {
Reference reference = ((ReferenceInstruction)instruction).getReference();
} catch (InvalidItemIndex ex) {
invalidReference = true;
writer.write("#invalid ");
writer.write(ReferenceType.toString(instruction.getOpcode().referenceType));
writer.write(" index: ");
writer.printSignedIntAsDec(ex.getInvalidIndex());
writer.write("\n#");
}
}
switch (instruction.getOpcode().format) {
case Format10t:
writeOpcode(writer);
@ -336,8 +354,14 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
}
protected void writeReference(IndentingWriter writer) throws IOException {
ReferenceFormatter.writeReference(writer, instruction.getOpcode().referenceType,
((ReferenceInstruction)instruction).getReference());
try {
ReferenceFormatter.writeReference(writer, instruction.getOpcode().referenceType,
((ReferenceInstruction)instruction).getReference());
} catch (InvalidItemIndex ex) {
writer.write(ReferenceType.toString(instruction.getOpcode().referenceType));
writer.write("@");
writer.printSignedIntAsDec(ex.getInvalidIndex());
}
}
protected void writeVerificationErrorType(IndentingWriter writer) throws IOException {

View File

@ -148,7 +148,7 @@ public class main {
options.addCodeOffsets = true;
break;
case 'r':
String[] values = commandLine.getOptionValues('r');
String[] values = commandLine.getOptionValues("r");
int registerInfo = 0;
if (values == null || values.length == 0) {

View File

@ -104,7 +104,7 @@ public class AnalysisTest {
className.substring(1, className.length() - 1));
String smaliContents = readResource(smaliPath);
Assert.assertEquals(smaliContents, stringWriter.toString());
Assert.assertEquals(smaliContents, stringWriter.toString().replace("\r\n", "\n"));
}
}

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();
}
}
}

View File

@ -140,10 +140,9 @@ tokens {
LOCALS_DIRECTIVE;
LONG_LITERAL;
METHOD_DIRECTIVE;
METHOD_NAME;
MEMBER_NAME;
NEGATIVE_INTEGER_LITERAL;
NULL_LITERAL;
OFFSET;
OPEN_BRACE;
OPEN_PAREN;
PACKED_SWITCH_DIRECTIVE;
@ -188,7 +187,6 @@ tokens {
I_METHOD_RETURN_TYPE;
I_REGISTERS;
I_LOCALS;
I_LABELS;
I_LABEL;
I_ANNOTATIONS;
I_ANNOTATION;
@ -202,26 +200,20 @@ tokens {
I_ARRAY_ELEMENTS;
I_PACKED_SWITCH_START_KEY;
I_PACKED_SWITCH_ELEMENTS;
I_PACKED_SWITCH_DECLARATION;
I_PACKED_SWITCH_DECLARATIONS;
I_SPARSE_SWITCH_ELEMENTS;
I_SPARSE_SWITCH_DECLARATION;
I_SPARSE_SWITCH_DECLARATIONS;
I_ADDRESS;
I_CATCH;
I_CATCHALL;
I_CATCHES;
I_PARAMETER;
I_PARAMETERS;
I_PARAMETER_NOT_SPECIFIED;
I_ORDERED_DEBUG_DIRECTIVES;
I_LINE;
I_LOCAL;
I_END_LOCAL;
I_RESTART_LOCAL;
I_PROLOGUE;
I_EPILOGUE;
I_STATEMENTS;
I_ORDERED_METHOD_ITEMS;
I_STATEMENT_FORMAT10t;
I_STATEMENT_FORMAT10x;
I_STATEMENT_FORMAT11n;
@ -500,56 +492,49 @@ the annotations. If it turns out that they are field annotations, we include the
add them to the $smali_file::classAnnotations list*/
field
@init {List<CommonTree> annotations = new ArrayList<CommonTree>();}
: FIELD_DIRECTIVE access_list simple_name COLON nonvoid_type_descriptor (EQUAL literal)?
: FIELD_DIRECTIVE access_list member_name COLON nonvoid_type_descriptor (EQUAL literal)?
( ({input.LA(1) == ANNOTATION_DIRECTIVE}? annotation {annotations.add($annotation.tree);})*
( END_FIELD_DIRECTIVE
-> ^(I_FIELD[$start, "I_FIELD"] simple_name access_list ^(I_FIELD_TYPE nonvoid_type_descriptor) ^(I_FIELD_INITIAL_VALUE literal)? ^(I_ANNOTATIONS annotation*))
-> ^(I_FIELD[$start, "I_FIELD"] member_name access_list ^(I_FIELD_TYPE nonvoid_type_descriptor) ^(I_FIELD_INITIAL_VALUE literal)? ^(I_ANNOTATIONS annotation*))
| /*epsilon*/ {$smali_file::classAnnotations.addAll(annotations);}
-> ^(I_FIELD[$start, "I_FIELD"] simple_name access_list ^(I_FIELD_TYPE nonvoid_type_descriptor) ^(I_FIELD_INITIAL_VALUE literal)? ^(I_ANNOTATIONS))
-> ^(I_FIELD[$start, "I_FIELD"] member_name access_list ^(I_FIELD_TYPE nonvoid_type_descriptor) ^(I_FIELD_INITIAL_VALUE literal)? ^(I_ANNOTATIONS))
)
);
method
scope {int currentAddress;}
: {$method::currentAddress = 0;}
METHOD_DIRECTIVE access_list method_name method_prototype statements_and_directives
: METHOD_DIRECTIVE access_list member_name method_prototype statements_and_directives
END_METHOD_DIRECTIVE
-> ^(I_METHOD[$start, "I_METHOD"] method_name method_prototype access_list statements_and_directives);
-> ^(I_METHOD[$start, "I_METHOD"] member_name method_prototype access_list statements_and_directives);
statements_and_directives
scope
{
boolean hasRegistersDirective;
List<CommonTree> packedSwitchDeclarations;
List<CommonTree> sparseSwitchDeclarations;
List<CommonTree> methodAnnotations;
}
: {
$method::currentAddress = 0;
$statements_and_directives::hasRegistersDirective = false;
$statements_and_directives::packedSwitchDeclarations = new ArrayList<CommonTree>();
$statements_and_directives::sparseSwitchDeclarations = new ArrayList<CommonTree>();
$statements_and_directives::methodAnnotations = new ArrayList<CommonTree>();
}
( instruction {$method::currentAddress += $instruction.size/2;}
( ordered_method_item
| registers_directive
| label
| catch_directive
| catchall_directive
| parameter_directive
| ordered_debug_directive
| annotation {$statements_and_directives::methodAnnotations.add($annotation.tree);}
)*
-> registers_directive?
^(I_LABELS label*)
{buildTree(I_PACKED_SWITCH_DECLARATIONS, "I_PACKED_SWITCH_DECLARATIONS", $statements_and_directives::packedSwitchDeclarations)}
{buildTree(I_SPARSE_SWITCH_DECLARATIONS, "I_SPARSE_SWITCH_DECLARATIONS", $statements_and_directives::sparseSwitchDeclarations)}
^(I_STATEMENTS instruction*)
^(I_ORDERED_METHOD_ITEMS ordered_method_item*)
^(I_CATCHES catch_directive* catchall_directive*)
^(I_PARAMETERS parameter_directive*)
^(I_ORDERED_DEBUG_DIRECTIVES ordered_debug_directive*)
{buildTree(I_ANNOTATIONS, "I_ANNOTATIONS", $statements_and_directives::methodAnnotations)};
/* Method items whose order/location is important */
ordered_method_item
: label
| instruction
| debug_directive;
registers_directive
: (
directive=REGISTERS_DIRECTIVE regCount=integral_literal -> ^(I_REGISTERS[$REGISTERS_DIRECTIVE, "I_REGISTERS"] $regCount)
@ -605,9 +590,9 @@ simple_name
| INSTRUCTION_FORMAT35ms_METHOD -> SIMPLE_NAME[$INSTRUCTION_FORMAT35ms_METHOD]
| INSTRUCTION_FORMAT51l -> SIMPLE_NAME[$INSTRUCTION_FORMAT51l];
method_name
member_name
: simple_name
| METHOD_NAME -> SIMPLE_NAME[$METHOD_NAME];
| MEMBER_NAME -> SIMPLE_NAME[$MEMBER_NAME];
method_prototype
: OPEN_PAREN param_list CLOSE_PAREN type_descriptor
@ -680,15 +665,15 @@ fixed_32bit_literal
| CHAR_LITERAL
| BOOL_LITERAL;
fixed_literal returns[int size]
: integer_literal {$size = 4;}
| LONG_LITERAL {$size = 8;}
| SHORT_LITERAL {$size = 2;}
| BYTE_LITERAL {$size = 1;}
| float_literal {$size = 4;}
| double_literal {$size = 8;}
| CHAR_LITERAL {$size = 2;}
| BOOL_LITERAL {$size = 1;};
fixed_literal
: integer_literal
| LONG_LITERAL
| SHORT_LITERAL
| BYTE_LITERAL
| float_literal
| double_literal
| CHAR_LITERAL
| BOOL_LITERAL;
array_literal
: OPEN_BRACE (literal (COMMA literal)* | ) CLOSE_BRACE
@ -714,8 +699,8 @@ enum_literal
type_field_method_literal
: reference_type_descriptor
( ARROW
( simple_name COLON nonvoid_type_descriptor -> ^(I_ENCODED_FIELD reference_type_descriptor simple_name nonvoid_type_descriptor)
| method_name method_prototype -> ^(I_ENCODED_METHOD reference_type_descriptor method_name method_prototype)
( member_name COLON nonvoid_type_descriptor -> ^(I_ENCODED_FIELD reference_type_descriptor member_name nonvoid_type_descriptor)
| member_name method_prototype -> ^(I_ENCODED_METHOD reference_type_descriptor member_name method_prototype)
)
| -> reference_type_descriptor
)
@ -723,20 +708,18 @@ type_field_method_literal
| VOID_TYPE;
fully_qualified_method
: reference_type_descriptor ARROW method_name method_prototype
-> reference_type_descriptor method_name method_prototype;
: reference_type_descriptor ARROW member_name method_prototype
-> reference_type_descriptor member_name method_prototype;
fully_qualified_field
: reference_type_descriptor ARROW simple_name COLON nonvoid_type_descriptor
-> reference_type_descriptor simple_name nonvoid_type_descriptor;
: reference_type_descriptor ARROW member_name COLON nonvoid_type_descriptor
-> reference_type_descriptor member_name nonvoid_type_descriptor;
label
: COLON simple_name -> ^(I_LABEL[$COLON, "I_LABEL"] simple_name I_ADDRESS[$start, Integer.toString($method::currentAddress)]);
: COLON simple_name -> ^(I_LABEL[$COLON, "I_LABEL"] simple_name);
label_ref_or_offset
: COLON simple_name -> simple_name
| OFFSET
| NEGATIVE_INTEGER_LITERAL -> OFFSET[$NEGATIVE_INTEGER_LITERAL];
label_ref
: COLON simple_name -> simple_name;
register_list
: REGISTER (COMMA REGISTER)* -> ^(I_REGISTER_LIST[$start, "I_REGISTER_LIST"] REGISTER*)
@ -749,12 +732,12 @@ verification_error_reference
: CLASS_DESCRIPTOR | fully_qualified_field | fully_qualified_method;
catch_directive
: CATCH_DIRECTIVE nonvoid_type_descriptor OPEN_BRACE from=label_ref_or_offset DOTDOT to=label_ref_or_offset CLOSE_BRACE using=label_ref_or_offset
-> ^(I_CATCH[$start, "I_CATCH"] I_ADDRESS[$start, Integer.toString($method::currentAddress)] nonvoid_type_descriptor $from $to $using);
: CATCH_DIRECTIVE nonvoid_type_descriptor OPEN_BRACE from=label_ref DOTDOT to=label_ref CLOSE_BRACE using=label_ref
-> ^(I_CATCH[$start, "I_CATCH"] nonvoid_type_descriptor $from $to $using);
catchall_directive
: CATCHALL_DIRECTIVE OPEN_BRACE from=label_ref_or_offset DOTDOT to=label_ref_or_offset CLOSE_BRACE using=label_ref_or_offset
-> ^(I_CATCHALL[$start, "I_CATCHALL"] I_ADDRESS[$start, Integer.toString($method::currentAddress)] $from $to $using);
: CATCHALL_DIRECTIVE OPEN_BRACE from=label_ref DOTDOT to=label_ref CLOSE_BRACE using=label_ref
-> ^(I_CATCHALL[$start, "I_CATCHALL"] $from $to $using);
/*When there are annotations immediately after a parameter definition, we don't know whether they are parameter annotations
or method annotations until we determine if there is an .end parameter directive. In either case, we still "consume" and parse
@ -771,7 +754,7 @@ parameter_directive
-> ^(I_PARAMETER[$start, "I_PARAMETER"] REGISTER STRING_LITERAL? ^(I_ANNOTATIONS))
);
ordered_debug_directive
debug_directive
: line_directive
| local_directive
| end_local_directive
@ -782,33 +765,32 @@ ordered_debug_directive
line_directive
: LINE_DIRECTIVE integral_literal
-> ^(I_LINE[$start, "I_LINE"] integral_literal I_ADDRESS[$start, Integer.toString($method::currentAddress)]);
-> ^(I_LINE[$start, "I_LINE"] integral_literal);
local_directive
: LOCAL_DIRECTIVE REGISTER (COMMA (NULL_LITERAL | name=STRING_LITERAL) COLON (VOID_TYPE | nonvoid_type_descriptor)
(COMMA signature=STRING_LITERAL)? )?
-> ^(I_LOCAL[$start, "I_LOCAL"] REGISTER NULL_LITERAL? $name? nonvoid_type_descriptor? $signature?
I_ADDRESS[$start, Integer.toString($method::currentAddress)]);
-> ^(I_LOCAL[$start, "I_LOCAL"] REGISTER NULL_LITERAL? $name? nonvoid_type_descriptor? $signature?);
end_local_directive
: END_LOCAL_DIRECTIVE REGISTER
-> ^(I_END_LOCAL[$start, "I_END_LOCAL"] REGISTER I_ADDRESS[$start, Integer.toString($method::currentAddress)]);
-> ^(I_END_LOCAL[$start, "I_END_LOCAL"] REGISTER);
restart_local_directive
: RESTART_LOCAL_DIRECTIVE REGISTER
-> ^(I_RESTART_LOCAL[$start, "I_RESTART_LOCAL"] REGISTER I_ADDRESS[$start, Integer.toString($method::currentAddress)]);
-> ^(I_RESTART_LOCAL[$start, "I_RESTART_LOCAL"] REGISTER);
prologue_directive
: PROLOGUE_DIRECTIVE
-> ^(I_PROLOGUE[$start, "I_PROLOGUE"] I_ADDRESS[$start, Integer.toString($method::currentAddress)]);
-> ^(I_PROLOGUE[$start, "I_PROLOGUE"]);
epilogue_directive
: EPILOGUE_DIRECTIVE
-> ^(I_EPILOGUE[$start, "I_EPILOGUE"] I_ADDRESS[$start, Integer.toString($method::currentAddress)]);
-> ^(I_EPILOGUE[$start, "I_EPILOGUE"]);
source_directive
: SOURCE_DIRECTIVE STRING_LITERAL?
-> ^(I_SOURCE[$start, "I_SOURCE"] STRING_LITERAL? I_ADDRESS[$start, Integer.toString($method::currentAddress)]);
-> ^(I_SOURCE[$start, "I_SOURCE"] STRING_LITERAL?);
instruction_format12x
: INSTRUCTION_FORMAT12x
@ -824,88 +806,88 @@ instruction_format31i
instruction returns [int size]
: insn_format10t { $size = $insn_format10t.size; }
| insn_format10x { $size = $insn_format10x.size; }
| insn_format10x_odex { $size = $insn_format10x_odex.size; }
| insn_format11n { $size = $insn_format11n.size; }
| insn_format11x { $size = $insn_format11x.size; }
| insn_format12x { $size = $insn_format12x.size; }
| insn_format20bc { $size = $insn_format20bc.size; }
| insn_format20t { $size = $insn_format20t.size; }
| insn_format21c_field { $size = $insn_format21c_field.size; }
| insn_format21c_field_odex { $size = $insn_format21c_field_odex.size; }
| insn_format21c_string { $size = $insn_format21c_string.size; }
| insn_format21c_type { $size = $insn_format21c_type.size; }
| insn_format21ih { $size = $insn_format21ih.size; }
| insn_format21lh { $size = $insn_format21lh.size; }
| insn_format21s { $size = $insn_format21s.size; }
| insn_format21t { $size = $insn_format21t.size; }
| insn_format22b { $size = $insn_format22b.size; }
| insn_format22c_field { $size = $insn_format22c_field.size; }
| insn_format22c_field_odex { $size = $insn_format22c_field_odex.size; }
| insn_format22c_type { $size = $insn_format22c_type.size; }
| insn_format22cs_field { $size = $insn_format22cs_field.size; }
| insn_format22s { $size = $insn_format22s.size; }
| insn_format22t { $size = $insn_format22t.size; }
| insn_format22x { $size = $insn_format22x.size; }
| insn_format23x { $size = $insn_format23x.size; }
| insn_format30t { $size = $insn_format30t.size; }
| insn_format31c { $size = $insn_format31c.size; }
| insn_format31i { $size = $insn_format31i.size; }
| insn_format31t { $size = $insn_format31t.size; }
| insn_format32x { $size = $insn_format32x.size; }
| insn_format35c_method { $size = $insn_format35c_method.size; }
| insn_format35c_type { $size = $insn_format35c_type.size; }
| insn_format35c_method_odex { $size = $insn_format35c_method_odex.size; }
| insn_format35mi_method { $size = $insn_format35mi_method.size; }
| insn_format35ms_method { $size = $insn_format35ms_method.size; }
| insn_format3rc_method { $size = $insn_format3rc_method.size; }
| insn_format3rc_method_odex { $size = $insn_format3rc_method_odex.size; }
| insn_format3rc_type { $size = $insn_format3rc_type.size; }
| insn_format3rmi_method { $size = $insn_format3rmi_method.size; }
| insn_format3rms_method { $size = $insn_format3rms_method.size; }
| insn_format51l { $size = $insn_format51l.size; }
| insn_array_data_directive { $size = $insn_array_data_directive.size; }
| insn_packed_switch_directive { $size = $insn_packed_switch_directive.size; }
| insn_sparse_switch_directive { $size = $insn_sparse_switch_directive.size; };
instruction
: insn_format10t
| insn_format10x
| insn_format10x_odex
| insn_format11n
| insn_format11x
| insn_format12x
| insn_format20bc
| insn_format20t
| insn_format21c_field
| insn_format21c_field_odex
| insn_format21c_string
| insn_format21c_type
| insn_format21ih
| insn_format21lh
| insn_format21s
| insn_format21t
| insn_format22b
| insn_format22c_field
| insn_format22c_field_odex
| insn_format22c_type
| insn_format22cs_field
| insn_format22s
| insn_format22t
| insn_format22x
| insn_format23x
| insn_format30t
| insn_format31c
| insn_format31i
| insn_format31t
| insn_format32x
| insn_format35c_method
| insn_format35c_type
| insn_format35c_method_odex
| insn_format35mi_method
| insn_format35ms_method
| insn_format3rc_method
| insn_format3rc_method_odex
| insn_format3rc_type
| insn_format3rmi_method
| insn_format3rms_method
| insn_format51l
| insn_array_data_directive
| insn_packed_switch_directive
| insn_sparse_switch_directive;
insn_format10t returns [int size]
insn_format10t
: //e.g. goto endloop:
//e.g. goto +3
INSTRUCTION_FORMAT10t label_ref_or_offset {$size = Format.Format10t.size;}
-> ^(I_STATEMENT_FORMAT10t[$start, "I_STATEMENT_FORMAT10t"] INSTRUCTION_FORMAT10t label_ref_or_offset);
INSTRUCTION_FORMAT10t label_ref
-> ^(I_STATEMENT_FORMAT10t[$start, "I_STATEMENT_FORMAT10t"] INSTRUCTION_FORMAT10t label_ref);
insn_format10x returns [int size]
insn_format10x
: //e.g. return-void
INSTRUCTION_FORMAT10x {$size = Format.Format10x.size;}
INSTRUCTION_FORMAT10x
-> ^(I_STATEMENT_FORMAT10x[$start, "I_STATEMENT_FORMAT10x"] INSTRUCTION_FORMAT10x);
insn_format10x_odex returns [int size]
insn_format10x_odex
: //e.g. return-void-barrier
INSTRUCTION_FORMAT10x_ODEX {$size = Format.Format10x.size;}
INSTRUCTION_FORMAT10x_ODEX
{
throwOdexedInstructionException(input, $INSTRUCTION_FORMAT10x_ODEX.text);
};
insn_format11n returns [int size]
insn_format11n
: //e.g. const/4 v0, 5
INSTRUCTION_FORMAT11n REGISTER COMMA integral_literal {$size = Format.Format11n.size;}
INSTRUCTION_FORMAT11n REGISTER COMMA integral_literal
-> ^(I_STATEMENT_FORMAT11n[$start, "I_STATEMENT_FORMAT11n"] INSTRUCTION_FORMAT11n REGISTER integral_literal);
insn_format11x returns [int size]
insn_format11x
: //e.g. move-result-object v1
INSTRUCTION_FORMAT11x REGISTER {$size = Format.Format11x.size;}
INSTRUCTION_FORMAT11x REGISTER
-> ^(I_STATEMENT_FORMAT11x[$start, "I_STATEMENT_FORMAT11x"] INSTRUCTION_FORMAT11x REGISTER);
insn_format12x returns [int size]
insn_format12x
: //e.g. move v1 v2
instruction_format12x REGISTER COMMA REGISTER {$size = Format.Format12x.size;}
instruction_format12x REGISTER COMMA REGISTER
-> ^(I_STATEMENT_FORMAT12x[$start, "I_STATEMENT_FORMAT12x"] instruction_format12x REGISTER REGISTER);
insn_format20bc returns [int size]
insn_format20bc
: //e.g. throw-verification-error generic-error, Lsome/class;
INSTRUCTION_FORMAT20bc VERIFICATION_ERROR_TYPE COMMA verification_error_reference {$size += Format.Format20bc.size;}
INSTRUCTION_FORMAT20bc VERIFICATION_ERROR_TYPE COMMA verification_error_reference
{
if (!allowOdex || opcodes.getOpcodeByName($INSTRUCTION_FORMAT20bc.text) == null || apiLevel >= 14) {
throwOdexedInstructionException(input, $INSTRUCTION_FORMAT20bc.text);
@ -913,19 +895,19 @@ insn_format20bc returns [int size]
}
-> ^(I_STATEMENT_FORMAT20bc INSTRUCTION_FORMAT20bc VERIFICATION_ERROR_TYPE verification_error_reference);
insn_format20t returns [int size]
insn_format20t
: //e.g. goto/16 endloop:
INSTRUCTION_FORMAT20t label_ref_or_offset {$size = Format.Format20t.size;}
-> ^(I_STATEMENT_FORMAT20t[$start, "I_STATEMENT_FORMAT20t"] INSTRUCTION_FORMAT20t label_ref_or_offset);
INSTRUCTION_FORMAT20t label_ref
-> ^(I_STATEMENT_FORMAT20t[$start, "I_STATEMENT_FORMAT20t"] INSTRUCTION_FORMAT20t label_ref);
insn_format21c_field returns [int size]
insn_format21c_field
: //e.g. sget-object v0, java/lang/System/out LJava/io/PrintStream;
INSTRUCTION_FORMAT21c_FIELD REGISTER COMMA fully_qualified_field {$size = Format.Format21c.size;}
INSTRUCTION_FORMAT21c_FIELD REGISTER COMMA fully_qualified_field
-> ^(I_STATEMENT_FORMAT21c_FIELD[$start, "I_STATEMENT_FORMAT21c_FIELD"] INSTRUCTION_FORMAT21c_FIELD REGISTER fully_qualified_field);
insn_format21c_field_odex returns [int size]
insn_format21c_field_odex
: //e.g. sget-object-volatile v0, java/lang/System/out LJava/io/PrintStream;
INSTRUCTION_FORMAT21c_FIELD_ODEX REGISTER COMMA fully_qualified_field {$size = Format.Format21c.size;}
INSTRUCTION_FORMAT21c_FIELD_ODEX REGISTER COMMA fully_qualified_field
{
if (!allowOdex || opcodes.getOpcodeByName($INSTRUCTION_FORMAT21c_FIELD_ODEX.text) == null || apiLevel >= 14) {
throwOdexedInstructionException(input, $INSTRUCTION_FORMAT21c_FIELD_ODEX.text);
@ -933,49 +915,49 @@ insn_format21c_field_odex returns [int size]
}
-> ^(I_STATEMENT_FORMAT21c_FIELD[$start, "I_STATEMENT_FORMAT21c_FIELD"] INSTRUCTION_FORMAT21c_FIELD_ODEX REGISTER fully_qualified_field);
insn_format21c_string returns [int size]
insn_format21c_string
: //e.g. const-string v1, "Hello World!"
INSTRUCTION_FORMAT21c_STRING REGISTER COMMA STRING_LITERAL {$size = Format.Format21c.size;}
INSTRUCTION_FORMAT21c_STRING REGISTER COMMA STRING_LITERAL
-> ^(I_STATEMENT_FORMAT21c_STRING[$start, "I_STATEMENT_FORMAT21c_STRING"] INSTRUCTION_FORMAT21c_STRING REGISTER STRING_LITERAL);
insn_format21c_type returns [int size]
insn_format21c_type
: //e.g. const-class v2, Lorg/jf/HelloWorld2/HelloWorld2;
INSTRUCTION_FORMAT21c_TYPE REGISTER COMMA reference_type_descriptor {$size = Format.Format21c.size;}
INSTRUCTION_FORMAT21c_TYPE REGISTER COMMA reference_type_descriptor
-> ^(I_STATEMENT_FORMAT21c_TYPE[$start, "I_STATEMENT_FORMAT21c"] INSTRUCTION_FORMAT21c_TYPE REGISTER reference_type_descriptor);
insn_format21ih returns [int size]
insn_format21ih
: //e.g. const/high16 v1, 1234
INSTRUCTION_FORMAT21ih REGISTER COMMA fixed_32bit_literal {$size = Format.Format21ih.size;}
INSTRUCTION_FORMAT21ih REGISTER COMMA fixed_32bit_literal
-> ^(I_STATEMENT_FORMAT21ih[$start, "I_STATEMENT_FORMAT21ih"] INSTRUCTION_FORMAT21ih REGISTER fixed_32bit_literal);
insn_format21lh returns [int size]
insn_format21lh
: //e.g. const-wide/high16 v1, 1234
INSTRUCTION_FORMAT21lh REGISTER COMMA fixed_32bit_literal {$size = Format.Format21lh.size;}
INSTRUCTION_FORMAT21lh REGISTER COMMA fixed_32bit_literal
-> ^(I_STATEMENT_FORMAT21lh[$start, "I_STATEMENT_FORMAT21lh"] INSTRUCTION_FORMAT21lh REGISTER fixed_32bit_literal);
insn_format21s returns [int size]
insn_format21s
: //e.g. const/16 v1, 1234
INSTRUCTION_FORMAT21s REGISTER COMMA integral_literal {$size = Format.Format21s.size;}
INSTRUCTION_FORMAT21s REGISTER COMMA integral_literal
-> ^(I_STATEMENT_FORMAT21s[$start, "I_STATEMENT_FORMAT21s"] INSTRUCTION_FORMAT21s REGISTER integral_literal);
insn_format21t returns [int size]
insn_format21t
: //e.g. if-eqz v0, endloop:
INSTRUCTION_FORMAT21t REGISTER COMMA (label_ref_or_offset) {$size = Format.Format21t.size;}
-> ^(I_STATEMENT_FORMAT21t[$start, "I_STATEMENT_FORMAT21t"] INSTRUCTION_FORMAT21t REGISTER label_ref_or_offset);
INSTRUCTION_FORMAT21t REGISTER COMMA label_ref
-> ^(I_STATEMENT_FORMAT21t[$start, "I_STATEMENT_FORMAT21t"] INSTRUCTION_FORMAT21t REGISTER label_ref);
insn_format22b returns [int size]
insn_format22b
: //e.g. add-int v0, v1, 123
INSTRUCTION_FORMAT22b REGISTER COMMA REGISTER COMMA integral_literal {$size = Format.Format22b.size;}
INSTRUCTION_FORMAT22b REGISTER COMMA REGISTER COMMA integral_literal
-> ^(I_STATEMENT_FORMAT22b[$start, "I_STATEMENT_FORMAT22b"] INSTRUCTION_FORMAT22b REGISTER REGISTER integral_literal);
insn_format22c_field returns [int size]
insn_format22c_field
: //e.g. iput-object v1, v0 org/jf/HelloWorld2/HelloWorld2.helloWorld Ljava/lang/String;
INSTRUCTION_FORMAT22c_FIELD REGISTER COMMA REGISTER COMMA fully_qualified_field {$size = Format.Format22c.size;}
INSTRUCTION_FORMAT22c_FIELD REGISTER COMMA REGISTER COMMA fully_qualified_field
-> ^(I_STATEMENT_FORMAT22c_FIELD[$start, "I_STATEMENT_FORMAT22c_FIELD"] INSTRUCTION_FORMAT22c_FIELD REGISTER REGISTER fully_qualified_field);
insn_format22c_field_odex returns [int size]
insn_format22c_field_odex
: //e.g. iput-object-volatile v1, v0 org/jf/HelloWorld2/HelloWorld2.helloWorld Ljava/lang/String;
INSTRUCTION_FORMAT22c_FIELD_ODEX REGISTER COMMA REGISTER COMMA fully_qualified_field {$size = Format.Format22c.size;}
INSTRUCTION_FORMAT22c_FIELD_ODEX REGISTER COMMA REGISTER COMMA fully_qualified_field
{
if (!allowOdex || opcodes.getOpcodeByName($INSTRUCTION_FORMAT22c_FIELD_ODEX.text) == null || apiLevel >= 14) {
throwOdexedInstructionException(input, $INSTRUCTION_FORMAT22c_FIELD_ODEX.text);
@ -983,146 +965,131 @@ insn_format22c_field_odex returns [int size]
}
-> ^(I_STATEMENT_FORMAT22c_FIELD[$start, "I_STATEMENT_FORMAT22c_FIELD"] INSTRUCTION_FORMAT22c_FIELD_ODEX REGISTER REGISTER fully_qualified_field);
insn_format22c_type returns [int size]
insn_format22c_type
: //e.g. instance-of v0, v1, Ljava/lang/String;
INSTRUCTION_FORMAT22c_TYPE REGISTER COMMA REGISTER COMMA nonvoid_type_descriptor {$size = Format.Format22c.size;}
INSTRUCTION_FORMAT22c_TYPE REGISTER COMMA REGISTER COMMA nonvoid_type_descriptor
-> ^(I_STATEMENT_FORMAT22c_TYPE[$start, "I_STATEMENT_FORMAT22c_TYPE"] INSTRUCTION_FORMAT22c_TYPE REGISTER REGISTER nonvoid_type_descriptor);
insn_format22cs_field returns [int size]
insn_format22cs_field
: //e.g. iget-quick v0, v1, field@0xc
INSTRUCTION_FORMAT22cs_FIELD REGISTER COMMA REGISTER COMMA FIELD_OFFSET
{
throwOdexedInstructionException(input, $INSTRUCTION_FORMAT22cs_FIELD.text);
};
insn_format22s returns [int size]
insn_format22s
: //e.g. add-int/lit16 v0, v1, 12345
instruction_format22s REGISTER COMMA REGISTER COMMA integral_literal {$size = Format.Format22s.size;}
instruction_format22s REGISTER COMMA REGISTER COMMA integral_literal
-> ^(I_STATEMENT_FORMAT22s[$start, "I_STATEMENT_FORMAT22s"] instruction_format22s REGISTER REGISTER integral_literal);
insn_format22t returns [int size]
insn_format22t
: //e.g. if-eq v0, v1, endloop:
INSTRUCTION_FORMAT22t REGISTER COMMA REGISTER COMMA label_ref_or_offset {$size = Format.Format22t.size;}
-> ^(I_STATEMENT_FORMAT22t[$start, "I_STATEMENT_FFORMAT22t"] INSTRUCTION_FORMAT22t REGISTER REGISTER label_ref_or_offset);
INSTRUCTION_FORMAT22t REGISTER COMMA REGISTER COMMA label_ref
-> ^(I_STATEMENT_FORMAT22t[$start, "I_STATEMENT_FFORMAT22t"] INSTRUCTION_FORMAT22t REGISTER REGISTER label_ref);
insn_format22x returns [int size]
insn_format22x
: //e.g. move/from16 v1, v1234
INSTRUCTION_FORMAT22x REGISTER COMMA REGISTER {$size = Format.Format22x.size;}
INSTRUCTION_FORMAT22x REGISTER COMMA REGISTER
-> ^(I_STATEMENT_FORMAT22x[$start, "I_STATEMENT_FORMAT22x"] INSTRUCTION_FORMAT22x REGISTER REGISTER);
insn_format23x returns [int size]
insn_format23x
: //e.g. add-int v1, v2, v3
INSTRUCTION_FORMAT23x REGISTER COMMA REGISTER COMMA REGISTER {$size = Format.Format23x.size;}
INSTRUCTION_FORMAT23x REGISTER COMMA REGISTER COMMA REGISTER
-> ^(I_STATEMENT_FORMAT23x[$start, "I_STATEMENT_FORMAT23x"] INSTRUCTION_FORMAT23x REGISTER REGISTER REGISTER);
insn_format30t returns [int size]
insn_format30t
: //e.g. goto/32 endloop:
INSTRUCTION_FORMAT30t label_ref_or_offset {$size = Format.Format30t.size;}
-> ^(I_STATEMENT_FORMAT30t[$start, "I_STATEMENT_FORMAT30t"] INSTRUCTION_FORMAT30t label_ref_or_offset);
INSTRUCTION_FORMAT30t label_ref
-> ^(I_STATEMENT_FORMAT30t[$start, "I_STATEMENT_FORMAT30t"] INSTRUCTION_FORMAT30t label_ref);
insn_format31c returns [int size]
insn_format31c
: //e.g. const-string/jumbo v1 "Hello World!"
INSTRUCTION_FORMAT31c REGISTER COMMA STRING_LITERAL {$size = Format.Format31c.size;}
INSTRUCTION_FORMAT31c REGISTER COMMA STRING_LITERAL
->^(I_STATEMENT_FORMAT31c[$start, "I_STATEMENT_FORMAT31c"] INSTRUCTION_FORMAT31c REGISTER STRING_LITERAL);
insn_format31i returns [int size]
insn_format31i
: //e.g. const v0, 123456
instruction_format31i REGISTER COMMA fixed_32bit_literal {$size = Format.Format31i.size;}
instruction_format31i REGISTER COMMA fixed_32bit_literal
-> ^(I_STATEMENT_FORMAT31i[$start, "I_STATEMENT_FORMAT31i"] instruction_format31i REGISTER fixed_32bit_literal);
insn_format31t returns [int size]
insn_format31t
: //e.g. fill-array-data v0, ArrayData:
INSTRUCTION_FORMAT31t REGISTER COMMA label_ref_or_offset {$size = Format.Format31t.size;}
{
if ($INSTRUCTION_FORMAT31t.text.equals("packed-switch")) {
CommonTree root = new CommonTree(new CommonToken(I_PACKED_SWITCH_DECLARATION, "I_PACKED_SWITCH_DECLARATION"));
CommonTree address = new CommonTree(new CommonToken(I_ADDRESS, Integer.toString($method::currentAddress)));
root.addChild(address);
root.addChild($label_ref_or_offset.tree.dupNode());
$statements_and_directives::packedSwitchDeclarations.add(root);
} else if ($INSTRUCTION_FORMAT31t.text.equals("sparse-switch")) {
CommonTree root = new CommonTree(new CommonToken(I_SPARSE_SWITCH_DECLARATION, "I_SPARSE_SWITCH_DECLARATION"));
CommonTree address = new CommonTree(new CommonToken(I_ADDRESS, Integer.toString($method::currentAddress)));
root.addChild(address);
root.addChild($label_ref_or_offset.tree.dupNode());
$statements_and_directives::sparseSwitchDeclarations.add(root);
}
}
-> ^(I_STATEMENT_FORMAT31t[$start, "I_STATEMENT_FORMAT31t"] INSTRUCTION_FORMAT31t REGISTER label_ref_or_offset);
INSTRUCTION_FORMAT31t REGISTER COMMA label_ref
-> ^(I_STATEMENT_FORMAT31t[$start, "I_STATEMENT_FORMAT31t"] INSTRUCTION_FORMAT31t REGISTER label_ref);
insn_format32x returns [int size]
insn_format32x
: //e.g. move/16 v4567, v1234
INSTRUCTION_FORMAT32x REGISTER COMMA REGISTER {$size = Format.Format32x.size;}
INSTRUCTION_FORMAT32x REGISTER COMMA REGISTER
-> ^(I_STATEMENT_FORMAT32x[$start, "I_STATEMENT_FORMAT32x"] INSTRUCTION_FORMAT32x REGISTER REGISTER);
insn_format35c_method returns [int size]
insn_format35c_method
: //e.g. invoke-virtual {v0,v1} java/io/PrintStream/print(Ljava/lang/Stream;)V
INSTRUCTION_FORMAT35c_METHOD OPEN_BRACE register_list CLOSE_BRACE COMMA fully_qualified_method {$size = Format.Format35c.size;}
INSTRUCTION_FORMAT35c_METHOD OPEN_BRACE register_list CLOSE_BRACE COMMA fully_qualified_method
-> ^(I_STATEMENT_FORMAT35c_METHOD[$start, "I_STATEMENT_FORMAT35c_METHOD"] INSTRUCTION_FORMAT35c_METHOD register_list fully_qualified_method);
insn_format35c_type returns [int size]
insn_format35c_type
: //e.g. filled-new-array {v0,v1}, I
INSTRUCTION_FORMAT35c_TYPE OPEN_BRACE register_list CLOSE_BRACE COMMA nonvoid_type_descriptor {$size = Format.Format35c.size;}
INSTRUCTION_FORMAT35c_TYPE OPEN_BRACE register_list CLOSE_BRACE COMMA nonvoid_type_descriptor
-> ^(I_STATEMENT_FORMAT35c_TYPE[$start, "I_STATEMENT_FORMAT35c_TYPE"] INSTRUCTION_FORMAT35c_TYPE register_list nonvoid_type_descriptor);
insn_format35c_method_odex returns [int size]
insn_format35c_method_odex
: //e.g. invoke-direct {p0}, Ljava/lang/Object;-><init>()V
INSTRUCTION_FORMAT35c_METHOD_ODEX OPEN_BRACE register_list CLOSE_BRACE COMMA fully_qualified_method
{
throwOdexedInstructionException(input, $INSTRUCTION_FORMAT35c_METHOD_ODEX.text);
};
insn_format35mi_method returns [int size]
insn_format35mi_method
: //e.g. execute-inline {v0, v1}, inline@0x4
INSTRUCTION_FORMAT35mi_METHOD OPEN_BRACE register_list CLOSE_BRACE COMMA INLINE_INDEX
{
throwOdexedInstructionException(input, $INSTRUCTION_FORMAT35mi_METHOD.text);
};
insn_format35ms_method returns [int size]
insn_format35ms_method
: //e.g. invoke-virtual-quick {v0, v1}, vtable@0x4
INSTRUCTION_FORMAT35ms_METHOD OPEN_BRACE register_list CLOSE_BRACE COMMA VTABLE_INDEX
{
throwOdexedInstructionException(input, $INSTRUCTION_FORMAT35ms_METHOD.text);
};
insn_format3rc_method returns [int size]
insn_format3rc_method
: //e.g. invoke-virtual/range {v25..v26}, java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
INSTRUCTION_FORMAT3rc_METHOD OPEN_BRACE register_range CLOSE_BRACE COMMA fully_qualified_method {$size = Format.Format3rc.size;}
INSTRUCTION_FORMAT3rc_METHOD OPEN_BRACE register_range CLOSE_BRACE COMMA fully_qualified_method
-> ^(I_STATEMENT_FORMAT3rc_METHOD[$start, "I_STATEMENT_FORMAT3rc_METHOD"] INSTRUCTION_FORMAT3rc_METHOD register_range fully_qualified_method);
insn_format3rc_method_odex returns [int size]
insn_format3rc_method_odex
: //e.g. invoke-object-init/range {p0}, Ljava/lang/Object;-><init>()V
INSTRUCTION_FORMAT3rc_METHOD_ODEX OPEN_BRACE register_list CLOSE_BRACE COMMA fully_qualified_method
{
throwOdexedInstructionException(input, $INSTRUCTION_FORMAT3rc_METHOD_ODEX.text);
};
insn_format3rc_type returns [int size]
insn_format3rc_type
: //e.g. filled-new-array/range {v0..v6}, I
INSTRUCTION_FORMAT3rc_TYPE OPEN_BRACE register_range CLOSE_BRACE COMMA nonvoid_type_descriptor {$size = Format.Format3rc.size;}
INSTRUCTION_FORMAT3rc_TYPE OPEN_BRACE register_range CLOSE_BRACE COMMA nonvoid_type_descriptor
-> ^(I_STATEMENT_FORMAT3rc_TYPE[$start, "I_STATEMENT_FORMAT3rc_TYPE"] INSTRUCTION_FORMAT3rc_TYPE register_range nonvoid_type_descriptor);
insn_format3rmi_method returns [int size]
insn_format3rmi_method
: //e.g. execute-inline/range {v0 .. v10}, inline@0x14
INSTRUCTION_FORMAT3rmi_METHOD OPEN_BRACE register_range CLOSE_BRACE COMMA INLINE_INDEX
{
throwOdexedInstructionException(input, $INSTRUCTION_FORMAT3rmi_METHOD.text);
};
insn_format3rms_method returns [int size]
insn_format3rms_method
: //e.g. invoke-virtual-quick/range {v0 .. v10}, vtable@0x14
INSTRUCTION_FORMAT3rms_METHOD OPEN_BRACE register_range CLOSE_BRACE COMMA VTABLE_INDEX
{
throwOdexedInstructionException(input, $INSTRUCTION_FORMAT3rms_METHOD.text);
};
insn_format51l returns [int size]
insn_format51l
: //e.g. const-wide v0, 5000000000L
INSTRUCTION_FORMAT51l REGISTER COMMA fixed_literal {$size = Format.Format51l.size;}
INSTRUCTION_FORMAT51l REGISTER COMMA fixed_literal
-> ^(I_STATEMENT_FORMAT51l[$start, "I_STATEMENT_FORMAT51l"] INSTRUCTION_FORMAT51l REGISTER fixed_literal);
insn_array_data_directive returns [int size]
insn_array_data_directive
: ARRAY_DATA_DIRECTIVE
parsed_integer_literal
{
@ -1131,32 +1098,25 @@ insn_array_data_directive returns [int size]
throw new SemanticException(input, $start, "Invalid element width: \%d. Must be 1, 2, 4 or 8", elementWidth);
}
}
(fixed_literal {$size+=elementWidth;})* END_ARRAY_DATA_DIRECTIVE
{$size = (($size + 1) & ~1) + 8;}
fixed_literal* END_ARRAY_DATA_DIRECTIVE
-> ^(I_STATEMENT_ARRAY_DATA[$start, "I_STATEMENT_ARRAY_DATA"] ^(I_ARRAY_ELEMENT_SIZE parsed_integer_literal)
^(I_ARRAY_ELEMENTS fixed_literal*));
insn_packed_switch_directive returns [int size]
insn_packed_switch_directive
: PACKED_SWITCH_DIRECTIVE
fixed_32bit_literal
(switch_target += label_ref_or_offset {$size+=4;})*
END_PACKED_SWITCH_DIRECTIVE {$size = $size + 8;}
label_ref*
END_PACKED_SWITCH_DIRECTIVE
-> ^(I_STATEMENT_PACKED_SWITCH[$start, "I_STATEMENT_PACKED_SWITCH"]
^(I_PACKED_SWITCH_START_KEY[$start, "I_PACKED_SWITCH_START_KEY"] fixed_32bit_literal)
^(I_PACKED_SWITCH_ELEMENTS[$start, "I_PACKED_SWITCH_ELEMENTS"]
$switch_target*)
label_ref*)
);
insn_sparse_switch_directive returns [int size]
insn_sparse_switch_directive
: SPARSE_SWITCH_DIRECTIVE
(fixed_32bit_literal ARROW switch_target += label_ref_or_offset {$size += 8;})*
END_SPARSE_SWITCH_DIRECTIVE {$size = $size + 4;}
(fixed_32bit_literal ARROW label_ref)*
END_SPARSE_SWITCH_DIRECTIVE
-> ^(I_STATEMENT_SPARSE_SWITCH[$start, "I_STATEMENT_SPARSE_SWITCH"]
^(I_SPARSE_SWITCH_ELEMENTS[$start, "I_SPARSE_SWITCH_ELEMENTS"] (fixed_32bit_literal $switch_target)*));
^(I_SPARSE_SWITCH_ELEMENTS[$start, "I_SPARSE_SWITCH_ELEMENTS"] (fixed_32bit_literal label_ref)*));

File diff suppressed because it is too large Load Diff

View File

@ -36,6 +36,7 @@ import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.CommonTreeNodeStream;
import org.apache.commons.cli.*;
import org.jf.dexlib2.writer.builder.DexBuilder;
import org.jf.dexlib2.writer.io.FileDataStore;
import org.jf.util.ConsoleUtil;
import org.jf.util.SmaliHelpFormatter;
@ -225,7 +226,7 @@ public class main {
System.exit(1);
}
dexBuilder.writeTo(outputDexFile);
dexBuilder.writeTo(new FileDataStore(new File(outputDexFile)));
} catch (RuntimeException ex) {
System.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:");
ex.printStackTrace();
@ -241,14 +242,14 @@ public class main {
File[] files = dir.listFiles();
if (files != null) {
for(File file: files) {
if (file.isDirectory()) {
getSmaliFilesInDir(file, smaliFiles);
} else if (file.getName().endsWith(".smali")) {
smaliFiles.add(file);
if (file.isDirectory()) {
getSmaliFilesInDir(file, smaliFiles);
} else if (file.getName().endsWith(".smali")) {
smaliFiles.add(file);
}
}
}
}
}
private static boolean assembleSmaliFile(File smaliFile, DexBuilder dexBuilder, boolean verboseErrors,
boolean printTokens, boolean allowOdex, int apiLevel)

View File

@ -377,8 +377,6 @@ Type = {PrimitiveType} | {ClassDescriptor} | {ArrayDescriptor}
"vtable@0x" {HexDigit}+ { return newToken(VTABLE_INDEX); }
"field@0x" {HexDigit}+ { return newToken(FIELD_OFFSET); }
"+" {Integer} { return newToken(OFFSET); }
# [^\r\n]* { return newToken(LINE_COMMENT, true); }
}
@ -592,7 +590,7 @@ Type = {PrimitiveType} | {ClassDescriptor} | {ArrayDescriptor}
{PrimitiveType} {PrimitiveType}+ { return newToken(PARAM_LIST_OR_ID); }
{Type} {Type}+ { return newToken(PARAM_LIST); }
{SimpleName} { return newToken(SIMPLE_NAME); }
"<init>" | "<clinit>" { return newToken(METHOD_NAME); }
"<" {SimpleName} ">" { return newToken(MEMBER_NAME); }
}
/*Symbols/Whitespace/EOF*/

View File

@ -67,12 +67,12 @@ SIMPLE_NAME("field") INVALID_TOKEN("@")
SIMPLE_NAME("field") INVALID_TOKEN("@") SIMPLE_NAME("zzz")
SIMPLE_NAME("field") INVALID_TOKEN("@") SIMPLE_NAME("abcd")
OFFSET("+0")
OFFSET("+10")
OFFSET("+01")
OFFSET("+0777")
OFFSET("+0x1234ABC")
OFFSET("+1234")
INVALID_TOKEN("+") POSITIVE_INTEGER_LITERAL("0")
INVALID_TOKEN("+") POSITIVE_INTEGER_LITERAL("10")
INVALID_TOKEN("+") POSITIVE_INTEGER_LITERAL("01")
INVALID_TOKEN("+") POSITIVE_INTEGER_LITERAL("0777")
INVALID_TOKEN("+") POSITIVE_INTEGER_LITERAL("0x1234ABC")
INVALID_TOKEN("+") POSITIVE_INTEGER_LITERAL("1234")
OFFSET("+0") POSITIVE_INTEGER_LITERAL("8")
INVALID_TOKEN("+") SIMPLE_NAME("08")
INVALID_TOKEN("+")

View File

@ -152,7 +152,7 @@ END_FIELD_DIRECTIVE(".end field")
METHOD_DIRECTIVE(".method")
ACCESS_SPEC("static")
ACCESS_SPEC("constructor")
METHOD_NAME("<clinit>")
MEMBER_NAME("<clinit>")
OPEN_PAREN("(")
CLOSE_PAREN(")")
VOID_TYPE("V")
@ -211,7 +211,7 @@ CLOSE_BRACE("}")
COMMA(",")
CLASS_DESCRIPTOR("Lcom/android/internal/os/BatteryStatsImpl$1;")
ARROW("->")
METHOD_NAME("<init>")
MEMBER_NAME("<init>")
OPEN_PAREN("(")
CLOSE_PAREN(")")
VOID_TYPE("V")
@ -259,7 +259,7 @@ END_ARRAY_DATA_DIRECTIVE(".end array-data")
END_METHOD_DIRECTIVE(".end method")
METHOD_DIRECTIVE(".method")
ACCESS_SPEC("constructor")
METHOD_NAME("<init>")
MEMBER_NAME("<init>")
OPEN_PAREN("(")
CLASS_DESCRIPTOR("Lcom/android/internal/telephony/cdma/CDMAPhone;")
CLOSE_PAREN(")")
@ -282,7 +282,7 @@ CLOSE_BRACE("}")
COMMA(",")
CLASS_DESCRIPTOR("Lcom/android/internal/telephony/IccFileHandler;")
ARROW("->")
METHOD_NAME("<init>")
MEMBER_NAME("<init>")
OPEN_PAREN("(")
CLASS_DESCRIPTOR("Lcom/android/internal/telephony/PhoneBase;")
CLOSE_PAREN(")")
@ -402,7 +402,7 @@ CLOSE_BRACE("}")
COMMA(",")
CLASS_DESCRIPTOR("Ljava/lang/StringBuilder;")
ARROW("->")
METHOD_NAME("<init>")
MEMBER_NAME("<init>")
OPEN_PAREN("(")
CLOSE_PAREN(")")
VOID_TYPE("V")
@ -464,7 +464,7 @@ CLOSE_BRACE("}")
COMMA(",")
CLASS_DESCRIPTOR("Ljava/lang/RuntimeException;")
ARROW("->")
METHOD_NAME("<init>")
MEMBER_NAME("<init>")
OPEN_PAREN("(")
CLASS_DESCRIPTOR("Ljava/lang/String;")
CLOSE_PAREN(")")

View File

@ -34,6 +34,8 @@ Ljava/lang/String;Ljava/lang/String;
<init>
<clinit>
<blah>
<init->
Ljava/lang/String
L;

View File

@ -32,8 +32,10 @@ PARAM_LIST("[I[I[I")
PARAM_LIST("[I[Z")
PARAM_LIST("[I[Ljava/lang/String;")
METHOD_NAME("<init>")
METHOD_NAME("<clinit>")
MEMBER_NAME("<init>")
MEMBER_NAME("<clinit>")
MEMBER_NAME("<blah>")
MEMBER_NAME("<init->")
SIMPLE_NAME("Ljava") INVALID_TOKEN("/") SIMPLE_NAME("lang") INVALID_TOKEN("/") SIMPLE_NAME("String")
SIMPLE_NAME("L") INVALID_TOKEN(";")
@ -45,4 +47,4 @@ INVALID_TOKEN("[") VOID_TYPE("V")
INVALID_TOKEN("[") SIMPLE_NAME("java") INVALID_TOKEN("/") SIMPLE_NAME("lang") INVALID_TOKEN("/") SIMPLE_NAME("String") INVALID_TOKEN(";")
INVALID_TOKEN("[") INVALID_TOKEN(";")
INVALID_TOKEN("<") SIMPLE_NAME("linit") INVALID_TOKEN(">")
MEMBER_NAME("<linit>")

View File

@ -31,6 +31,7 @@ package org.jf.util;
import ds.tree.RadixTree;
import ds.tree.RadixTreeImpl;
import javax.annotation.Nonnull;
import java.io.*;
import java.nio.CharBuffer;
import java.util.regex.Pattern;
@ -41,6 +42,9 @@ import java.util.regex.Pattern;
* class name to distinguish it from another class with a name that differes only by case. i.e. a.smali and a_2.smali
*/
public class ClassFileNameHandler {
// we leave an extra 10 characters to allow for a numeric suffix to be added, if it's needed
private static final int MAX_FILENAME_LENGTH = 245;
private PackageNameEntry top;
private String fileExtension;
private boolean modifyWindowsReservedFilenames;
@ -83,6 +87,10 @@ public class ClassFileNameHandler {
packageElement += "#";
}
if (packageElement.length() > MAX_FILENAME_LENGTH) {
packageElement = shortenPathComponent(packageElement, MAX_FILENAME_LENGTH);
}
packageElements[elementIndex++] = packageElement;
elementStart = ++i;
}
@ -101,28 +109,44 @@ public class ClassFileNameHandler {
packageElement += "#";
}
if ((packageElement.length() + fileExtension.length()) > MAX_FILENAME_LENGTH) {
packageElement = shortenPathComponent(packageElement, MAX_FILENAME_LENGTH - fileExtension.length());
}
packageElements[elementIndex] = packageElement;
return top.addUniqueChild(packageElements, 0);
}
private static boolean testForWindowsReservedFileNames(File path) {
File f = new File(path, "aux.smali");
if (f.exists()) {
return false;
}
@Nonnull
static String shortenPathComponent(@Nonnull String pathComponent, int maxLength) {
int toRemove = pathComponent.length() - maxLength + 1;
try {
FileWriter writer = new FileWriter(f);
writer.write("test");
writer.flush();
writer.close();
f.delete(); //doesn't throw IOException
return false;
} catch (IOException ex) {
//if an exception occured, it's likely that we're on a windows system.
return true;
int firstIndex = (pathComponent.length()/2) - (toRemove/2);
return pathComponent.substring(0, firstIndex) + "#" + pathComponent.substring(firstIndex+toRemove);
}
private static boolean testForWindowsReservedFileNames(File path) {
String[] reservedNames = new String[]{"aux", "con", "com1", "com9", "lpt1", "com9"};
for (String reservedName: reservedNames) {
File f = new File(path, reservedName + ".smali");
if (f.exists()) {
continue;
}
try {
FileWriter writer = new FileWriter(f);
writer.write("test");
writer.flush();
writer.close();
f.delete(); //doesn't throw IOException
} catch (IOException ex) {
//if an exception occured, it's likely that we're on a windows system.
return true;
}
}
return false;
}
private static Pattern reservedFileNameRegex = Pattern.compile("^CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9]$",
@ -242,7 +266,6 @@ public class ClassFileNameHandler {
}
}
if (pathElementsIndex == pathElements.length - 1) {
String fileName;
if (!isCaseSensitive()) {