mirror of
https://github.com/revanced/Apktool.git
synced 2024-12-11 21:37:47 +01:00
Update to smali 2b5
This commit is contained in:
parent
115db91fab
commit
007a6d45a2
1
brut.apktool.smali/baksmali/.gitignore
vendored
1
brut.apktool.smali/baksmali/.gitignore
vendored
@ -1 +0,0 @@
|
||||
/target
|
@ -29,11 +29,19 @@
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
configurations {
|
||||
proguard
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':brut.apktool.smali:util')
|
||||
compile project(':brut.apktool.smali:dexlib')
|
||||
compile 'commons-cli:commons-cli:1.2'
|
||||
compile 'com.google.code.findbugs:jsr305:1.3.9'
|
||||
compile project(':util')
|
||||
compile project(':dexlib2')
|
||||
compile depends.commons_cli
|
||||
compile depends.guava
|
||||
|
||||
testCompile depends.junit
|
||||
|
||||
proguard depends.proguard
|
||||
}
|
||||
|
||||
processResources.inputs.property('version', version)
|
||||
@ -46,4 +54,28 @@ jar {
|
||||
manifest {
|
||||
attributes("Main-Class": "org.jf.baksmali.main")
|
||||
}
|
||||
}
|
||||
|
||||
doLast {
|
||||
ant.symlink(link: file("${destinationDir}/baksmali.jar"), resource: archivePath, overwrite: true)
|
||||
}
|
||||
}
|
||||
|
||||
task proguard(type: JavaExec, dependsOn: jar) {
|
||||
def outFile = jar.destinationDir.getPath() + '/' + jar.baseName + '-' + jar.version + '-small' + '.' + jar.extension
|
||||
inputs.file jar.archivePath
|
||||
outputs.file outFile
|
||||
|
||||
classpath = configurations.proguard
|
||||
main = 'proguard.ProGuard'
|
||||
args '-injars ' + jar.archivePath
|
||||
args '-outjars ' + outFile
|
||||
args '-libraryjars ' + System.properties['java.home'] + '/lib/rt.jar'
|
||||
args '-dontobfuscate'
|
||||
args '-dontoptimize'
|
||||
args '-keep public class org.jf.baksmali.main { public static void main(java.lang.String[]); }'
|
||||
args '-keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); }'
|
||||
args '-dontwarn com.google.common.**'
|
||||
args '-dontnote com.google.common.**'
|
||||
}
|
||||
|
||||
tasks.getByPath(':release').dependsOn(proguard)
|
||||
|
@ -29,35 +29,36 @@
|
||||
package org.jf.baksmali.Adaptors;
|
||||
|
||||
import org.jf.baksmali.Adaptors.EncodedValue.AnnotationEncodedValueAdaptor;
|
||||
import org.jf.dexlib2.AnnotationVisibility;
|
||||
import org.jf.dexlib2.iface.Annotation;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.jf.dexlib.AnnotationItem;
|
||||
import org.jf.dexlib.AnnotationSetItem;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class AnnotationFormatter {
|
||||
|
||||
public static void writeTo(IndentingWriter writer, AnnotationSetItem annotationSet) throws IOException {
|
||||
public static void writeTo(IndentingWriter writer,
|
||||
Collection<? extends Annotation> annotations) throws IOException {
|
||||
boolean first = true;
|
||||
for (AnnotationItem annotationItem: annotationSet.getAnnotations()) {
|
||||
for (Annotation annotation: annotations) {
|
||||
if (!first) {
|
||||
writer.write('\n');
|
||||
}
|
||||
first = false;
|
||||
|
||||
writeTo(writer, annotationItem);
|
||||
writeTo(writer, annotation);
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeTo(IndentingWriter writer, AnnotationItem annotationItem) throws IOException {
|
||||
public static void writeTo(IndentingWriter writer, Annotation annotation) throws IOException {
|
||||
writer.write(".annotation ");
|
||||
writer.write(annotationItem.getVisibility().visibility);
|
||||
writer.write(AnnotationVisibility.getVisibility(annotation.getVisibility()));
|
||||
writer.write(' ');
|
||||
ReferenceFormatter.writeTypeReference(writer, annotationItem.getEncodedAnnotation().annotationType);
|
||||
writer.write(annotation.getType());
|
||||
writer.write('\n');
|
||||
|
||||
AnnotationEncodedValueAdaptor.writeElementsTo(writer, annotationItem.getEncodedAnnotation());
|
||||
AnnotationEncodedValueAdaptor.writeElementsTo(writer, annotation.getElements());
|
||||
|
||||
writer.write(".end annotation\n");
|
||||
}
|
||||
|
@ -28,33 +28,36 @@
|
||||
|
||||
package org.jf.baksmali.Adaptors;
|
||||
|
||||
import org.jf.baksmali.baksmaliOptions;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.jf.dexlib.TypeIdItem;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
|
||||
public class CatchMethodItem extends MethodItem {
|
||||
private final TypeIdItem exceptionType;
|
||||
private final String exceptionType;
|
||||
|
||||
private final LabelMethodItem tryStartLabel;
|
||||
private final LabelMethodItem tryEndLabel;
|
||||
private final LabelMethodItem handlerLabel;
|
||||
|
||||
public CatchMethodItem(MethodDefinition.LabelCache labelCache, int codeAddress, TypeIdItem exceptionType,
|
||||
int startAddress, int endAddress, int handlerAddress) {
|
||||
public CatchMethodItem(@Nonnull baksmaliOptions options, @Nonnull MethodDefinition.LabelCache labelCache,
|
||||
int codeAddress, @Nullable String exceptionType, int startAddress, int endAddress,
|
||||
int handlerAddress) {
|
||||
super(codeAddress);
|
||||
this.exceptionType = exceptionType;
|
||||
|
||||
tryStartLabel = labelCache.internLabel(new LabelMethodItem(startAddress, "try_start_"));
|
||||
tryStartLabel = labelCache.internLabel(new LabelMethodItem(options, startAddress, "try_start_"));
|
||||
|
||||
//use the address from the last covered instruction, but make the label
|
||||
//name refer to the address of the next instruction
|
||||
tryEndLabel = labelCache.internLabel(new EndTryLabelMethodItem(codeAddress, endAddress));
|
||||
tryEndLabel = labelCache.internLabel(new EndTryLabelMethodItem(options, codeAddress, endAddress));
|
||||
|
||||
if (exceptionType == null) {
|
||||
handlerLabel = labelCache.internLabel(new LabelMethodItem(handlerAddress, "catchall_"));
|
||||
handlerLabel = labelCache.internLabel(new LabelMethodItem(options, handlerAddress, "catchall_"));
|
||||
} else {
|
||||
handlerLabel = labelCache.internLabel(new LabelMethodItem(handlerAddress, "catch_"));
|
||||
handlerLabel = labelCache.internLabel(new LabelMethodItem(options, handlerAddress, "catch_"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,7 +84,7 @@ public class CatchMethodItem extends MethodItem {
|
||||
writer.write(".catchall");
|
||||
} else {
|
||||
writer.write(".catch ");
|
||||
ReferenceFormatter.writeTypeReference(writer, exceptionType);
|
||||
writer.write(exceptionType);
|
||||
}
|
||||
writer.write(" {");
|
||||
tryStartLabel.writeTo(writer);
|
||||
|
@ -28,81 +28,69 @@
|
||||
|
||||
package org.jf.baksmali.Adaptors;
|
||||
|
||||
import org.jf.dexlib.Util.Utf8Utils;
|
||||
import org.jf.util.CommentingIndentingWriter;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.jf.baksmali.baksmaliOptions;
|
||||
import org.jf.dexlib2.AccessFlags;
|
||||
import org.jf.dexlib2.dexbacked.DexBackedClassDef;
|
||||
import org.jf.dexlib2.iface.*;
|
||||
import org.jf.dexlib2.iface.instruction.Instruction;
|
||||
import org.jf.dexlib2.iface.instruction.formats.Instruction21c;
|
||||
import org.jf.dexlib2.iface.reference.FieldReference;
|
||||
import org.jf.dexlib2.util.ReferenceUtil;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.jf.dexlib.*;
|
||||
import org.jf.dexlib.Code.Analysis.ValidationException;
|
||||
import org.jf.dexlib.Code.Format.Instruction21c;
|
||||
import org.jf.dexlib.Code.Format.Instruction41c;
|
||||
import org.jf.dexlib.Code.Instruction;
|
||||
import org.jf.dexlib.EncodedValue.EncodedValue;
|
||||
import org.jf.dexlib.Util.AccessFlags;
|
||||
import org.jf.dexlib.Util.SparseArray;
|
||||
import org.jf.util.StringUtils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
public class ClassDefinition {
|
||||
private ClassDefItem classDefItem;
|
||||
@Nullable
|
||||
private ClassDataItem classDataItem;
|
||||
|
||||
private SparseArray<FieldIdItem> fieldsSetInStaticConstructor;
|
||||
@Nonnull public final baksmaliOptions options;
|
||||
@Nonnull public final ClassDef classDef;
|
||||
@Nonnull private final HashSet<String> fieldsSetInStaticConstructor;
|
||||
|
||||
protected boolean validationErrors;
|
||||
|
||||
public ClassDefinition(ClassDefItem classDefItem) {
|
||||
this.classDefItem = classDefItem;
|
||||
this.classDataItem = classDefItem.getClassData();
|
||||
findFieldsSetInStaticConstructor();
|
||||
public ClassDefinition(@Nonnull baksmaliOptions options, @Nonnull ClassDef classDef) {
|
||||
this.options = options;
|
||||
this.classDef = classDef;
|
||||
fieldsSetInStaticConstructor = findFieldsSetInStaticConstructor();
|
||||
}
|
||||
|
||||
public boolean hadValidationErrors() {
|
||||
return validationErrors;
|
||||
}
|
||||
|
||||
private void findFieldsSetInStaticConstructor() {
|
||||
fieldsSetInStaticConstructor = new SparseArray<FieldIdItem>();
|
||||
@Nonnull
|
||||
private HashSet<String> findFieldsSetInStaticConstructor() {
|
||||
HashSet<String> fieldsSetInStaticConstructor = new HashSet<String>();
|
||||
|
||||
if (classDataItem == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (ClassDataItem.EncodedMethod directMethod: classDataItem.getDirectMethods()) {
|
||||
if (directMethod.method.getMethodName().getStringValue().equals("<clinit>") &&
|
||||
directMethod.codeItem != null) {
|
||||
for (Instruction instruction: directMethod.codeItem.getInstructions()) {
|
||||
switch (instruction.opcode) {
|
||||
case SPUT:
|
||||
case SPUT_BOOLEAN:
|
||||
case SPUT_BYTE:
|
||||
case SPUT_CHAR:
|
||||
case SPUT_OBJECT:
|
||||
case SPUT_SHORT:
|
||||
case SPUT_WIDE: {
|
||||
Instruction21c ins = (Instruction21c)instruction;
|
||||
FieldIdItem fieldIdItem = (FieldIdItem)ins.getReferencedItem();
|
||||
fieldsSetInStaticConstructor.put(fieldIdItem.getIndex(), fieldIdItem);
|
||||
break;
|
||||
}
|
||||
case SPUT_JUMBO:
|
||||
case SPUT_BOOLEAN_JUMBO:
|
||||
case SPUT_BYTE_JUMBO:
|
||||
case SPUT_CHAR_JUMBO:
|
||||
case SPUT_OBJECT_JUMBO:
|
||||
case SPUT_SHORT_JUMBO:
|
||||
case SPUT_WIDE_JUMBO: {
|
||||
Instruction41c ins = (Instruction41c)instruction;
|
||||
FieldIdItem fieldIdItem = (FieldIdItem)ins.getReferencedItem();
|
||||
fieldsSetInStaticConstructor.put(fieldIdItem.getIndex(), fieldIdItem);
|
||||
break;
|
||||
for (Method method: classDef.getDirectMethods()) {
|
||||
if (method.getName().equals("<clinit>")) {
|
||||
MethodImplementation impl = method.getImplementation();
|
||||
if (impl != null) {
|
||||
for (Instruction instruction: impl.getInstructions()) {
|
||||
switch (instruction.getOpcode()) {
|
||||
case SPUT:
|
||||
case SPUT_BOOLEAN:
|
||||
case SPUT_BYTE:
|
||||
case SPUT_CHAR:
|
||||
case SPUT_OBJECT:
|
||||
case SPUT_SHORT:
|
||||
case SPUT_WIDE: {
|
||||
Instruction21c ins = (Instruction21c)instruction;
|
||||
FieldReference fieldRef = (FieldReference)ins.getReference();
|
||||
if (fieldRef.getDefiningClass().equals((classDef.getType()))) {
|
||||
fieldsSetInStaticConstructor.add(ReferenceUtil.getShortFieldDescriptor(fieldRef));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return fieldsSetInStaticConstructor;
|
||||
}
|
||||
|
||||
public void writeTo(IndentingWriter writer) throws IOException {
|
||||
@ -111,238 +99,219 @@ public class ClassDefinition {
|
||||
writeSourceFile(writer);
|
||||
writeInterfaces(writer);
|
||||
writeAnnotations(writer);
|
||||
writeStaticFields(writer);
|
||||
writeInstanceFields(writer);
|
||||
writeDirectMethods(writer);
|
||||
writeVirtualMethods(writer);
|
||||
Set<String> staticFields = writeStaticFields(writer);
|
||||
writeInstanceFields(writer, staticFields);
|
||||
Set<String> directMethods = writeDirectMethods(writer);
|
||||
writeVirtualMethods(writer, directMethods);
|
||||
}
|
||||
|
||||
private void writeClass(IndentingWriter writer) throws IOException {
|
||||
writer.write(".class ");
|
||||
writeAccessFlags(writer);
|
||||
writer.write(classDefItem.getClassType().getTypeDescriptor());
|
||||
writer.write(classDef.getType());
|
||||
writer.write('\n');
|
||||
}
|
||||
|
||||
private void writeAccessFlags(IndentingWriter writer) throws IOException {
|
||||
for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForClass(classDefItem.getAccessFlags())) {
|
||||
for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForClass(classDef.getAccessFlags())) {
|
||||
writer.write(accessFlag.toString());
|
||||
writer.write(' ');
|
||||
}
|
||||
}
|
||||
|
||||
private void writeSuper(IndentingWriter writer) throws IOException {
|
||||
TypeIdItem superClass = classDefItem.getSuperclass();
|
||||
String superClass = classDef.getSuperclass();
|
||||
if (superClass != null) {
|
||||
writer.write(".super ");
|
||||
writer.write(superClass.getTypeDescriptor());
|
||||
writer.write(superClass);
|
||||
writer.write('\n');
|
||||
}
|
||||
}
|
||||
|
||||
private void writeSourceFile(IndentingWriter writer) throws IOException {
|
||||
StringIdItem sourceFile = classDefItem.getSourceFile();
|
||||
String sourceFile = classDef.getSourceFile();
|
||||
if (sourceFile != null) {
|
||||
writer.write(".source \"");
|
||||
Utf8Utils.writeEscapedString(writer, sourceFile.getStringValue());
|
||||
StringUtils.writeEscapedString(writer, sourceFile);
|
||||
writer.write("\"\n");
|
||||
}
|
||||
}
|
||||
|
||||
private void writeInterfaces(IndentingWriter writer) throws IOException {
|
||||
TypeListItem interfaceList = classDefItem.getInterfaces();
|
||||
if (interfaceList == null) {
|
||||
return;
|
||||
}
|
||||
List<String> interfaces = Lists.newArrayList(classDef.getInterfaces());
|
||||
Collections.sort(interfaces);
|
||||
|
||||
List<TypeIdItem> interfaces = interfaceList.getTypes();
|
||||
if (interfaces == null || interfaces.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
writer.write('\n');
|
||||
writer.write("# interfaces\n");
|
||||
for (TypeIdItem typeIdItem: interfaceList.getTypes()) {
|
||||
writer.write(".implements ");
|
||||
writer.write(typeIdItem.getTypeDescriptor());
|
||||
if (interfaces.size() != 0) {
|
||||
writer.write('\n');
|
||||
writer.write("# interfaces\n");
|
||||
for (String interfaceName: interfaces) {
|
||||
writer.write(".implements ");
|
||||
writer.write(interfaceName);
|
||||
writer.write('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeAnnotations(IndentingWriter writer) throws IOException {
|
||||
AnnotationDirectoryItem annotationDirectory = classDefItem.getAnnotations();
|
||||
if (annotationDirectory == null) {
|
||||
return;
|
||||
Collection<? extends Annotation> classAnnotations = classDef.getAnnotations();
|
||||
if (classAnnotations.size() != 0) {
|
||||
writer.write("\n\n");
|
||||
writer.write("# annotations\n");
|
||||
AnnotationFormatter.writeTo(writer, classAnnotations);
|
||||
}
|
||||
|
||||
AnnotationSetItem annotationSet = annotationDirectory.getClassAnnotations();
|
||||
if (annotationSet == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
writer.write("\n\n");
|
||||
writer.write("# annotations\n");
|
||||
AnnotationFormatter.writeTo(writer, annotationSet);
|
||||
}
|
||||
|
||||
private void writeStaticFields(IndentingWriter writer) throws IOException {
|
||||
if (classDataItem == null) {
|
||||
return;
|
||||
}
|
||||
//if classDataItem is not null, then classDefItem won't be null either
|
||||
assert(classDefItem != null);
|
||||
private Set<String> writeStaticFields(IndentingWriter writer) throws IOException {
|
||||
boolean wroteHeader = false;
|
||||
Set<String> writtenFields = new HashSet<String>();
|
||||
|
||||
EncodedArrayItem encodedStaticInitializers = classDefItem.getStaticFieldInitializers();
|
||||
|
||||
EncodedValue[] staticInitializers;
|
||||
if (encodedStaticInitializers != null) {
|
||||
staticInitializers = encodedStaticInitializers.getEncodedArray().values;
|
||||
Iterable<? extends Field> staticFields;
|
||||
if (classDef instanceof DexBackedClassDef) {
|
||||
staticFields = ((DexBackedClassDef)classDef).getStaticFields(false);
|
||||
} else {
|
||||
staticInitializers = new EncodedValue[0];
|
||||
staticFields = classDef.getStaticFields();
|
||||
}
|
||||
|
||||
List<ClassDataItem.EncodedField> encodedFields = classDataItem.getStaticFields();
|
||||
if (encodedFields.size() == 0) {
|
||||
return;
|
||||
for (Field field: staticFields) {
|
||||
if (!wroteHeader) {
|
||||
writer.write("\n\n");
|
||||
writer.write("# static fields");
|
||||
wroteHeader = true;
|
||||
}
|
||||
writer.write('\n');
|
||||
|
||||
boolean setInStaticConstructor;
|
||||
IndentingWriter fieldWriter = writer;
|
||||
String fieldString = ReferenceUtil.getShortFieldDescriptor(field);
|
||||
if (!writtenFields.add(fieldString)) {
|
||||
writer.write("# duplicate field ignored\n");
|
||||
fieldWriter = new CommentingIndentingWriter(writer);
|
||||
System.err.println(String.format("Ignoring duplicate field: %s->%s", classDef.getType(), fieldString));
|
||||
setInStaticConstructor = false;
|
||||
} else {
|
||||
setInStaticConstructor = fieldsSetInStaticConstructor.contains(fieldString);
|
||||
}
|
||||
FieldDefinition.writeTo(fieldWriter, field, setInStaticConstructor);
|
||||
}
|
||||
return writtenFields;
|
||||
}
|
||||
|
||||
private void writeInstanceFields(IndentingWriter writer, Set<String> staticFields) throws IOException {
|
||||
boolean wroteHeader = false;
|
||||
Set<String> writtenFields = new HashSet<String>();
|
||||
|
||||
Iterable<? extends Field> instanceFields;
|
||||
if (classDef instanceof DexBackedClassDef) {
|
||||
instanceFields = ((DexBackedClassDef)classDef).getInstanceFields(false);
|
||||
} else {
|
||||
instanceFields = classDef.getInstanceFields();
|
||||
}
|
||||
|
||||
writer.write("\n\n");
|
||||
writer.write("# static fields\n");
|
||||
|
||||
for (int i=0; i<encodedFields.size(); i++) {
|
||||
if (i > 0) {
|
||||
writer.write('\n');
|
||||
}
|
||||
|
||||
ClassDataItem.EncodedField field = encodedFields.get(i);
|
||||
EncodedValue encodedValue = null;
|
||||
if (i < staticInitializers.length) {
|
||||
encodedValue = staticInitializers[i];
|
||||
}
|
||||
AnnotationSetItem fieldAnnotations = null;
|
||||
AnnotationDirectoryItem annotations = classDefItem.getAnnotations();
|
||||
if (annotations != null) {
|
||||
fieldAnnotations = annotations.getFieldAnnotations(field.field);
|
||||
for (Field field: instanceFields) {
|
||||
if (!wroteHeader) {
|
||||
writer.write("\n\n");
|
||||
writer.write("# instance fields");
|
||||
wroteHeader = true;
|
||||
}
|
||||
writer.write('\n');
|
||||
|
||||
IndentingWriter fieldWriter = writer;
|
||||
// the encoded fields are sorted, so we just have to compare with the previous one to detect duplicates
|
||||
if (i > 0 && field.equals(encodedFields.get(i-1))) {
|
||||
fieldWriter = new CommentingIndentingWriter(writer, "#");
|
||||
fieldWriter.write("Ignoring field with duplicate signature\n");
|
||||
System.err.println(String.format("Warning: class %s has duplicate static field %s, Ignoring.",
|
||||
classDefItem.getClassType().getTypeDescriptor(), field.field.getShortFieldString()));
|
||||
String fieldString = ReferenceUtil.getShortFieldDescriptor(field);
|
||||
if (!writtenFields.add(fieldString)) {
|
||||
writer.write("# duplicate field ignored\n");
|
||||
fieldWriter = new CommentingIndentingWriter(writer);
|
||||
System.err.println(String.format("Ignoring duplicate field: %s->%s", classDef.getType(), fieldString));
|
||||
} else if (staticFields.contains(fieldString)) {
|
||||
System.err.println(String.format("Duplicate static+instance field found: %s->%s",
|
||||
classDef.getType(), fieldString));
|
||||
System.err.println("You will need to rename one of these fields, including all references.");
|
||||
|
||||
writer.write("# There is both a static and instance field with this signature.\n" +
|
||||
"# You will need to rename one of these fields, including all references.\n");
|
||||
}
|
||||
|
||||
boolean setInStaticConstructor =
|
||||
fieldsSetInStaticConstructor.get(field.field.getIndex()) != null;
|
||||
|
||||
FieldDefinition.writeTo(fieldWriter, field, encodedValue, fieldAnnotations, setInStaticConstructor);
|
||||
FieldDefinition.writeTo(fieldWriter, field, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeInstanceFields(IndentingWriter writer) throws IOException {
|
||||
if (classDataItem == null) {
|
||||
return;
|
||||
private Set<String> writeDirectMethods(IndentingWriter writer) throws IOException {
|
||||
boolean wroteHeader = false;
|
||||
Set<String> writtenMethods = new HashSet<String>();
|
||||
|
||||
Iterable<? extends Method> directMethods;
|
||||
if (classDef instanceof DexBackedClassDef) {
|
||||
directMethods = ((DexBackedClassDef)classDef).getDirectMethods(false);
|
||||
} else {
|
||||
directMethods = classDef.getDirectMethods();
|
||||
}
|
||||
|
||||
List<ClassDataItem.EncodedField> encodedFields = classDataItem.getInstanceFields();
|
||||
if (encodedFields.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
writer.write("\n\n");
|
||||
writer.write("# instance fields\n");
|
||||
for (int i=0; i<encodedFields.size(); i++) {
|
||||
ClassDataItem.EncodedField field = encodedFields.get(i);
|
||||
|
||||
if (i > 0) {
|
||||
writer.write('\n');
|
||||
for (Method method: directMethods) {
|
||||
if (!wroteHeader) {
|
||||
writer.write("\n\n");
|
||||
writer.write("# direct methods");
|
||||
wroteHeader = true;
|
||||
}
|
||||
writer.write('\n');
|
||||
|
||||
AnnotationSetItem fieldAnnotations = null;
|
||||
AnnotationDirectoryItem annotations = classDefItem.getAnnotations();
|
||||
if (annotations != null) {
|
||||
fieldAnnotations = annotations.getFieldAnnotations(field.field);
|
||||
}
|
||||
|
||||
IndentingWriter fieldWriter = writer;
|
||||
// the encoded fields are sorted, so we just have to compare with the previous one to detect duplicates
|
||||
if (i > 0 && field.equals(encodedFields.get(i-1))) {
|
||||
fieldWriter = new CommentingIndentingWriter(writer, "#");
|
||||
fieldWriter.write("Ignoring field with duplicate signature\n");
|
||||
System.err.println(String.format("Warning: class %s has duplicate instance field %s, Ignoring.",
|
||||
classDefItem.getClassType().getTypeDescriptor(), field.field.getShortFieldString()));
|
||||
}
|
||||
|
||||
FieldDefinition.writeTo(fieldWriter, field, null, fieldAnnotations, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeDirectMethods(IndentingWriter writer) throws IOException {
|
||||
if (classDataItem == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<ClassDataItem.EncodedMethod> directMethods = classDataItem.getDirectMethods();
|
||||
if (directMethods.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
writer.write("\n\n");
|
||||
writer.write("# direct methods\n");
|
||||
writeMethods(writer, directMethods);
|
||||
}
|
||||
|
||||
private void writeVirtualMethods(IndentingWriter writer) throws IOException {
|
||||
if (classDataItem == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<ClassDataItem.EncodedMethod> virtualMethods = classDataItem.getVirtualMethods();
|
||||
|
||||
if (virtualMethods.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
writer.write("\n\n");
|
||||
writer.write("# virtual methods\n");
|
||||
writeMethods(writer, virtualMethods);
|
||||
}
|
||||
|
||||
private void writeMethods(IndentingWriter writer, List<ClassDataItem.EncodedMethod> methods) throws IOException {
|
||||
for (int i=0; i<methods.size(); i++) {
|
||||
ClassDataItem.EncodedMethod method = methods.get(i);
|
||||
if (i > 0) {
|
||||
writer.write('\n');
|
||||
}
|
||||
|
||||
AnnotationSetItem methodAnnotations = null;
|
||||
AnnotationSetRefList parameterAnnotations = null;
|
||||
AnnotationDirectoryItem annotations = classDefItem.getAnnotations();
|
||||
if (annotations != null) {
|
||||
methodAnnotations = annotations.getMethodAnnotations(method.method);
|
||||
parameterAnnotations = annotations.getParameterAnnotations(method.method);
|
||||
}
|
||||
// TODO: check for method validation errors
|
||||
String methodString = ReferenceUtil.getShortMethodDescriptor(method);
|
||||
|
||||
IndentingWriter methodWriter = writer;
|
||||
// the encoded methods are sorted, so we just have to compare with the previous one to detect duplicates
|
||||
if (i > 0 && method.equals(methods.get(i-1))) {
|
||||
methodWriter = new CommentingIndentingWriter(writer, "#");
|
||||
methodWriter.write("Ignoring method with duplicate signature\n");
|
||||
System.err.println(String.format("Warning: class %s has duplicate method %s, Ignoring.",
|
||||
classDefItem.getClassType().getTypeDescriptor(), method.method.getShortMethodString()));
|
||||
if (!writtenMethods.add(methodString)) {
|
||||
writer.write("# duplicate method ignored\n");
|
||||
methodWriter = new CommentingIndentingWriter(writer);
|
||||
}
|
||||
|
||||
MethodDefinition methodDefinition = new MethodDefinition(method);
|
||||
methodDefinition.writeTo(methodWriter, methodAnnotations, parameterAnnotations);
|
||||
MethodImplementation methodImpl = method.getImplementation();
|
||||
if (methodImpl == null) {
|
||||
MethodDefinition.writeEmptyMethodTo(methodWriter, method);
|
||||
} else {
|
||||
MethodDefinition methodDefinition = new MethodDefinition(this, method, methodImpl);
|
||||
methodDefinition.writeTo(methodWriter);
|
||||
}
|
||||
}
|
||||
return writtenMethods;
|
||||
}
|
||||
|
||||
ValidationException validationException = methodDefinition.getValidationException();
|
||||
if (validationException != null) {
|
||||
System.err.println(String.format("Error while disassembling method %s. Continuing.",
|
||||
method.method.getMethodString()));
|
||||
validationException.printStackTrace(System.err);
|
||||
this.validationErrors = true;
|
||||
private void writeVirtualMethods(IndentingWriter writer, Set<String> directMethods) throws IOException {
|
||||
boolean wroteHeader = false;
|
||||
Set<String> writtenMethods = new HashSet<String>();
|
||||
|
||||
Iterable<? extends Method> virtualMethods;
|
||||
if (classDef instanceof DexBackedClassDef) {
|
||||
virtualMethods = ((DexBackedClassDef)classDef).getVirtualMethods(false);
|
||||
} else {
|
||||
virtualMethods = classDef.getVirtualMethods();
|
||||
}
|
||||
|
||||
for (Method method: virtualMethods) {
|
||||
if (!wroteHeader) {
|
||||
writer.write("\n\n");
|
||||
writer.write("# virtual methods");
|
||||
wroteHeader = true;
|
||||
}
|
||||
writer.write('\n');
|
||||
|
||||
// TODO: check for method validation errors
|
||||
String methodString = ReferenceUtil.getShortMethodDescriptor(method);
|
||||
|
||||
IndentingWriter methodWriter = writer;
|
||||
if (!writtenMethods.add(methodString)) {
|
||||
writer.write("# duplicate method ignored\n");
|
||||
methodWriter = new CommentingIndentingWriter(writer);
|
||||
} else if (directMethods.contains(methodString)) {
|
||||
writer.write("# There is both a direct and virtual method with this signature.\n" +
|
||||
"# You will need to rename one of these methods, including all references.\n");
|
||||
System.err.println(String.format("Duplicate direct+virtual method found: %s->%s",
|
||||
classDef.getType(), methodString));
|
||||
System.err.println("You will need to rename one of these methods, including all references.");
|
||||
}
|
||||
|
||||
MethodImplementation methodImpl = method.getImplementation();
|
||||
if (methodImpl == null) {
|
||||
MethodDefinition.writeEmptyMethodTo(methodWriter, method);
|
||||
} else {
|
||||
MethodDefinition methodDefinition = new MethodDefinition(this, method, methodImpl);
|
||||
methodDefinition.writeTo(methodWriter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012, Google Inc.
|
||||
* Copyright 2013, Google Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -29,20 +29,20 @@
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.jf.util;
|
||||
package org.jf.baksmali.Adaptors;
|
||||
|
||||
import org.jf.util.IndentingWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
public class CommentingIndentingWriter extends IndentingWriter {
|
||||
private final String commentStr;
|
||||
|
||||
public CommentingIndentingWriter(Writer writer, String commentStr) {
|
||||
public CommentingIndentingWriter(Writer writer) {
|
||||
super(writer);
|
||||
this.commentStr = commentStr;
|
||||
}
|
||||
|
||||
protected void writeLineStart() throws IOException {
|
||||
writer.write(commentStr);
|
||||
@Override protected void writeIndent() throws IOException {
|
||||
writer.write("# ");
|
||||
super.writeIndent();
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.baksmali.Adaptors.Debug;
|
||||
|
||||
import org.jf.util.IndentingWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class BeginEpilogueMethodItem extends DebugMethodItem {
|
||||
public BeginEpilogueMethodItem(int codeAddress, int sortOrder) {
|
||||
super(codeAddress, sortOrder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||
writer.write(".prologue");
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.baksmali.Adaptors.Debug;
|
||||
|
||||
import org.jf.baksmali.Adaptors.MethodItem;
|
||||
import org.jf.baksmali.Adaptors.RegisterFormatter;
|
||||
import org.jf.dexlib2.DebugItemType;
|
||||
import org.jf.dexlib2.iface.debug.*;
|
||||
import org.jf.util.ExceptionWithContext;
|
||||
|
||||
public abstract class DebugMethodItem extends MethodItem {
|
||||
private final int sortOrder;
|
||||
|
||||
protected DebugMethodItem(int codeAddress, int sortOrder) {
|
||||
super(codeAddress);
|
||||
this.sortOrder = sortOrder;
|
||||
}
|
||||
|
||||
@Override public double getSortOrder() { return sortOrder; }
|
||||
|
||||
public static DebugMethodItem build(RegisterFormatter registerFormatter, DebugItem debugItem) {
|
||||
int codeAddress = debugItem.getCodeAddress();
|
||||
switch (debugItem.getDebugItemType()) {
|
||||
case DebugItemType.START_LOCAL:
|
||||
return new StartLocalMethodItem(codeAddress, -1, registerFormatter, (StartLocal)debugItem);
|
||||
case DebugItemType.END_LOCAL:
|
||||
return new EndLocalMethodItem(codeAddress, -1, registerFormatter, (EndLocal)debugItem);
|
||||
case DebugItemType.RESTART_LOCAL:
|
||||
return new RestartLocalMethodItem(codeAddress, -1, registerFormatter, (RestartLocal)debugItem);
|
||||
case DebugItemType.EPILOGUE_BEGIN:
|
||||
return new BeginEpilogueMethodItem(codeAddress, -4);
|
||||
case DebugItemType.PROLOGUE_END:
|
||||
return new EndPrologueMethodItem(codeAddress, -4);
|
||||
case DebugItemType.SET_SOURCE_FILE:
|
||||
return new SetSourceFileMethodItem(codeAddress, -3, (SetSourceFile)debugItem);
|
||||
case DebugItemType.LINE_NUMBER:
|
||||
return new LineNumberMethodItem(codeAddress, -2, (LineNumber)debugItem);
|
||||
default:
|
||||
throw new ExceptionWithContext("Invalid debug item type: %d", debugItem.getDebugItemType());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.baksmali.Adaptors.Debug;
|
||||
|
||||
import org.jf.baksmali.Adaptors.RegisterFormatter;
|
||||
import org.jf.dexlib2.iface.debug.EndLocal;
|
||||
import org.jf.util.IndentingWriter;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
|
||||
public class EndLocalMethodItem extends DebugMethodItem {
|
||||
@Nonnull private final EndLocal endLocal;
|
||||
@Nonnull private final RegisterFormatter registerFormatter;
|
||||
|
||||
public EndLocalMethodItem(int codeAddress, int sortOrder, @Nonnull RegisterFormatter registerFormatter,
|
||||
@Nonnull EndLocal endLocal) {
|
||||
super(codeAddress, sortOrder);
|
||||
this.endLocal = endLocal;
|
||||
this.registerFormatter = registerFormatter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||
writer.write(".end local ");
|
||||
registerFormatter.writeTo(writer, endLocal.getRegister());
|
||||
|
||||
String name = endLocal.getName();
|
||||
String type = endLocal.getType();
|
||||
String signature = endLocal.getSignature();
|
||||
if (name != null || type != null || signature != null) {
|
||||
writer.write(" # ");
|
||||
LocalFormatter.writeLocal(writer, name, type, signature);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.baksmali.Adaptors.Debug;
|
||||
|
||||
import org.jf.util.IndentingWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class EndPrologueMethodItem extends DebugMethodItem {
|
||||
public EndPrologueMethodItem(int codeAddress, int sortOrder) {
|
||||
super(codeAddress, sortOrder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||
writer.write(".prologue");
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.baksmali.Adaptors.Debug;
|
||||
|
||||
import org.jf.dexlib2.iface.debug.LineNumber;
|
||||
import org.jf.util.IndentingWriter;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
|
||||
public class LineNumberMethodItem extends DebugMethodItem {
|
||||
private final int lineNumber;
|
||||
|
||||
public LineNumberMethodItem(int codeAddress, int sortOrder, @Nonnull LineNumber lineNumber) {
|
||||
super(codeAddress, sortOrder);
|
||||
this.lineNumber = lineNumber.getLineNumber();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||
writer.write(".line ");
|
||||
writer.printUnsignedIntAsDec(lineNumber);
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.baksmali.Adaptors.Debug;
|
||||
|
||||
import org.jf.baksmali.Adaptors.ReferenceFormatter;
|
||||
import org.jf.util.IndentingWriter;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
|
||||
public class LocalFormatter {
|
||||
/**
|
||||
* Writes out the given local info
|
||||
*
|
||||
* The written string will be something like:
|
||||
*
|
||||
* "localVar":Ljava/lang/String;, "SomeSignature"
|
||||
* "localVar":Ljava/lang/String;
|
||||
* "localVar":V, "SomeSignature"
|
||||
* null:Ljava/lang/String;, "SomeSignature"
|
||||
* null:V, "SomeSignature"
|
||||
*
|
||||
* One of name, type or signature must be non-null
|
||||
*/
|
||||
public static void writeLocal(@Nonnull IndentingWriter writer, @Nullable String name, @Nullable String type,
|
||||
@Nullable String signature) throws IOException {
|
||||
if (name != null) {
|
||||
ReferenceFormatter.writeStringReference(writer, name);
|
||||
} else {
|
||||
writer.write("null");
|
||||
}
|
||||
writer.write(':');
|
||||
if (type != null) {
|
||||
writer.write(type);
|
||||
} else {
|
||||
writer.write("V");
|
||||
}
|
||||
if (signature != null) {
|
||||
writer.write(", ");
|
||||
ReferenceFormatter.writeStringReference(writer, signature);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.baksmali.Adaptors.Debug;
|
||||
|
||||
import org.jf.baksmali.Adaptors.RegisterFormatter;
|
||||
import org.jf.dexlib2.iface.debug.RestartLocal;
|
||||
import org.jf.util.IndentingWriter;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
|
||||
public class RestartLocalMethodItem extends DebugMethodItem {
|
||||
@Nonnull private final RestartLocal restartLocal;
|
||||
@Nonnull private final RegisterFormatter registerFormatter;
|
||||
|
||||
public RestartLocalMethodItem(int codeAddress, int sortOrder, @Nonnull RegisterFormatter registerFormatter,
|
||||
@Nonnull RestartLocal restartLocal) {
|
||||
super(codeAddress, sortOrder);
|
||||
this.restartLocal = restartLocal;
|
||||
this.registerFormatter = registerFormatter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||
writer.write(".restart local ");
|
||||
registerFormatter.writeTo(writer, restartLocal.getRegister());
|
||||
|
||||
String name = restartLocal.getName();
|
||||
String type = restartLocal.getType();
|
||||
String signature = restartLocal.getSignature();
|
||||
if (name != null || type != null || signature != null) {
|
||||
writer.write(" # ");
|
||||
LocalFormatter.writeLocal(writer, name, type, signature);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.baksmali.Adaptors.Debug;
|
||||
|
||||
import org.jf.dexlib2.iface.debug.SetSourceFile;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.jf.util.StringUtils;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
|
||||
public class SetSourceFileMethodItem extends DebugMethodItem {
|
||||
@Nullable private final String sourceFile;
|
||||
|
||||
public SetSourceFileMethodItem(int codeAddress, int sortOrder, @Nonnull SetSourceFile setSourceFile) {
|
||||
super(codeAddress, sortOrder);
|
||||
this.sourceFile = setSourceFile.getSourceFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||
writer.write(".source");
|
||||
|
||||
if (sourceFile != null) {
|
||||
writer.write(" \"");
|
||||
StringUtils.writeEscapedString(writer, sourceFile);
|
||||
writer.write('"');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.baksmali.Adaptors.Debug;
|
||||
|
||||
import org.jf.baksmali.Adaptors.RegisterFormatter;
|
||||
import org.jf.dexlib2.iface.debug.StartLocal;
|
||||
import org.jf.util.IndentingWriter;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
|
||||
public class StartLocalMethodItem extends DebugMethodItem {
|
||||
@Nonnull private final StartLocal startLocal;
|
||||
@Nonnull private final RegisterFormatter registerFormatter;
|
||||
|
||||
public StartLocalMethodItem(int codeAddress, int sortOrder, @Nonnull RegisterFormatter registerFormatter,
|
||||
@Nonnull StartLocal startLocal) {
|
||||
super(codeAddress, sortOrder);
|
||||
this.startLocal = startLocal;
|
||||
this.registerFormatter = registerFormatter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||
writer.write(".local ");
|
||||
registerFormatter.writeTo(writer, startLocal.getRegister());
|
||||
|
||||
String name = startLocal.getName();
|
||||
String type = startLocal.getType();
|
||||
String signature = startLocal.getSignature();
|
||||
|
||||
if (name != null || type != null || signature != null) {
|
||||
writer.write(", ");
|
||||
LocalFormatter.writeLocal(writer, name, type, signature);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,123 +0,0 @@
|
||||
/*
|
||||
* [The "BSD licence"]
|
||||
* Copyright (c) 2010 Ben Gruver (JesusFreke)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. 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.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.jf.baksmali.Adaptors;
|
||||
|
||||
import org.jf.dexlib.Util.Utf8Utils;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.jf.dexlib.CodeItem;
|
||||
import org.jf.dexlib.StringIdItem;
|
||||
import org.jf.dexlib.TypeIdItem;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public abstract class DebugMethodItem extends MethodItem {
|
||||
private final double sortOrder;
|
||||
|
||||
public DebugMethodItem(int codeAddress, double sortOrder) {
|
||||
super(codeAddress);
|
||||
this.sortOrder = sortOrder;
|
||||
}
|
||||
|
||||
public double getSortOrder() {
|
||||
return sortOrder;
|
||||
}
|
||||
|
||||
protected static void writeLine(IndentingWriter writer, int line) throws IOException {
|
||||
writer.write(".line ");
|
||||
writer.printSignedIntAsDec(line);
|
||||
}
|
||||
|
||||
protected static void writeEndPrologue(IndentingWriter writer) throws IOException {
|
||||
writer.write(".prologue");
|
||||
}
|
||||
|
||||
protected static void writeBeginEpilogue(IndentingWriter writer) throws IOException {
|
||||
writer.write(".epilogue");
|
||||
}
|
||||
|
||||
protected static void writeStartLocal(IndentingWriter writer, CodeItem codeItem, int register,
|
||||
StringIdItem name, TypeIdItem type, StringIdItem signature)
|
||||
throws IOException {
|
||||
writer.write(".local ");
|
||||
RegisterFormatter.writeTo(writer, codeItem, register);
|
||||
writer.write(", ");
|
||||
writer.write(name.getStringValue());
|
||||
writer.write(':');
|
||||
writer.write(type.getTypeDescriptor());
|
||||
if (signature != null) {
|
||||
writer.write(",\"");
|
||||
writer.write(signature.getStringValue());
|
||||
writer.write('"');
|
||||
}
|
||||
}
|
||||
|
||||
protected static void writeEndLocal(IndentingWriter writer, CodeItem codeItem, int register, StringIdItem name,
|
||||
TypeIdItem type, StringIdItem signature) throws IOException {
|
||||
writer.write(".end local ");
|
||||
RegisterFormatter.writeTo(writer, codeItem, register);
|
||||
|
||||
if (name != null) {
|
||||
writer.write(" #");
|
||||
writer.write(name.getStringValue());
|
||||
writer.write(':');
|
||||
writer.write(type.getTypeDescriptor());
|
||||
if (signature != null) {
|
||||
writer.write(",\"");
|
||||
writer.write(signature.getStringValue());
|
||||
writer.write('"');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected static void writeRestartLocal(IndentingWriter writer, CodeItem codeItem, int register,
|
||||
StringIdItem name, TypeIdItem type, StringIdItem signature)
|
||||
throws IOException {
|
||||
writer.write(".restart local ");
|
||||
RegisterFormatter.writeTo(writer, codeItem, register);
|
||||
|
||||
if (name != null) {
|
||||
writer.write(" #");
|
||||
writer.write(name.getStringValue());
|
||||
writer.write(':');
|
||||
writer.write(type.getTypeDescriptor());
|
||||
if (signature != null) {
|
||||
writer.write(",\"");
|
||||
writer.write(signature.getStringValue());
|
||||
writer.write('"');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static void writeSetFile(IndentingWriter writer, String fileName) throws IOException {
|
||||
writer.write(".source \"");
|
||||
Utf8Utils.writeEscapedString(writer, fileName);
|
||||
writer.write('"');
|
||||
}
|
||||
}
|
@ -28,32 +28,32 @@
|
||||
|
||||
package org.jf.baksmali.Adaptors.EncodedValue;
|
||||
|
||||
import org.jf.baksmali.Adaptors.ReferenceFormatter;
|
||||
import org.jf.dexlib2.iface.AnnotationElement;
|
||||
import org.jf.dexlib2.iface.value.AnnotationEncodedValue;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.jf.dexlib.EncodedValue.AnnotationEncodedSubValue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
public abstract class AnnotationEncodedValueAdaptor {
|
||||
|
||||
public static void writeTo(IndentingWriter writer, AnnotationEncodedSubValue encodedAnnotation)
|
||||
public static void writeTo(IndentingWriter writer, AnnotationEncodedValue annotationEncodedValue)
|
||||
throws IOException {
|
||||
writer.write(".subannotation ");
|
||||
ReferenceFormatter.writeTypeReference(writer, encodedAnnotation.annotationType);
|
||||
writer.write(annotationEncodedValue.getType());
|
||||
writer.write('\n');
|
||||
|
||||
writeElementsTo(writer, encodedAnnotation);
|
||||
writeElementsTo(writer, annotationEncodedValue.getElements());
|
||||
writer.write(".end subannotation");
|
||||
}
|
||||
|
||||
public static void writeElementsTo(IndentingWriter writer, AnnotationEncodedSubValue encodedAnnotation)
|
||||
throws IOException {
|
||||
public static void writeElementsTo(IndentingWriter writer,
|
||||
Collection<? extends AnnotationElement> annotationElements) throws IOException {
|
||||
writer.indent(4);
|
||||
for (int i=0; i<encodedAnnotation.names.length; i++) {
|
||||
writer.write(encodedAnnotation.names[i].getStringValue());
|
||||
for (AnnotationElement annotationElement: annotationElements) {
|
||||
writer.write(annotationElement.getName());
|
||||
writer.write(" = ");
|
||||
|
||||
EncodedValueAdaptor.writeTo(writer, encodedAnnotation.values[i]);
|
||||
EncodedValueAdaptor.writeTo(writer, annotationElement.getValue());
|
||||
writer.write('\n');
|
||||
}
|
||||
writer.deindent(4);
|
||||
|
@ -29,16 +29,17 @@
|
||||
package org.jf.baksmali.Adaptors.EncodedValue;
|
||||
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.jf.dexlib.EncodedValue.ArrayEncodedValue;
|
||||
import org.jf.dexlib.EncodedValue.EncodedValue;
|
||||
import org.jf.dexlib2.iface.value.ArrayEncodedValue;
|
||||
import org.jf.dexlib2.iface.value.EncodedValue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
public class ArrayEncodedValueAdaptor {
|
||||
public static void writeTo(IndentingWriter writer, ArrayEncodedValue encodedArray) throws IOException {
|
||||
public static void writeTo(IndentingWriter writer, ArrayEncodedValue arrayEncodedValue) throws IOException {
|
||||
writer.write('{');
|
||||
EncodedValue[] values = encodedArray.values;
|
||||
if (values == null || values.length == 0) {
|
||||
Collection<? extends EncodedValue> values = arrayEncodedValue.getValue();
|
||||
if (values.size() == 0) {
|
||||
writer.write('}');
|
||||
return;
|
||||
}
|
||||
@ -46,7 +47,7 @@ public class ArrayEncodedValueAdaptor {
|
||||
writer.write('\n');
|
||||
writer.indent(4);
|
||||
boolean first = true;
|
||||
for (EncodedValue encodedValue: encodedArray.values) {
|
||||
for (EncodedValue encodedValue: values) {
|
||||
if (!first) {
|
||||
writer.write(",\n");
|
||||
}
|
||||
|
@ -29,62 +29,65 @@
|
||||
package org.jf.baksmali.Adaptors.EncodedValue;
|
||||
|
||||
import org.jf.baksmali.Adaptors.ReferenceFormatter;
|
||||
import org.jf.dexlib2.ValueType;
|
||||
import org.jf.dexlib2.iface.value.*;
|
||||
import org.jf.dexlib2.util.ReferenceUtil;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.jf.baksmali.Renderers.*;
|
||||
import org.jf.dexlib.EncodedValue.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public abstract class EncodedValueAdaptor {
|
||||
public static void writeTo(IndentingWriter writer, EncodedValue encodedValue) throws IOException {
|
||||
switch (encodedValue.getValueType()) {
|
||||
case VALUE_ANNOTATION:
|
||||
case ValueType.ANNOTATION:
|
||||
AnnotationEncodedValueAdaptor.writeTo(writer, (AnnotationEncodedValue)encodedValue);
|
||||
return;
|
||||
case VALUE_ARRAY:
|
||||
case ValueType.ARRAY:
|
||||
ArrayEncodedValueAdaptor.writeTo(writer, (ArrayEncodedValue)encodedValue);
|
||||
return;
|
||||
case VALUE_BOOLEAN:
|
||||
BooleanRenderer.writeTo(writer, ((BooleanEncodedValue)encodedValue).value);
|
||||
case ValueType.BOOLEAN:
|
||||
BooleanRenderer.writeTo(writer, ((BooleanEncodedValue)encodedValue).getValue());
|
||||
return;
|
||||
case VALUE_BYTE:
|
||||
ByteRenderer.writeTo(writer, ((ByteEncodedValue)encodedValue).value);
|
||||
case ValueType.BYTE:
|
||||
ByteRenderer.writeTo(writer, ((ByteEncodedValue)encodedValue).getValue());
|
||||
return;
|
||||
case VALUE_CHAR:
|
||||
CharRenderer.writeTo(writer, ((CharEncodedValue)encodedValue).value);
|
||||
case ValueType.CHAR:
|
||||
CharRenderer.writeTo(writer, ((CharEncodedValue)encodedValue).getValue());
|
||||
return;
|
||||
case VALUE_DOUBLE:
|
||||
DoubleRenderer.writeTo(writer, ((DoubleEncodedValue)encodedValue).value);
|
||||
case ValueType.DOUBLE:
|
||||
DoubleRenderer.writeTo(writer, ((DoubleEncodedValue)encodedValue).getValue());
|
||||
return;
|
||||
case VALUE_ENUM:
|
||||
EnumEncodedValueAdaptor.writeTo(writer, ((EnumEncodedValue)encodedValue).value);
|
||||
case ValueType.ENUM:
|
||||
writer.write(".enum ");
|
||||
ReferenceUtil.writeFieldDescriptor(writer, ((EnumEncodedValue)encodedValue).getValue());
|
||||
return;
|
||||
case VALUE_FIELD:
|
||||
ReferenceFormatter.writeFieldReference(writer, ((FieldEncodedValue)encodedValue).value);
|
||||
case ValueType.FIELD:
|
||||
ReferenceUtil.writeFieldDescriptor(writer, ((FieldEncodedValue)encodedValue).getValue());
|
||||
return;
|
||||
case VALUE_FLOAT:
|
||||
FloatRenderer.writeTo(writer, ((FloatEncodedValue)encodedValue).value);
|
||||
case ValueType.FLOAT:
|
||||
FloatRenderer.writeTo(writer, ((FloatEncodedValue)encodedValue).getValue());
|
||||
return;
|
||||
case VALUE_INT:
|
||||
IntegerRenderer.writeTo(writer, ((IntEncodedValue)encodedValue).value);
|
||||
case ValueType.INT:
|
||||
IntegerRenderer.writeTo(writer, ((IntEncodedValue)encodedValue).getValue());
|
||||
return;
|
||||
case VALUE_LONG:
|
||||
LongRenderer.writeTo(writer, ((LongEncodedValue)encodedValue).value);
|
||||
case ValueType.LONG:
|
||||
LongRenderer.writeTo(writer, ((LongEncodedValue)encodedValue).getValue());
|
||||
return;
|
||||
case VALUE_METHOD:
|
||||
ReferenceFormatter.writeMethodReference(writer, ((MethodEncodedValue)encodedValue).value);
|
||||
case ValueType.METHOD:
|
||||
ReferenceUtil.writeMethodDescriptor(writer, ((MethodEncodedValue)encodedValue).getValue());
|
||||
return;
|
||||
case VALUE_NULL:
|
||||
case ValueType.NULL:
|
||||
writer.write("null");
|
||||
return;
|
||||
case VALUE_SHORT:
|
||||
ShortRenderer.writeTo(writer, ((ShortEncodedValue)encodedValue).value);
|
||||
case ValueType.SHORT:
|
||||
ShortRenderer.writeTo(writer, ((ShortEncodedValue)encodedValue).getValue());
|
||||
return;
|
||||
case VALUE_STRING:
|
||||
ReferenceFormatter.writeStringReference(writer, ((StringEncodedValue)encodedValue).value);
|
||||
case ValueType.STRING:
|
||||
ReferenceFormatter.writeStringReference(writer, ((StringEncodedValue)encodedValue).getValue());
|
||||
return;
|
||||
case VALUE_TYPE:
|
||||
ReferenceFormatter.writeTypeReference(writer, ((TypeEncodedValue)encodedValue).value);
|
||||
case ValueType.TYPE:
|
||||
writer.write(((TypeEncodedValue)encodedValue).getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,42 +0,0 @@
|
||||
/*
|
||||
* [The "BSD licence"]
|
||||
* Copyright (c) 2010 Ben Gruver (JesusFreke)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. 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.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.jf.baksmali.Adaptors.EncodedValue;
|
||||
|
||||
import org.jf.baksmali.Adaptors.ReferenceFormatter;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.jf.dexlib.FieldIdItem;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class EnumEncodedValueAdaptor {
|
||||
public static void writeTo(IndentingWriter writer, FieldIdItem item) throws IOException {
|
||||
writer.write(".enum ");
|
||||
ReferenceFormatter.writeFieldReference(writer, item);
|
||||
}
|
||||
}
|
@ -28,11 +28,15 @@
|
||||
|
||||
package org.jf.baksmali.Adaptors;
|
||||
|
||||
import org.jf.baksmali.baksmaliOptions;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class EndTryLabelMethodItem extends LabelMethodItem {
|
||||
private int endTryAddress;
|
||||
|
||||
public EndTryLabelMethodItem(int codeAddress, int endTryAddress) {
|
||||
super(codeAddress, "try_end_");
|
||||
public EndTryLabelMethodItem(@Nonnull baksmaliOptions options, int codeAddress, int endTryAddress) {
|
||||
super(options, codeAddress, "try_end_");
|
||||
this.endTryAddress = endTryAddress;
|
||||
}
|
||||
|
||||
|
@ -29,40 +29,39 @@
|
||||
package org.jf.baksmali.Adaptors;
|
||||
|
||||
import org.jf.baksmali.Adaptors.EncodedValue.EncodedValueAdaptor;
|
||||
import org.jf.dexlib2.AccessFlags;
|
||||
import org.jf.dexlib2.iface.Annotation;
|
||||
import org.jf.dexlib2.iface.Field;
|
||||
import org.jf.dexlib2.iface.value.EncodedValue;
|
||||
import org.jf.dexlib2.util.EncodedValueUtils;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.jf.dexlib.AnnotationSetItem;
|
||||
import org.jf.dexlib.ClassDataItem;
|
||||
import org.jf.dexlib.EncodedValue.EncodedValue;
|
||||
import org.jf.dexlib.EncodedValue.NullEncodedValue;
|
||||
import org.jf.dexlib.Util.AccessFlags;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
public class FieldDefinition {
|
||||
public static void writeTo(IndentingWriter writer, ClassDataItem.EncodedField encodedField,
|
||||
EncodedValue initialValue, AnnotationSetItem annotationSet,
|
||||
boolean setInStaticConstructor) throws IOException {
|
||||
|
||||
String fieldTypeDescriptor = encodedField.field.getFieldType().getTypeDescriptor();
|
||||
public static void writeTo(IndentingWriter writer, Field field, boolean setInStaticConstructor) throws IOException {
|
||||
EncodedValue initialValue = field.getInitialValue();
|
||||
int accessFlags = field.getAccessFlags();
|
||||
|
||||
if (setInStaticConstructor &&
|
||||
encodedField.isStatic() &&
|
||||
(encodedField.accessFlags & AccessFlags.FINAL.getValue()) != 0 &&
|
||||
initialValue != null &&
|
||||
(
|
||||
//it's a primitive type, or it's an array/reference type and the initial value isn't null
|
||||
fieldTypeDescriptor.length() == 1 ||
|
||||
initialValue != NullEncodedValue.NullValue
|
||||
)) {
|
||||
|
||||
writer.write("#the value of this static final field might be set in the static constructor\n");
|
||||
AccessFlags.STATIC.isSet(accessFlags) &&
|
||||
AccessFlags.FINAL.isSet(accessFlags) &&
|
||||
initialValue != null) {
|
||||
if (!EncodedValueUtils.isDefaultValue(initialValue)) {
|
||||
writer.write("# The value of this static final field might be set in the static constructor\n");
|
||||
} else {
|
||||
// don't write out the default initial value for static final fields that get set in the static
|
||||
// constructor
|
||||
initialValue = null;
|
||||
}
|
||||
}
|
||||
|
||||
writer.write(".field ");
|
||||
writeAccessFlags(writer, encodedField);
|
||||
writer.write(encodedField.field.getFieldName().getStringValue());
|
||||
writeAccessFlags(writer, field.getAccessFlags());
|
||||
writer.write(field.getName());
|
||||
writer.write(':');
|
||||
writer.write(encodedField.field.getFieldType().getTypeDescriptor());
|
||||
writer.write(field.getType());
|
||||
if (initialValue != null) {
|
||||
writer.write(" = ");
|
||||
EncodedValueAdaptor.writeTo(writer, initialValue);
|
||||
@ -70,17 +69,17 @@ public class FieldDefinition {
|
||||
|
||||
writer.write('\n');
|
||||
|
||||
if (annotationSet != null) {
|
||||
Collection<? extends Annotation> annotations = field.getAnnotations();
|
||||
if (annotations.size() > 0) {
|
||||
writer.indent(4);
|
||||
AnnotationFormatter.writeTo(writer, annotationSet);
|
||||
AnnotationFormatter.writeTo(writer, annotations);
|
||||
writer.deindent(4);
|
||||
writer.write(".end field\n");
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeAccessFlags(IndentingWriter writer, ClassDataItem.EncodedField encodedField)
|
||||
throws IOException {
|
||||
for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForField(encodedField.accessFlags)) {
|
||||
private static void writeAccessFlags(IndentingWriter writer, int accessFlags) throws IOException {
|
||||
for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForField(accessFlags)) {
|
||||
writer.write(accessFlag.toString());
|
||||
writer.write(' ');
|
||||
}
|
||||
|
@ -28,36 +28,44 @@
|
||||
|
||||
package org.jf.baksmali.Adaptors.Format;
|
||||
|
||||
import org.jf.baksmali.Adaptors.MethodDefinition;
|
||||
import org.jf.baksmali.Renderers.LongRenderer;
|
||||
import org.jf.dexlib2.iface.instruction.formats.ArrayPayload;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.jf.baksmali.Renderers.ByteRenderer;
|
||||
import org.jf.dexlib.Code.Format.ArrayDataPseudoInstruction;
|
||||
import org.jf.dexlib.CodeItem;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class ArrayDataMethodItem extends InstructionMethodItem<ArrayDataPseudoInstruction> {
|
||||
public ArrayDataMethodItem(CodeItem codeItem, int codeAddress, ArrayDataPseudoInstruction instruction) {
|
||||
super(codeItem, codeAddress, instruction);
|
||||
public class ArrayDataMethodItem extends InstructionMethodItem<ArrayPayload> {
|
||||
public ArrayDataMethodItem(MethodDefinition methodDef, int codeAddress, ArrayPayload instruction) {
|
||||
super(methodDef, codeAddress, instruction);
|
||||
}
|
||||
|
||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||
writer.write(".array-data 0x");
|
||||
writer.printUnsignedLongAsHex(instruction.getElementWidth());
|
||||
int elementWidth = instruction.getElementWidth();
|
||||
|
||||
writer.write(".array-data ");
|
||||
writer.printSignedIntAsDec(instruction.getElementWidth());
|
||||
writer.write('\n');
|
||||
|
||||
writer.indent(4);
|
||||
Iterator<ArrayDataPseudoInstruction.ArrayElement> iterator = instruction.getElements();
|
||||
while (iterator.hasNext()) {
|
||||
ArrayDataPseudoInstruction.ArrayElement element = iterator.next();
|
||||
|
||||
for (int i=0; i<element.elementWidth; i++) {
|
||||
if (i!=0) {
|
||||
writer.write(' ');
|
||||
}
|
||||
ByteRenderer.writeUnsignedTo(writer, element.buffer[element.bufferIndex+i]);
|
||||
}
|
||||
writer.write('\n');
|
||||
List<Number> elements = instruction.getArrayElements();
|
||||
|
||||
String suffix = "";
|
||||
switch (elementWidth) {
|
||||
case 1:
|
||||
suffix = "t";
|
||||
break;
|
||||
case 2:
|
||||
suffix = "s";
|
||||
break;
|
||||
}
|
||||
|
||||
for (Number number: elements) {
|
||||
LongRenderer.writeSignedIntOrLongTo(writer, number.longValue());
|
||||
writer.write(suffix);
|
||||
writer.write("\n");
|
||||
}
|
||||
writer.deindent(4);
|
||||
writer.write(".end array-data");
|
||||
|
@ -28,26 +28,26 @@
|
||||
|
||||
package org.jf.baksmali.Adaptors.Format;
|
||||
|
||||
import org.jf.baksmali.Adaptors.MethodDefinition;
|
||||
import org.jf.baksmali.Adaptors.MethodItem;
|
||||
import org.jf.baksmali.Adaptors.ReferenceFormatter;
|
||||
import org.jf.baksmali.Adaptors.RegisterFormatter;
|
||||
import org.jf.dexlib.Code.Format.Instruction20bc;
|
||||
import org.jf.dexlib.Code.Format.UnknownInstruction;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.jf.baksmali.Renderers.LongRenderer;
|
||||
import org.jf.dexlib.Code.*;
|
||||
import org.jf.dexlib.CodeItem;
|
||||
import org.jf.dexlib.Item;
|
||||
import org.jf.dexlib2.VerificationError;
|
||||
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.util.IndentingWriter;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
|
||||
public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
||||
protected final CodeItem codeItem;
|
||||
protected final T instruction;
|
||||
@Nonnull protected final MethodDefinition methodDef;
|
||||
@Nonnull protected final T instruction;
|
||||
|
||||
public InstructionMethodItem(CodeItem codeItem, int codeAddress, T instruction) {
|
||||
public InstructionMethodItem(@Nonnull MethodDefinition methodDef, int codeAddress, @Nonnull T instruction) {
|
||||
super(codeAddress);
|
||||
this.codeItem = codeItem;
|
||||
this.methodDef = methodDef;
|
||||
this.instruction = instruction;
|
||||
}
|
||||
|
||||
@ -58,7 +58,7 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
||||
|
||||
@Override
|
||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||
switch (instruction.getFormat()) {
|
||||
switch (instruction.getOpcode().format) {
|
||||
case Format10t:
|
||||
writeOpcode(writer);
|
||||
writer.write(' ');
|
||||
@ -67,7 +67,7 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
||||
case Format10x:
|
||||
if (instruction instanceof UnknownInstruction) {
|
||||
writer.write("#unknown opcode: 0x");
|
||||
writer.printUnsignedLongAsHex(((UnknownInstruction) instruction).getOriginalOpcode() & 0xFFFF);
|
||||
writer.printUnsignedLongAsHex(((UnknownInstruction)instruction).getOriginalOpcode());
|
||||
writer.write('\n');
|
||||
}
|
||||
writeOpcode(writer);
|
||||
@ -106,14 +106,14 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
||||
return true;
|
||||
case Format21c:
|
||||
case Format31c:
|
||||
case Format41c:
|
||||
writeOpcode(writer);
|
||||
writer.write(' ');
|
||||
writeFirstRegister(writer);
|
||||
writer.write(", ");
|
||||
writeReference(writer);
|
||||
return true;
|
||||
case Format21h:
|
||||
case Format21ih:
|
||||
case Format21lh:
|
||||
case Format21s:
|
||||
case Format31i:
|
||||
case Format51l:
|
||||
@ -142,7 +142,6 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
||||
writeLiteral(writer);
|
||||
return true;
|
||||
case Format22c:
|
||||
case Format52c:
|
||||
writeOpcode(writer);
|
||||
writer.write(' ');
|
||||
writeFirstRegister(writer);
|
||||
@ -208,7 +207,6 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
||||
writeVtableIndex(writer);
|
||||
return true;
|
||||
case Format3rc:
|
||||
case Format5rc:
|
||||
writeOpcode(writer);
|
||||
writer.write(' ');
|
||||
writeInvokeRangeRegisters(writer);
|
||||
@ -235,21 +233,21 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
||||
}
|
||||
|
||||
protected void writeOpcode(IndentingWriter writer) throws IOException {
|
||||
writer.write(instruction.opcode.name);
|
||||
writer.write(instruction.getOpcode().name);
|
||||
}
|
||||
|
||||
protected void writeTargetLabel(IndentingWriter writer) throws IOException {
|
||||
//this method is overrided by OffsetInstructionMethodItem, and should only be called for the formats that
|
||||
//this method is overridden by OffsetInstructionMethodItem, and should only be called for the formats that
|
||||
//have a target
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
protected void writeRegister(IndentingWriter writer, int registerNumber) throws IOException {
|
||||
RegisterFormatter.writeTo(writer, codeItem, registerNumber);
|
||||
methodDef.registerFormatter.writeTo(writer, registerNumber);
|
||||
}
|
||||
|
||||
protected void writeFirstRegister(IndentingWriter writer) throws IOException {
|
||||
writeRegister(writer, ((SingleRegisterInstruction)instruction).getRegisterA());
|
||||
writeRegister(writer, ((OneRegisterInstruction)instruction).getRegisterA());
|
||||
}
|
||||
|
||||
protected void writeSecondRegister(IndentingWriter writer) throws IOException {
|
||||
@ -257,40 +255,42 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
||||
}
|
||||
|
||||
protected void writeThirdRegister(IndentingWriter writer) throws IOException {
|
||||
writeRegister(writer, ((ThreeRegisterInstruction)instruction).getRegisterC());
|
||||
writeRegister(writer, ((ThreeRegisterInstruction) instruction).getRegisterC());
|
||||
}
|
||||
|
||||
protected void writeInvokeRegisters(IndentingWriter writer) throws IOException {
|
||||
FiveRegisterInstruction instruction = (FiveRegisterInstruction)this.instruction;
|
||||
final int regCount = instruction.getRegCount();
|
||||
final int regCount = instruction.getRegisterCount();
|
||||
|
||||
writer.write('{');
|
||||
switch (regCount) {
|
||||
case 1:
|
||||
writeRegister(writer, instruction.getRegisterD());
|
||||
writeRegister(writer, instruction.getRegisterC());
|
||||
break;
|
||||
case 2:
|
||||
writeRegister(writer, instruction.getRegisterD());
|
||||
writeRegister(writer, instruction.getRegisterC());
|
||||
writer.write(", ");
|
||||
writeRegister(writer, instruction.getRegisterE());
|
||||
writeRegister(writer, instruction.getRegisterD());
|
||||
break;
|
||||
case 3:
|
||||
writeRegister(writer, instruction.getRegisterC());
|
||||
writer.write(", ");
|
||||
writeRegister(writer, instruction.getRegisterD());
|
||||
writer.write(", ");
|
||||
writeRegister(writer, instruction.getRegisterE());
|
||||
writer.write(", ");
|
||||
writeRegister(writer, instruction.getRegisterF());
|
||||
break;
|
||||
case 4:
|
||||
writeRegister(writer, instruction.getRegisterC());
|
||||
writer.write(", ");
|
||||
writeRegister(writer, instruction.getRegisterD());
|
||||
writer.write(", ");
|
||||
writeRegister(writer, instruction.getRegisterE());
|
||||
writer.write(", ");
|
||||
writeRegister(writer, instruction.getRegisterF());
|
||||
writer.write(", ");
|
||||
writeRegister(writer, instruction.getRegisterG());
|
||||
break;
|
||||
case 5:
|
||||
writeRegister(writer, instruction.getRegisterC());
|
||||
writer.write(", ");
|
||||
writeRegister(writer, instruction.getRegisterD());
|
||||
writer.write(", ");
|
||||
writeRegister(writer, instruction.getRegisterE());
|
||||
@ -298,8 +298,6 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
||||
writeRegister(writer, instruction.getRegisterF());
|
||||
writer.write(", ");
|
||||
writeRegister(writer, instruction.getRegisterG());
|
||||
writer.write(", ");
|
||||
writeRegister(writer, instruction.getRegisterA());
|
||||
break;
|
||||
}
|
||||
writer.write('}');
|
||||
@ -308,41 +306,42 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
||||
protected void writeInvokeRangeRegisters(IndentingWriter writer) throws IOException {
|
||||
RegisterRangeInstruction instruction = (RegisterRangeInstruction)this.instruction;
|
||||
|
||||
int regCount = instruction.getRegCount();
|
||||
int regCount = instruction.getRegisterCount();
|
||||
if (regCount == 0) {
|
||||
writer.write("{}");
|
||||
} else {
|
||||
int startRegister = instruction.getStartRegister();
|
||||
RegisterFormatter.writeRegisterRange(writer, codeItem, startRegister, startRegister+regCount-1);
|
||||
methodDef.registerFormatter.writeRegisterRange(writer, startRegister, startRegister+regCount-1);
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeLiteral(IndentingWriter writer) throws IOException {
|
||||
LongRenderer.writeSignedIntOrLongTo(writer, ((LiteralInstruction)instruction).getLiteral());
|
||||
LongRenderer.writeSignedIntOrLongTo(writer, ((WideLiteralInstruction)instruction).getWideLiteral());
|
||||
}
|
||||
|
||||
|
||||
protected void writeFieldOffset(IndentingWriter writer) throws IOException {
|
||||
writer.write("field@0x");
|
||||
writer.printUnsignedLongAsHex(((OdexedFieldAccess) instruction).getFieldOffset());
|
||||
writer.printUnsignedLongAsHex(((FieldOffsetInstruction)instruction).getFieldOffset());
|
||||
}
|
||||
|
||||
protected void writeInlineIndex(IndentingWriter writer) throws IOException {
|
||||
writer.write("inline@0x");
|
||||
writer.printUnsignedLongAsHex(((OdexedInvokeInline) instruction).getInlineIndex());
|
||||
writer.write("inline@");
|
||||
writer.printSignedIntAsDec(((InlineIndexInstruction)instruction).getInlineIndex());
|
||||
}
|
||||
|
||||
protected void writeVtableIndex(IndentingWriter writer) throws IOException {
|
||||
writer.write("vtable@0x");
|
||||
writer.printUnsignedLongAsHex(((OdexedInvokeVirtual) instruction).getVtableIndex());
|
||||
writer.write("vtable@");
|
||||
writer.printSignedIntAsDec(((VtableIndexInstruction)instruction).getVtableIndex());
|
||||
}
|
||||
|
||||
protected void writeReference(IndentingWriter writer) throws IOException {
|
||||
Item item = ((InstructionWithReference)instruction).getReferencedItem();
|
||||
ReferenceFormatter.writeReference(writer, item);
|
||||
ReferenceFormatter.writeReference(writer, instruction.getOpcode().referenceType,
|
||||
((ReferenceInstruction)instruction).getReference());
|
||||
}
|
||||
|
||||
protected void writeVerificationErrorType(IndentingWriter writer) throws IOException {
|
||||
VerificationErrorType validationErrorType = ((Instruction20bc)instruction).getValidationErrorType();
|
||||
writer.write(validationErrorType.getName());
|
||||
int verificationError = ((Instruction20bc)instruction).getVerificationError();
|
||||
writer.write(VerificationError.getVerificationErrorName(verificationError));
|
||||
}
|
||||
}
|
||||
|
@ -29,37 +29,39 @@
|
||||
package org.jf.baksmali.Adaptors.Format;
|
||||
|
||||
import org.jf.baksmali.Adaptors.MethodDefinition;
|
||||
import org.jf.dexlib.Code.Format.*;
|
||||
import org.jf.dexlib.Code.Instruction;
|
||||
import org.jf.dexlib.Code.OffsetInstruction;
|
||||
import org.jf.dexlib.CodeItem;
|
||||
import org.jf.dexlib2.analysis.UnresolvedOdexInstruction;
|
||||
import org.jf.dexlib2.iface.instruction.Instruction;
|
||||
import org.jf.dexlib2.iface.instruction.OffsetInstruction;
|
||||
import org.jf.dexlib2.iface.instruction.formats.ArrayPayload;
|
||||
import org.jf.dexlib2.iface.instruction.formats.PackedSwitchPayload;
|
||||
import org.jf.dexlib2.iface.instruction.formats.SparseSwitchPayload;
|
||||
|
||||
public class InstructionMethodItemFactory {
|
||||
private InstructionMethodItemFactory() {
|
||||
}
|
||||
|
||||
public static InstructionMethodItem makeInstructionFormatMethodItem(
|
||||
MethodDefinition methodDefinition, CodeItem codeItem, int codeAddress, Instruction instruction) {
|
||||
MethodDefinition methodDef, int codeAddress, Instruction instruction) {
|
||||
|
||||
if (instruction instanceof OffsetInstruction) {
|
||||
return new OffsetInstructionFormatMethodItem(methodDefinition.getLabelCache(), codeItem,
|
||||
codeAddress, (OffsetInstruction)instruction);
|
||||
return new OffsetInstructionFormatMethodItem(methodDef.classDef.options, methodDef, codeAddress,
|
||||
(OffsetInstruction)instruction);
|
||||
}
|
||||
|
||||
switch (instruction.getFormat()) {
|
||||
case ArrayData:
|
||||
return new ArrayDataMethodItem(codeItem, codeAddress,
|
||||
(ArrayDataPseudoInstruction)instruction);
|
||||
case PackedSwitchData:
|
||||
return new PackedSwitchMethodItem(methodDefinition, codeItem, codeAddress,
|
||||
(PackedSwitchDataPseudoInstruction)instruction);
|
||||
case SparseSwitchData:
|
||||
return new SparseSwitchMethodItem(methodDefinition, codeItem, codeAddress,
|
||||
(SparseSwitchDataPseudoInstruction)instruction);
|
||||
case UnresolvedOdexInstruction:
|
||||
return new UnresolvedOdexInstructionMethodItem(codeItem, codeAddress,
|
||||
(UnresolvedOdexInstruction)instruction);
|
||||
if (instruction instanceof UnresolvedOdexInstruction) {
|
||||
return new UnresolvedOdexInstructionMethodItem(methodDef, codeAddress,
|
||||
(UnresolvedOdexInstruction)instruction);
|
||||
}
|
||||
|
||||
switch (instruction.getOpcode().format) {
|
||||
case ArrayPayload:
|
||||
return new ArrayDataMethodItem(methodDef, codeAddress, (ArrayPayload)instruction);
|
||||
case PackedSwitchPayload:
|
||||
return new PackedSwitchMethodItem(methodDef, codeAddress, (PackedSwitchPayload)instruction);
|
||||
case SparseSwitchPayload:
|
||||
return new SparseSwitchMethodItem(methodDef, codeAddress, (SparseSwitchPayload)instruction);
|
||||
default:
|
||||
return new InstructionMethodItem<Instruction>(codeItem, codeAddress, instruction);
|
||||
return new InstructionMethodItem<Instruction>(methodDef, codeAddress, instruction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,22 +30,23 @@ package org.jf.baksmali.Adaptors.Format;
|
||||
|
||||
import org.jf.baksmali.Adaptors.LabelMethodItem;
|
||||
import org.jf.baksmali.Adaptors.MethodDefinition;
|
||||
import org.jf.baksmali.baksmaliOptions;
|
||||
import org.jf.dexlib2.Opcode;
|
||||
import org.jf.dexlib2.iface.instruction.OffsetInstruction;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.jf.dexlib.Code.OffsetInstruction;
|
||||
import org.jf.dexlib.Code.Opcode;
|
||||
import org.jf.dexlib.CodeItem;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
|
||||
public class OffsetInstructionFormatMethodItem extends InstructionMethodItem<OffsetInstruction> {
|
||||
protected LabelMethodItem label;
|
||||
|
||||
public OffsetInstructionFormatMethodItem(MethodDefinition.LabelCache labelCache, CodeItem codeItem, int codeAddress,
|
||||
OffsetInstruction instruction) {
|
||||
super(codeItem, codeAddress, instruction);
|
||||
public OffsetInstructionFormatMethodItem(@Nonnull baksmaliOptions options, @Nonnull MethodDefinition methodDef,
|
||||
int codeAddress, OffsetInstruction instruction) {
|
||||
super(methodDef, codeAddress, instruction);
|
||||
|
||||
label = new LabelMethodItem(codeAddress + instruction.getTargetAddressOffset(), getLabelPrefix());
|
||||
label = labelCache.internLabel(label);
|
||||
label = new LabelMethodItem(options, codeAddress + instruction.getCodeOffset(), getLabelPrefix());
|
||||
label = methodDef.getLabelCache().internLabel(label);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -58,7 +59,8 @@ public class OffsetInstructionFormatMethodItem extends InstructionMethodItem<Off
|
||||
}
|
||||
|
||||
private String getLabelPrefix() {
|
||||
switch (instruction.getFormat()) {
|
||||
Opcode opcode = instruction.getOpcode();
|
||||
switch (opcode.format) {
|
||||
case Format10t:
|
||||
case Format20t:
|
||||
case Format30t:
|
||||
@ -67,13 +69,13 @@ public class OffsetInstructionFormatMethodItem extends InstructionMethodItem<Off
|
||||
case Format22t:
|
||||
return "cond_";
|
||||
case Format31t:
|
||||
if (instruction.opcode == Opcode.FILL_ARRAY_DATA) {
|
||||
if (opcode == Opcode.FILL_ARRAY_DATA) {
|
||||
return "array_";
|
||||
}
|
||||
if (instruction.opcode == Opcode.PACKED_SWITCH) {
|
||||
if (opcode == Opcode.PACKED_SWITCH) {
|
||||
return "pswitch_data_";
|
||||
}
|
||||
assert instruction.opcode == Opcode.SPARSE_SWITCH;
|
||||
// Opcode.SPARSE_SWITCH;
|
||||
return "sswitch_data_";
|
||||
}
|
||||
|
||||
|
@ -30,55 +30,56 @@ package org.jf.baksmali.Adaptors.Format;
|
||||
|
||||
import org.jf.baksmali.Adaptors.LabelMethodItem;
|
||||
import org.jf.baksmali.Adaptors.MethodDefinition;
|
||||
import org.jf.dexlib2.iface.instruction.SwitchElement;
|
||||
import org.jf.dexlib2.iface.instruction.formats.PackedSwitchPayload;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.jf.baksmali.Renderers.IntegerRenderer;
|
||||
import org.jf.dexlib.Code.Format.PackedSwitchDataPseudoInstruction;
|
||||
import org.jf.dexlib.CodeItem;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class PackedSwitchMethodItem extends InstructionMethodItem<PackedSwitchDataPseudoInstruction> {
|
||||
public class PackedSwitchMethodItem extends InstructionMethodItem<PackedSwitchPayload> {
|
||||
private final List<PackedSwitchTarget> targets;
|
||||
private final int firstKey;
|
||||
|
||||
public PackedSwitchMethodItem(MethodDefinition methodDefinition, CodeItem codeItem, int codeAddress,
|
||||
PackedSwitchDataPseudoInstruction instruction) {
|
||||
super(codeItem, codeAddress, instruction);
|
||||
public PackedSwitchMethodItem(MethodDefinition methodDef, int codeAddress, PackedSwitchPayload instruction) {
|
||||
super(methodDef, codeAddress, instruction);
|
||||
|
||||
int baseCodeAddress = methodDefinition.getPackedSwitchBaseAddress(codeAddress);
|
||||
int baseCodeAddress = methodDef.getPackedSwitchBaseAddress(codeAddress);
|
||||
|
||||
targets = new ArrayList<PackedSwitchTarget>();
|
||||
Iterator<PackedSwitchDataPseudoInstruction.PackedSwitchTarget> iterator = instruction.iterateKeysAndTargets();
|
||||
|
||||
boolean first = true;
|
||||
//TODO: does dalvik allow switc payloads with no cases?
|
||||
int firstKey = 0;
|
||||
if (baseCodeAddress >= 0) {
|
||||
while (iterator.hasNext()) {
|
||||
PackedSwitchDataPseudoInstruction.PackedSwitchTarget target = iterator.next();
|
||||
PackedSwitchLabelTarget packedSwitchLabelTarget = new PackedSwitchLabelTarget();
|
||||
|
||||
|
||||
LabelMethodItem label = new LabelMethodItem(baseCodeAddress + target.targetAddressOffset, "pswitch_");
|
||||
label = methodDefinition.getLabelCache().internLabel(label);
|
||||
packedSwitchLabelTarget.Target = label;
|
||||
targets.add(packedSwitchLabelTarget);
|
||||
for (SwitchElement switchElement: instruction.getSwitchElements()) {
|
||||
if (first) {
|
||||
firstKey = switchElement.getKey();
|
||||
first = false;
|
||||
}
|
||||
LabelMethodItem label = methodDef.getLabelCache().internLabel(
|
||||
new LabelMethodItem(methodDef.classDef.options, baseCodeAddress + switchElement.getOffset(),
|
||||
"pswitch_"));
|
||||
targets.add(new PackedSwitchLabelTarget(label));
|
||||
}
|
||||
} else {
|
||||
while (iterator.hasNext()) {
|
||||
PackedSwitchDataPseudoInstruction.PackedSwitchTarget target = iterator.next();
|
||||
PackedSwitchOffsetTarget packedSwitchOffsetTarget = new PackedSwitchOffsetTarget();
|
||||
|
||||
|
||||
packedSwitchOffsetTarget.Target = target.targetAddressOffset;
|
||||
targets.add(packedSwitchOffsetTarget);
|
||||
for (SwitchElement switchElement: instruction.getSwitchElements()) {
|
||||
if (first) {
|
||||
firstKey = switchElement.getKey();
|
||||
first = false;
|
||||
}
|
||||
targets.add(new PackedSwitchOffsetTarget(switchElement.getOffset()));
|
||||
}
|
||||
}
|
||||
this.firstKey = firstKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||
writer.write(".packed-switch ");
|
||||
IntegerRenderer.writeTo(writer, instruction.getFirstKey());
|
||||
IntegerRenderer.writeTo(writer, firstKey);
|
||||
writer.indent(4);
|
||||
writer.write('\n');
|
||||
for (PackedSwitchTarget target: targets) {
|
||||
@ -95,19 +96,25 @@ public class PackedSwitchMethodItem extends InstructionMethodItem<PackedSwitchDa
|
||||
}
|
||||
|
||||
private static class PackedSwitchLabelTarget extends PackedSwitchTarget {
|
||||
public LabelMethodItem Target;
|
||||
private final LabelMethodItem target;
|
||||
public PackedSwitchLabelTarget(LabelMethodItem target) {
|
||||
this.target = target;
|
||||
}
|
||||
public void writeTargetTo(IndentingWriter writer) throws IOException {
|
||||
Target.writeTo(writer);
|
||||
target.writeTo(writer);
|
||||
}
|
||||
}
|
||||
|
||||
private static class PackedSwitchOffsetTarget extends PackedSwitchTarget {
|
||||
public int Target;
|
||||
private final int target;
|
||||
public PackedSwitchOffsetTarget(int target) {
|
||||
this.target = target;
|
||||
}
|
||||
public void writeTargetTo(IndentingWriter writer) throws IOException {
|
||||
if (Target >= 0) {
|
||||
if (target >= 0) {
|
||||
writer.write('+');
|
||||
}
|
||||
writer.printSignedIntAsDec(Target);
|
||||
writer.printSignedIntAsDec(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,48 +30,35 @@ package org.jf.baksmali.Adaptors.Format;
|
||||
|
||||
import org.jf.baksmali.Adaptors.LabelMethodItem;
|
||||
import org.jf.baksmali.Adaptors.MethodDefinition;
|
||||
import org.jf.dexlib2.iface.instruction.SwitchElement;
|
||||
import org.jf.dexlib2.iface.instruction.formats.SparseSwitchPayload;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.jf.baksmali.Renderers.IntegerRenderer;
|
||||
import org.jf.dexlib.Code.Format.SparseSwitchDataPseudoInstruction;
|
||||
import org.jf.dexlib.CodeItem;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class SparseSwitchMethodItem extends InstructionMethodItem<SparseSwitchDataPseudoInstruction> {
|
||||
public class SparseSwitchMethodItem extends InstructionMethodItem<SparseSwitchPayload> {
|
||||
private final List<SparseSwitchTarget> targets;
|
||||
|
||||
public SparseSwitchMethodItem(MethodDefinition methodDefinition, CodeItem codeItem, int codeAddress,
|
||||
SparseSwitchDataPseudoInstruction instruction) {
|
||||
super(codeItem, codeAddress, instruction);
|
||||
public SparseSwitchMethodItem(MethodDefinition methodDef, int codeAddress, SparseSwitchPayload instruction) {
|
||||
super(methodDef, codeAddress, instruction);
|
||||
|
||||
int baseCodeAddress = methodDefinition.getSparseSwitchBaseAddress(codeAddress);
|
||||
int baseCodeAddress = methodDef.getSparseSwitchBaseAddress(codeAddress);
|
||||
|
||||
targets = new ArrayList<SparseSwitchTarget>();
|
||||
Iterator<SparseSwitchDataPseudoInstruction.SparseSwitchTarget> iterator = instruction.iterateKeysAndTargets();
|
||||
if (baseCodeAddress >= 0) {
|
||||
while (iterator.hasNext()) {
|
||||
SparseSwitchDataPseudoInstruction.SparseSwitchTarget target = iterator.next();
|
||||
SparseSwitchLabelTarget sparseSwitchLabelTarget = new SparseSwitchLabelTarget();
|
||||
sparseSwitchLabelTarget.Key = target.key;
|
||||
|
||||
LabelMethodItem label = new LabelMethodItem(baseCodeAddress + target.targetAddressOffset, "sswitch_");
|
||||
label = methodDefinition.getLabelCache().internLabel(label);
|
||||
sparseSwitchLabelTarget.Target = label;
|
||||
|
||||
targets.add(sparseSwitchLabelTarget);
|
||||
for (SwitchElement switchElement: instruction.getSwitchElements()) {
|
||||
LabelMethodItem label = methodDef.getLabelCache().internLabel(
|
||||
new LabelMethodItem( methodDef.classDef.options, baseCodeAddress + switchElement.getOffset(),
|
||||
"sswitch_"));
|
||||
targets.add(new SparseSwitchLabelTarget(switchElement.getKey(), label));
|
||||
}
|
||||
} else {
|
||||
//if we couldn't determine a base address, just use relative offsets rather than labels
|
||||
while (iterator.hasNext()) {
|
||||
SparseSwitchDataPseudoInstruction.SparseSwitchTarget target = iterator.next();
|
||||
SparseSwitchOffsetTarget sparseSwitchOffsetTarget = new SparseSwitchOffsetTarget();
|
||||
sparseSwitchOffsetTarget.Key = target.key;
|
||||
|
||||
sparseSwitchOffsetTarget.Target = target.targetAddressOffset;
|
||||
targets.add(sparseSwitchOffsetTarget);
|
||||
for (SwitchElement switchElement: instruction.getSwitchElements()) {
|
||||
targets.add(new SparseSwitchOffsetTarget(switchElement.getKey(), switchElement.getOffset()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -81,7 +68,7 @@ public class SparseSwitchMethodItem extends InstructionMethodItem<SparseSwitchDa
|
||||
writer.write(".sparse-switch\n");
|
||||
writer.indent(4);
|
||||
for (SparseSwitchTarget target: targets) {
|
||||
IntegerRenderer.writeTo(writer, target.Key);
|
||||
IntegerRenderer.writeTo(writer, target.getKey());
|
||||
writer.write(" -> ");
|
||||
target.writeTargetTo(writer);
|
||||
writer.write('\n');
|
||||
@ -92,24 +79,38 @@ public class SparseSwitchMethodItem extends InstructionMethodItem<SparseSwitchDa
|
||||
}
|
||||
|
||||
private static abstract class SparseSwitchTarget {
|
||||
public int Key;
|
||||
private final int key;
|
||||
public SparseSwitchTarget(int key) {
|
||||
this.key = key;
|
||||
}
|
||||
public int getKey() { return key; }
|
||||
public abstract void writeTargetTo(IndentingWriter writer) throws IOException;
|
||||
}
|
||||
|
||||
private static class SparseSwitchLabelTarget extends SparseSwitchTarget {
|
||||
public LabelMethodItem Target;
|
||||
private final LabelMethodItem target;
|
||||
public SparseSwitchLabelTarget(int key, LabelMethodItem target) {
|
||||
super(key);
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public void writeTargetTo(IndentingWriter writer) throws IOException {
|
||||
Target.writeTo(writer);
|
||||
target.writeTo(writer);
|
||||
}
|
||||
}
|
||||
|
||||
private static class SparseSwitchOffsetTarget extends SparseSwitchTarget {
|
||||
public int Target;
|
||||
private final int target;
|
||||
public SparseSwitchOffsetTarget(int key, int target) {
|
||||
super(key);
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public void writeTargetTo(IndentingWriter writer) throws IOException {
|
||||
if (Target >= 0) {
|
||||
if (target >= 0) {
|
||||
writer.write('+');
|
||||
}
|
||||
writer.printSignedIntAsDec(Target);
|
||||
writer.printSignedIntAsDec(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,15 +28,17 @@
|
||||
|
||||
package org.jf.baksmali.Adaptors.Format;
|
||||
|
||||
import org.jf.baksmali.Adaptors.MethodDefinition;
|
||||
import org.jf.dexlib2.analysis.UnresolvedOdexInstruction;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.jf.dexlib.Code.Format.UnresolvedOdexInstruction;
|
||||
import org.jf.dexlib.CodeItem;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
|
||||
public class UnresolvedOdexInstructionMethodItem extends InstructionMethodItem<UnresolvedOdexInstruction> {
|
||||
public UnresolvedOdexInstructionMethodItem(CodeItem codeItem, int codeAddress, UnresolvedOdexInstruction instruction) {
|
||||
super(codeItem, codeAddress, instruction);
|
||||
public UnresolvedOdexInstructionMethodItem(@Nonnull MethodDefinition methodDef, int codeAddress,
|
||||
@Nonnull UnresolvedOdexInstruction instruction) {
|
||||
super(methodDef, codeAddress, instruction);
|
||||
}
|
||||
|
||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||
@ -47,6 +49,6 @@ public class UnresolvedOdexInstructionMethodItem extends InstructionMethodItem<U
|
||||
private void writeThrowTo(IndentingWriter writer) throws IOException {
|
||||
writer.write("#Replaced unresolvable odex instruction with a throw\n");
|
||||
writer.write("throw ");
|
||||
writeRegister(writer, instruction.ObjectRegisterNum);
|
||||
writeRegister(writer, instruction.objectRegisterNum);
|
||||
}
|
||||
}
|
||||
|
@ -28,17 +28,20 @@
|
||||
|
||||
package org.jf.baksmali.Adaptors;
|
||||
|
||||
import org.jf.baksmali.baksmaliOptions;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.jf.baksmali.baksmali;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
|
||||
public class LabelMethodItem extends MethodItem {
|
||||
private final baksmaliOptions options;
|
||||
private final String labelPrefix;
|
||||
private int labelSequence;
|
||||
|
||||
public LabelMethodItem(int codeAddress, String labelPrefix) {
|
||||
public LabelMethodItem(@Nonnull baksmaliOptions options, int codeAddress, @Nonnull String labelPrefix) {
|
||||
super(codeAddress);
|
||||
this.options = options;
|
||||
this.labelPrefix = labelPrefix;
|
||||
}
|
||||
|
||||
@ -73,7 +76,7 @@ public class LabelMethodItem extends MethodItem {
|
||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||
writer.write(':');
|
||||
writer.write(labelPrefix);
|
||||
if (baksmali.useSequentialLabels) {
|
||||
if (options.useSequentialLabels) {
|
||||
writer.printUnsignedLongAsHex(labelSequence);
|
||||
} else {
|
||||
writer.printUnsignedLongAsHex(this.getLabelAddress());
|
||||
|
@ -28,297 +28,256 @@
|
||||
|
||||
package org.jf.baksmali.Adaptors;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.jf.baksmali.Adaptors.Debug.DebugMethodItem;
|
||||
import org.jf.baksmali.Adaptors.Format.InstructionMethodItemFactory;
|
||||
import org.jf.dexlib.Code.Analysis.SyntheticAccessorResolver;
|
||||
import org.jf.dexlib.Code.InstructionWithReference;
|
||||
import org.jf.dexlib2.AccessFlags;
|
||||
import org.jf.dexlib2.Format;
|
||||
import org.jf.dexlib2.Opcode;
|
||||
import org.jf.dexlib2.ReferenceType;
|
||||
import org.jf.dexlib2.analysis.AnalysisException;
|
||||
import org.jf.dexlib2.analysis.AnalyzedInstruction;
|
||||
import org.jf.dexlib2.analysis.MethodAnalyzer;
|
||||
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.OffsetInstruction;
|
||||
import org.jf.dexlib2.iface.instruction.ReferenceInstruction;
|
||||
import org.jf.dexlib2.iface.reference.MethodReference;
|
||||
import org.jf.dexlib2.util.InstructionOffsetMap;
|
||||
import org.jf.dexlib2.util.ReferenceUtil;
|
||||
import org.jf.dexlib2.util.SyntheticAccessorResolver;
|
||||
import org.jf.dexlib2.util.TypeUtils;
|
||||
import org.jf.util.ExceptionWithContext;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.jf.baksmali.baksmali;
|
||||
import org.jf.dexlib.*;
|
||||
import org.jf.dexlib.Code.Analysis.AnalyzedInstruction;
|
||||
import org.jf.dexlib.Code.Analysis.MethodAnalyzer;
|
||||
import org.jf.dexlib.Code.Analysis.ValidationException;
|
||||
import org.jf.dexlib.Code.Format.Format;
|
||||
import org.jf.dexlib.Code.Instruction;
|
||||
import org.jf.dexlib.Code.OffsetInstruction;
|
||||
import org.jf.dexlib.Code.Opcode;
|
||||
import org.jf.dexlib.Debug.DebugInstructionIterator;
|
||||
import org.jf.dexlib.Util.AccessFlags;
|
||||
import org.jf.dexlib.Util.ExceptionWithContext;
|
||||
import org.jf.dexlib.Util.SparseIntArray;
|
||||
import org.jf.util.SparseIntArray;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
public class MethodDefinition {
|
||||
private final ClassDataItem.EncodedMethod encodedMethod;
|
||||
private MethodAnalyzer methodAnalyzer;
|
||||
@Nonnull public final ClassDefinition classDef;
|
||||
@Nonnull public final Method method;
|
||||
@Nonnull public final MethodImplementation methodImpl;
|
||||
@Nonnull public final ImmutableList<Instruction> instructions;
|
||||
@Nonnull public final ImmutableList<MethodParameter> methodParameters;
|
||||
public RegisterFormatter registerFormatter;
|
||||
|
||||
private final LabelCache labelCache = new LabelCache();
|
||||
@Nonnull private final LabelCache labelCache = new LabelCache();
|
||||
|
||||
private final SparseIntArray packedSwitchMap;
|
||||
private final SparseIntArray sparseSwitchMap;
|
||||
private final SparseIntArray instructionMap;
|
||||
|
||||
public MethodDefinition(ClassDataItem.EncodedMethod encodedMethod) {
|
||||
@Nonnull private final SparseIntArray packedSwitchMap;
|
||||
@Nonnull private final SparseIntArray sparseSwitchMap;
|
||||
@Nonnull private final InstructionOffsetMap instructionOffsetMap;
|
||||
|
||||
public MethodDefinition(@Nonnull ClassDefinition classDef, @Nonnull Method method,
|
||||
@Nonnull MethodImplementation methodImpl) {
|
||||
this.classDef = classDef;
|
||||
this.method = method;
|
||||
this.methodImpl = methodImpl;
|
||||
|
||||
try {
|
||||
this.encodedMethod = encodedMethod;
|
||||
|
||||
//TODO: what about try/catch blocks inside the dead code? those will need to be commented out too. ugh.
|
||||
|
||||
if (encodedMethod.codeItem != null) {
|
||||
Instruction[] instructions = encodedMethod.codeItem.getInstructions();
|
||||
instructions = ImmutableList.copyOf(methodImpl.getInstructions());
|
||||
methodParameters = ImmutableList.copyOf(method.getParameters());
|
||||
|
||||
packedSwitchMap = new SparseIntArray(1);
|
||||
sparseSwitchMap = new SparseIntArray(1);
|
||||
instructionMap = new SparseIntArray(instructions.length);
|
||||
packedSwitchMap = new SparseIntArray(0);
|
||||
sparseSwitchMap = new SparseIntArray(0);
|
||||
instructionOffsetMap = new InstructionOffsetMap(instructions);
|
||||
|
||||
int currentCodeAddress = 0;
|
||||
for (int i=0; i<instructions.length; i++) {
|
||||
Instruction instruction = instructions[i];
|
||||
if (instruction.opcode == Opcode.PACKED_SWITCH) {
|
||||
packedSwitchMap.append(
|
||||
currentCodeAddress +
|
||||
((OffsetInstruction)instruction).getTargetAddressOffset(),
|
||||
currentCodeAddress);
|
||||
} else if (instruction.opcode == Opcode.SPARSE_SWITCH) {
|
||||
sparseSwitchMap.append(
|
||||
currentCodeAddress +
|
||||
((OffsetInstruction)instruction).getTargetAddressOffset(),
|
||||
currentCodeAddress);
|
||||
}
|
||||
instructionMap.append(currentCodeAddress, i);
|
||||
currentCodeAddress += instruction.getSize(currentCodeAddress);
|
||||
for (int i=0; i<instructions.size(); i++) {
|
||||
Instruction instruction = instructions.get(i);
|
||||
|
||||
Opcode opcode = instruction.getOpcode();
|
||||
if (opcode == Opcode.PACKED_SWITCH) {
|
||||
int codeOffset = instructionOffsetMap.getInstructionCodeOffset(i);
|
||||
int targetOffset = codeOffset + ((OffsetInstruction)instruction).getCodeOffset();
|
||||
targetOffset = findSwitchPayload(targetOffset, Opcode.PACKED_SWITCH_PAYLOAD);
|
||||
packedSwitchMap.append(targetOffset, codeOffset);
|
||||
} else if (opcode == Opcode.SPARSE_SWITCH) {
|
||||
int codeOffset = instructionOffsetMap.getInstructionCodeOffset(i);
|
||||
int targetOffset = codeOffset + ((OffsetInstruction)instruction).getCodeOffset();
|
||||
targetOffset = findSwitchPayload(targetOffset, Opcode.SPARSE_SWITCH_PAYLOAD);
|
||||
sparseSwitchMap.append(targetOffset, codeOffset);
|
||||
}
|
||||
} else {
|
||||
packedSwitchMap = null;
|
||||
sparseSwitchMap = null;
|
||||
instructionMap = null;
|
||||
methodAnalyzer = null;
|
||||
}
|
||||
}catch (Exception ex) {
|
||||
throw ExceptionWithContext.withContext(ex, String.format("Error while processing method %s",
|
||||
encodedMethod.method.getMethodString()));
|
||||
String methodString;
|
||||
try {
|
||||
methodString = ReferenceUtil.getMethodDescriptor(method);
|
||||
} catch (Exception ex2) {
|
||||
throw ExceptionWithContext.withContext(ex, "Error while processing method");
|
||||
}
|
||||
throw ExceptionWithContext.withContext(ex, "Error while processing method %s", methodString);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeTo(IndentingWriter writer, AnnotationSetItem annotationSet,
|
||||
AnnotationSetRefList parameterAnnotations) throws IOException {
|
||||
final CodeItem codeItem = encodedMethod.codeItem;
|
||||
|
||||
public static void writeEmptyMethodTo(IndentingWriter writer, Method method) throws IOException {
|
||||
writer.write(".method ");
|
||||
writeAccessFlags(writer, encodedMethod);
|
||||
writer.write(encodedMethod.method.getMethodName().getStringValue());
|
||||
writer.write(encodedMethod.method.getPrototype().getPrototypeString());
|
||||
writeAccessFlags(writer, method.getAccessFlags());
|
||||
writer.write(method.getName());
|
||||
writer.write("(");
|
||||
ImmutableList<MethodParameter> methodParameters = ImmutableList.copyOf(method.getParameters());
|
||||
for (MethodParameter parameter: methodParameters) {
|
||||
writer.write(parameter.getType());
|
||||
}
|
||||
writer.write(")");
|
||||
writer.write(method.getReturnType());
|
||||
writer.write('\n');
|
||||
|
||||
writer.indent(4);
|
||||
if (codeItem != null) {
|
||||
if (baksmali.useLocalsDirective) {
|
||||
writer.write(".locals ");
|
||||
} else {
|
||||
writer.write(".registers ");
|
||||
}
|
||||
writer.printSignedIntAsDec(getRegisterCount(encodedMethod));
|
||||
writer.write('\n');
|
||||
writeParameters(writer, codeItem, parameterAnnotations);
|
||||
if (annotationSet != null) {
|
||||
AnnotationFormatter.writeTo(writer, annotationSet);
|
||||
}
|
||||
writeParameters(writer, method, methodParameters);
|
||||
AnnotationFormatter.writeTo(writer, method.getAnnotations());
|
||||
writer.deindent(4);
|
||||
writer.write(".end method\n");
|
||||
}
|
||||
|
||||
writer.write('\n');
|
||||
public void writeTo(IndentingWriter writer) throws IOException {
|
||||
int parameterRegisterCount = 0;
|
||||
if (!AccessFlags.STATIC.isSet(method.getAccessFlags())) {
|
||||
parameterRegisterCount++;
|
||||
}
|
||||
|
||||
for (MethodItem methodItem: getMethodItems()) {
|
||||
if (methodItem.writeTo(writer)) {
|
||||
writer.write('\n');
|
||||
}
|
||||
writer.write(".method ");
|
||||
writeAccessFlags(writer, method.getAccessFlags());
|
||||
writer.write(method.getName());
|
||||
writer.write("(");
|
||||
for (MethodParameter parameter: methodParameters) {
|
||||
String type = parameter.getType();
|
||||
writer.write(type);
|
||||
parameterRegisterCount++;
|
||||
if (TypeUtils.isWideType(type)) {
|
||||
parameterRegisterCount++;
|
||||
}
|
||||
}
|
||||
writer.write(")");
|
||||
writer.write(method.getReturnType());
|
||||
writer.write('\n');
|
||||
|
||||
writer.indent(4);
|
||||
if (classDef.options.useLocalsDirective) {
|
||||
writer.write(".locals ");
|
||||
writer.printSignedIntAsDec(methodImpl.getRegisterCount() - parameterRegisterCount);
|
||||
} else {
|
||||
writeParameters(writer, codeItem, parameterAnnotations);
|
||||
if (annotationSet != null) {
|
||||
AnnotationFormatter.writeTo(writer, annotationSet);
|
||||
writer.write(".registers ");
|
||||
writer.printSignedIntAsDec(methodImpl.getRegisterCount());
|
||||
}
|
||||
writer.write('\n');
|
||||
writeParameters(writer, method, methodParameters);
|
||||
|
||||
if (registerFormatter == null) {
|
||||
registerFormatter = new RegisterFormatter(classDef.options, methodImpl.getRegisterCount(),
|
||||
parameterRegisterCount);
|
||||
}
|
||||
|
||||
AnnotationFormatter.writeTo(writer, method.getAnnotations());
|
||||
|
||||
writer.write('\n');
|
||||
|
||||
List<MethodItem> methodItems = getMethodItems();
|
||||
for (MethodItem methodItem: methodItems) {
|
||||
if (methodItem.writeTo(writer)) {
|
||||
writer.write('\n');
|
||||
}
|
||||
}
|
||||
writer.deindent(4);
|
||||
writer.write(".end method\n");
|
||||
}
|
||||
|
||||
private static int getRegisterCount(ClassDataItem.EncodedMethod encodedMethod)
|
||||
{
|
||||
int totalRegisters = encodedMethod.codeItem.getRegisterCount();
|
||||
if (baksmali.useLocalsDirective) {
|
||||
int parameterRegisters = encodedMethod.method.getPrototype().getParameterRegisterCount();
|
||||
if ((encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0) {
|
||||
parameterRegisters++;
|
||||
private int findSwitchPayload(int targetOffset, Opcode type) {
|
||||
int targetIndex = instructionOffsetMap.getInstructionIndexAtCodeOffset(targetOffset);
|
||||
|
||||
//TODO: does dalvik let you pad with multiple nops?
|
||||
//TODO: does dalvik let a switch instruction point to a non-payload instruction?
|
||||
|
||||
Instruction instruction = instructions.get(targetIndex);
|
||||
if (instruction.getOpcode() != type) {
|
||||
// maybe it's pointing to a NOP padding instruction. Look at the next instruction
|
||||
if (instruction.getOpcode() == Opcode.NOP) {
|
||||
targetIndex += 1;
|
||||
if (targetIndex < instructions.size()) {
|
||||
instruction = instructions.get(targetIndex);
|
||||
if (instruction.getOpcode() == type) {
|
||||
return instructionOffsetMap.getInstructionCodeOffset(targetIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return totalRegisters - parameterRegisters;
|
||||
throw new ExceptionWithContext("No switch payload at offset 0x%x", targetOffset);
|
||||
} else {
|
||||
return targetOffset;
|
||||
}
|
||||
return totalRegisters;
|
||||
}
|
||||
|
||||
private static void writeAccessFlags(IndentingWriter writer, ClassDataItem.EncodedMethod encodedMethod)
|
||||
throws IOException {
|
||||
for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForMethod(encodedMethod.accessFlags)) {
|
||||
private static void writeAccessFlags(IndentingWriter writer, int accessFlags)
|
||||
throws IOException {
|
||||
for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForMethod(accessFlags)) {
|
||||
writer.write(accessFlag.toString());
|
||||
writer.write(' ');
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeParameters(IndentingWriter writer, CodeItem codeItem,
|
||||
AnnotationSetRefList parameterAnnotations) throws IOException {
|
||||
DebugInfoItem debugInfoItem = null;
|
||||
if (baksmali.outputDebugInfo && codeItem != null) {
|
||||
debugInfoItem = codeItem.getDebugInfo();
|
||||
}
|
||||
private static void writeParameters(IndentingWriter writer, Method method,
|
||||
List<? extends MethodParameter> parameters) throws IOException {
|
||||
boolean isStatic = AccessFlags.STATIC.isSet(method.getAccessFlags());
|
||||
int registerNumber = isStatic?0:1;
|
||||
for (MethodParameter parameter: parameters) {
|
||||
String parameterType = parameter.getType();
|
||||
String parameterName = parameter.getName();
|
||||
Collection<? extends Annotation> annotations = parameter.getAnnotations();
|
||||
if (parameterName != null || annotations.size() != 0) {
|
||||
writer.write(".param p");
|
||||
writer.printSignedIntAsDec(registerNumber);
|
||||
|
||||
int parameterCount = 0;
|
||||
AnnotationSetItem[] annotations;
|
||||
StringIdItem[] parameterNames = null;
|
||||
|
||||
if (parameterAnnotations != null) {
|
||||
annotations = parameterAnnotations.getAnnotationSets();
|
||||
parameterCount = annotations.length;
|
||||
} else {
|
||||
annotations = new AnnotationSetItem[0];
|
||||
}
|
||||
|
||||
if (debugInfoItem != null) {
|
||||
parameterNames = debugInfoItem.getParameterNames();
|
||||
}
|
||||
if (parameterNames == null) {
|
||||
parameterNames = new StringIdItem[0];
|
||||
}
|
||||
|
||||
if (parameterCount < parameterNames.length) {
|
||||
parameterCount = parameterNames.length;
|
||||
}
|
||||
|
||||
for (int i=0; i<parameterCount; i++) {
|
||||
AnnotationSetItem annotationSet = null;
|
||||
if (i < annotations.length) {
|
||||
annotationSet = annotations[i];
|
||||
if (parameterName != null) {
|
||||
writer.write(", ");
|
||||
ReferenceFormatter.writeStringReference(writer, parameterName);
|
||||
}
|
||||
writer.write(" # ");
|
||||
writer.write(parameterType);
|
||||
writer.write("\n");
|
||||
if (annotations.size() > 0) {
|
||||
writer.indent(4);
|
||||
AnnotationFormatter.writeTo(writer, annotations);
|
||||
writer.deindent(4);
|
||||
writer.write(".end param\n");
|
||||
}
|
||||
}
|
||||
|
||||
StringIdItem parameterName = null;
|
||||
if (i < parameterNames.length) {
|
||||
parameterName = parameterNames[i];
|
||||
}
|
||||
|
||||
writer.write(".parameter");
|
||||
|
||||
if (parameterName != null) {
|
||||
writer.write(" \"");
|
||||
writer.write(parameterName.getStringValue());
|
||||
writer.write('"');
|
||||
}
|
||||
|
||||
writer.write('\n');
|
||||
if (annotationSet != null) {
|
||||
writer.indent(4);
|
||||
AnnotationFormatter.writeTo(writer, annotationSet);
|
||||
writer.deindent(4);
|
||||
|
||||
writer.write(".end parameter\n");
|
||||
registerNumber++;
|
||||
if (TypeUtils.isWideType(parameterType)) {
|
||||
registerNumber++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public LabelCache getLabelCache() {
|
||||
@Nonnull public LabelCache getLabelCache() {
|
||||
return labelCache;
|
||||
}
|
||||
|
||||
public ValidationException getValidationException() {
|
||||
if (methodAnalyzer == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return methodAnalyzer.getValidationException();
|
||||
public int getPackedSwitchBaseAddress(int packedSwitchPayloadCodeOffset) {
|
||||
return packedSwitchMap.get(packedSwitchPayloadCodeOffset, -1);
|
||||
}
|
||||
|
||||
public int getPackedSwitchBaseAddress(int packedSwitchDataAddress) {
|
||||
int packedSwitchBaseAddress = this.packedSwitchMap.get(packedSwitchDataAddress, -1);
|
||||
|
||||
if (packedSwitchBaseAddress == -1) {
|
||||
Instruction[] instructions = encodedMethod.codeItem.getInstructions();
|
||||
int index = instructionMap.get(packedSwitchDataAddress);
|
||||
|
||||
if (instructions[index].opcode == Opcode.NOP) {
|
||||
packedSwitchBaseAddress = this.packedSwitchMap.get(packedSwitchDataAddress+2, -1);
|
||||
}
|
||||
}
|
||||
|
||||
return packedSwitchBaseAddress;
|
||||
}
|
||||
|
||||
public int getSparseSwitchBaseAddress(int sparseSwitchDataAddress) {
|
||||
int sparseSwitchBaseAddress = this.sparseSwitchMap.get(sparseSwitchDataAddress, -1);
|
||||
|
||||
if (sparseSwitchBaseAddress == -1) {
|
||||
Instruction[] instructions = encodedMethod.codeItem.getInstructions();
|
||||
int index = instructionMap.get(sparseSwitchDataAddress);
|
||||
|
||||
if (instructions[index].opcode == Opcode.NOP) {
|
||||
sparseSwitchBaseAddress = this.packedSwitchMap.get(sparseSwitchDataAddress+2, -1);
|
||||
}
|
||||
}
|
||||
|
||||
return sparseSwitchBaseAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param instructions The instructions array for this method
|
||||
* @param instruction The instruction
|
||||
* @return true if the specified instruction is a NOP, and the next instruction is one of the variable sized
|
||||
* switch/array data structures
|
||||
*/
|
||||
private boolean isInstructionPaddingNop(List<AnalyzedInstruction> instructions, AnalyzedInstruction instruction) {
|
||||
if (instruction.getInstruction().opcode != Opcode.NOP ||
|
||||
instruction.getInstruction().getFormat().variableSizeFormat) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (instruction.getInstructionIndex() == instructions.size()-1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AnalyzedInstruction nextInstruction = instructions.get(instruction.getInstructionIndex()+1);
|
||||
if (nextInstruction.getInstruction().getFormat().variableSizeFormat) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean needsAnalyzed() {
|
||||
for (Instruction instruction: encodedMethod.codeItem.getInstructions()) {
|
||||
if (instruction.opcode.odexOnly()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
public int getSparseSwitchBaseAddress(int sparseSwitchPayloadCodeOffset) {
|
||||
return sparseSwitchMap.get(sparseSwitchPayloadCodeOffset, -1);
|
||||
}
|
||||
|
||||
private List<MethodItem> getMethodItems() {
|
||||
ArrayList<MethodItem> methodItems = new ArrayList<MethodItem>();
|
||||
|
||||
if (encodedMethod.codeItem == null) {
|
||||
return methodItems;
|
||||
}
|
||||
|
||||
if ((baksmali.registerInfo != 0) || baksmali.verify ||
|
||||
(baksmali.deodex && needsAnalyzed())) {
|
||||
if ((classDef.options.registerInfo != 0) || (classDef.options.deodex && needsAnalyzed())) {
|
||||
addAnalyzedInstructionMethodItems(methodItems);
|
||||
} else {
|
||||
addInstructionMethodItems(methodItems);
|
||||
}
|
||||
|
||||
addTries(methodItems);
|
||||
if (baksmali.outputDebugInfo) {
|
||||
if (classDef.options.outputDebugInfo) {
|
||||
addDebugInfo(methodItems);
|
||||
}
|
||||
|
||||
if (baksmali.useSequentialLabels) {
|
||||
if (classDef.options.useSequentialLabels) {
|
||||
setLabelSequentialNumbers();
|
||||
}
|
||||
|
||||
@ -331,23 +290,30 @@ public class MethodDefinition {
|
||||
return methodItems;
|
||||
}
|
||||
|
||||
private void addInstructionMethodItems(List<MethodItem> methodItems) {
|
||||
Instruction[] instructions = encodedMethod.codeItem.getInstructions();
|
||||
private boolean needsAnalyzed() {
|
||||
for (Instruction instruction: methodImpl.getInstructions()) {
|
||||
if (instruction.getOpcode().odexOnly()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void addInstructionMethodItems(List<MethodItem> methodItems) {
|
||||
int currentCodeAddress = 0;
|
||||
for (int i=0; i<instructions.length; i++) {
|
||||
Instruction instruction = instructions[i];
|
||||
for (int i=0; i<instructions.size(); i++) {
|
||||
Instruction instruction = instructions.get(i);
|
||||
|
||||
MethodItem methodItem = InstructionMethodItemFactory.makeInstructionFormatMethodItem(this,
|
||||
encodedMethod.codeItem, currentCodeAddress, instruction);
|
||||
currentCodeAddress, instruction);
|
||||
|
||||
methodItems.add(methodItem);
|
||||
|
||||
if (i != instructions.length - 1) {
|
||||
if (i != instructions.size() - 1) {
|
||||
methodItems.add(new BlankMethodItem(currentCodeAddress));
|
||||
}
|
||||
|
||||
if (baksmali.addCodeOffsets) {
|
||||
if (classDef.options.addCodeOffsets) {
|
||||
methodItems.add(new MethodItem(currentCodeAddress) {
|
||||
|
||||
@Override
|
||||
@ -358,20 +324,22 @@ public class MethodDefinition {
|
||||
@Override
|
||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||
writer.write("#@");
|
||||
writer.printUnsignedLongAsHex(codeAddress & 0xFFFFFFFF);
|
||||
writer.printUnsignedLongAsHex(codeAddress & 0xFFFFFFFFL);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!baksmali.noAccessorComments && (instruction instanceof InstructionWithReference)) {
|
||||
if (instruction.opcode == Opcode.INVOKE_STATIC || instruction.opcode == Opcode.INVOKE_STATIC_RANGE) {
|
||||
MethodIdItem methodIdItem =
|
||||
(MethodIdItem)((InstructionWithReference) instruction).getReferencedItem();
|
||||
if (!classDef.options.noAccessorComments && (instruction instanceof ReferenceInstruction)) {
|
||||
Opcode opcode = instruction.getOpcode();
|
||||
|
||||
if (SyntheticAccessorResolver.looksLikeSyntheticAccessor(methodIdItem)) {
|
||||
if (opcode.referenceType == ReferenceType.METHOD) {
|
||||
MethodReference methodReference =
|
||||
(MethodReference)((ReferenceInstruction)instruction).getReference();
|
||||
|
||||
if (SyntheticAccessorResolver.looksLikeSyntheticAccessor(methodReference.getName())) {
|
||||
SyntheticAccessorResolver.AccessedMember accessedMember =
|
||||
baksmali.syntheticAccessorResolver.getAccessedMember(methodIdItem);
|
||||
classDef.options.syntheticAccessorResolver.getAccessedMember(methodReference);
|
||||
if (accessedMember != null) {
|
||||
methodItems.add(new SyntheticAccessCommentMethodItem(accessedMember, currentCodeAddress));
|
||||
}
|
||||
@ -379,53 +347,45 @@ public class MethodDefinition {
|
||||
}
|
||||
}
|
||||
|
||||
currentCodeAddress += instruction.getSize(currentCodeAddress);
|
||||
currentCodeAddress += instruction.getCodeUnits();
|
||||
}
|
||||
}
|
||||
|
||||
private void addAnalyzedInstructionMethodItems(List<MethodItem> methodItems) {
|
||||
methodAnalyzer = new MethodAnalyzer(encodedMethod, baksmali.deodex, baksmali.inlineResolver);
|
||||
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classDef.options.classPath, method,
|
||||
classDef.options.inlineResolver);
|
||||
|
||||
methodAnalyzer.analyze();
|
||||
|
||||
ValidationException validationException = methodAnalyzer.getValidationException();
|
||||
if (validationException != null) {
|
||||
AnalysisException analysisException = methodAnalyzer.getAnalysisException();
|
||||
if (analysisException != null) {
|
||||
// TODO: need to keep track of whether any errors occurred, so we can exit with a non-zero result
|
||||
methodItems.add(new CommentMethodItem(
|
||||
String.format("ValidationException: %s" ,validationException.getMessage()),
|
||||
validationException.getCodeAddress(), Integer.MIN_VALUE));
|
||||
} else if (baksmali.verify) {
|
||||
methodAnalyzer.verify();
|
||||
|
||||
validationException = methodAnalyzer.getValidationException();
|
||||
if (validationException != null) {
|
||||
methodItems.add(new CommentMethodItem(
|
||||
String.format("ValidationException: %s" ,validationException.getMessage()),
|
||||
validationException.getCodeAddress(), Integer.MIN_VALUE));
|
||||
}
|
||||
String.format("AnalysisException: %s", analysisException.getMessage()),
|
||||
analysisException.codeAddress, Integer.MIN_VALUE));
|
||||
analysisException.printStackTrace(System.err);
|
||||
}
|
||||
|
||||
List<AnalyzedInstruction> instructions = methodAnalyzer.getInstructions();
|
||||
List<AnalyzedInstruction> instructions = methodAnalyzer.getAnalyzedInstructions();
|
||||
|
||||
int currentCodeAddress = 0;
|
||||
for (int i=0; i<instructions.size(); i++) {
|
||||
AnalyzedInstruction instruction = instructions.get(i);
|
||||
|
||||
MethodItem methodItem = InstructionMethodItemFactory.makeInstructionFormatMethodItem(this,
|
||||
encodedMethod.codeItem, currentCodeAddress, instruction.getInstruction());
|
||||
MethodItem methodItem = InstructionMethodItemFactory.makeInstructionFormatMethodItem(
|
||||
this, currentCodeAddress, instruction.getInstruction());
|
||||
|
||||
methodItems.add(methodItem);
|
||||
|
||||
if (instruction.getInstruction().getFormat() == Format.UnresolvedOdexInstruction) {
|
||||
if (instruction.getInstruction().getOpcode().format == Format.UnresolvedOdexInstruction) {
|
||||
methodItems.add(new CommentedOutMethodItem(
|
||||
InstructionMethodItemFactory.makeInstructionFormatMethodItem(this,
|
||||
encodedMethod.codeItem, currentCodeAddress, instruction.getOriginalInstruction())));
|
||||
InstructionMethodItemFactory.makeInstructionFormatMethodItem(
|
||||
this, currentCodeAddress, instruction.getOriginalInstruction())));
|
||||
}
|
||||
|
||||
if (i != instructions.size() - 1) {
|
||||
methodItems.add(new BlankMethodItem(currentCodeAddress));
|
||||
}
|
||||
|
||||
if (baksmali.addCodeOffsets) {
|
||||
if (classDef.options.addCodeOffsets) {
|
||||
methodItems.add(new MethodItem(currentCodeAddress) {
|
||||
|
||||
@Override
|
||||
@ -436,36 +396,38 @@ public class MethodDefinition {
|
||||
@Override
|
||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||
writer.write("#@");
|
||||
writer.printUnsignedLongAsHex(codeAddress & 0xFFFFFFFF);
|
||||
writer.printUnsignedLongAsHex(codeAddress & 0xFFFFFFFFL);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (baksmali.registerInfo != 0 && !instruction.getInstruction().getFormat().variableSizeFormat) {
|
||||
if (classDef.options.registerInfo != 0 &&
|
||||
!instruction.getInstruction().getOpcode().format.isPayloadFormat) {
|
||||
methodItems.add(
|
||||
new PreInstructionRegisterInfoMethodItem(instruction, methodAnalyzer, currentCodeAddress));
|
||||
new PreInstructionRegisterInfoMethodItem(classDef.options.registerInfo,
|
||||
methodAnalyzer, registerFormatter, instruction, currentCodeAddress));
|
||||
|
||||
methodItems.add(
|
||||
new PostInstructionRegisterInfoMethodItem(instruction, methodAnalyzer, currentCodeAddress));
|
||||
new PostInstructionRegisterInfoMethodItem(registerFormatter, instruction, currentCodeAddress));
|
||||
}
|
||||
|
||||
currentCodeAddress += instruction.getInstruction().getSize(currentCodeAddress);
|
||||
currentCodeAddress += instruction.getInstruction().getCodeUnits();
|
||||
}
|
||||
}
|
||||
|
||||
private void addTries(List<MethodItem> methodItems) {
|
||||
if (encodedMethod.codeItem == null || encodedMethod.codeItem.getTries() == null) {
|
||||
List<? extends TryBlock<? extends ExceptionHandler>> tryBlocks = methodImpl.getTryBlocks();
|
||||
if (tryBlocks.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Instruction[] instructions = encodedMethod.codeItem.getInstructions();
|
||||
int lastInstructionAddress = instructionMap.keyAt(instructionMap.size()-1);
|
||||
int codeSize = lastInstructionAddress + instructions[instructions.length - 1].getSize(lastInstructionAddress);
|
||||
int lastInstructionAddress = instructionOffsetMap.getInstructionCodeOffset(instructions.size() - 1);
|
||||
int codeSize = lastInstructionAddress + instructions.get(instructions.size() - 1).getCodeUnits();
|
||||
|
||||
for (CodeItem.TryItem tryItem: encodedMethod.codeItem.getTries()) {
|
||||
int startAddress = tryItem.getStartCodeAddress();
|
||||
int endAddress = tryItem.getStartCodeAddress() + tryItem.getTryLength();
|
||||
for (TryBlock<? extends ExceptionHandler> tryBlock: tryBlocks) {
|
||||
int startAddress = tryBlock.getStartCodeAddress();
|
||||
int endAddress = startAddress + tryBlock.getCodeUnitCount();
|
||||
|
||||
if (startAddress >= codeSize) {
|
||||
throw new RuntimeException(String.format("Try start offset %d is past the end of the code block.",
|
||||
@ -484,142 +446,28 @@ public class MethodDefinition {
|
||||
* the address for that instruction
|
||||
*/
|
||||
|
||||
int lastCoveredIndex = instructionMap.getClosestSmaller(endAddress-1);
|
||||
int lastCoveredAddress = instructionMap.keyAt(lastCoveredIndex);
|
||||
int lastCoveredIndex = instructionOffsetMap.getInstructionIndexAtCodeOffset(endAddress - 1, false);
|
||||
int lastCoveredAddress = instructionOffsetMap.getInstructionCodeOffset(lastCoveredIndex);
|
||||
|
||||
//add the catch all handler if it exists
|
||||
int catchAllAddress = tryItem.encodedCatchHandler.getCatchAllHandlerAddress();
|
||||
if (catchAllAddress != -1) {
|
||||
if (catchAllAddress >= codeSize) {
|
||||
throw new RuntimeException(String.format(
|
||||
"Catch all handler offset %d is past the end of the code block.", catchAllAddress));
|
||||
}
|
||||
|
||||
CatchMethodItem catchAllMethodItem = new CatchMethodItem(labelCache, lastCoveredAddress, null,
|
||||
startAddress, endAddress, catchAllAddress);
|
||||
methodItems.add(catchAllMethodItem);
|
||||
}
|
||||
|
||||
//add the rest of the handlers
|
||||
for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) {
|
||||
if (handler.getHandlerAddress() >= codeSize) {
|
||||
throw new RuntimeException(String.format(
|
||||
"Exception handler offset %d is past the end of the code block.", catchAllAddress));
|
||||
for (ExceptionHandler handler: tryBlock.getExceptionHandlers()) {
|
||||
int handlerAddress = handler.getHandlerCodeAddress();
|
||||
if (handlerAddress >= codeSize) {
|
||||
throw new ExceptionWithContext(
|
||||
"Exception handler offset %d is past the end of the code block.", handlerAddress);
|
||||
}
|
||||
|
||||
//use the address from the last covered instruction
|
||||
CatchMethodItem catchMethodItem = new CatchMethodItem(labelCache, lastCoveredAddress,
|
||||
handler.exceptionType, startAddress, endAddress, handler.getHandlerAddress());
|
||||
CatchMethodItem catchMethodItem = new CatchMethodItem(classDef.options, labelCache, lastCoveredAddress,
|
||||
handler.getExceptionType(), startAddress, endAddress, handlerAddress);
|
||||
methodItems.add(catchMethodItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addDebugInfo(final List<MethodItem> methodItems) {
|
||||
if (encodedMethod.codeItem == null || encodedMethod.codeItem.getDebugInfo() == null) {
|
||||
return;
|
||||
for (DebugItem debugItem: methodImpl.getDebugItems()) {
|
||||
methodItems.add(DebugMethodItem.build(registerFormatter, debugItem));
|
||||
}
|
||||
|
||||
final CodeItem codeItem = encodedMethod.codeItem;
|
||||
DebugInfoItem debugInfoItem = codeItem.getDebugInfo();
|
||||
|
||||
DebugInstructionIterator.DecodeInstructions(debugInfoItem, codeItem.getRegisterCount(),
|
||||
new DebugInstructionIterator.ProcessDecodedDebugInstructionDelegate() {
|
||||
@Override
|
||||
public void ProcessStartLocal(final int codeAddress, final int length, final int registerNum,
|
||||
final StringIdItem name, final TypeIdItem type) {
|
||||
methodItems.add(new DebugMethodItem(codeAddress, -1) {
|
||||
@Override
|
||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||
writeStartLocal(writer, codeItem, registerNum, name, type, null);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ProcessStartLocalExtended(final int codeAddress, final int length,
|
||||
final int registerNum, final StringIdItem name,
|
||||
final TypeIdItem type, final StringIdItem signature) {
|
||||
methodItems.add(new DebugMethodItem(codeAddress, -1) {
|
||||
@Override
|
||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||
writeStartLocal(writer, codeItem, registerNum, name, type, signature);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ProcessEndLocal(final int codeAddress, final int length, final int registerNum,
|
||||
final StringIdItem name, final TypeIdItem type,
|
||||
final StringIdItem signature) {
|
||||
methodItems.add(new DebugMethodItem(codeAddress, -1) {
|
||||
@Override
|
||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||
writeEndLocal(writer, codeItem, registerNum, name, type, signature);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ProcessRestartLocal(final int codeAddress, final int length, final int registerNum,
|
||||
final StringIdItem name, final TypeIdItem type,
|
||||
final StringIdItem signature) {
|
||||
methodItems.add(new DebugMethodItem(codeAddress, -1) {
|
||||
@Override
|
||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||
writeRestartLocal(writer, codeItem, registerNum, name, type, signature);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ProcessSetPrologueEnd(int codeAddress) {
|
||||
methodItems.add(new DebugMethodItem(codeAddress, -4) {
|
||||
@Override
|
||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||
writeEndPrologue(writer);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ProcessSetEpilogueBegin(int codeAddress) {
|
||||
methodItems.add(new DebugMethodItem(codeAddress, -4) {
|
||||
@Override
|
||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||
writeBeginEpilogue(writer);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ProcessSetFile(int codeAddress, int length, final StringIdItem name) {
|
||||
methodItems.add(new DebugMethodItem(codeAddress, -3) {
|
||||
@Override
|
||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||
writeSetFile(writer, name.getStringValue());
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ProcessLineEmit(int codeAddress, final int line) {
|
||||
methodItems.add(new DebugMethodItem(codeAddress, -2) {
|
||||
@Override
|
||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||
writeLine(writer, line);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setLabelSequentialNumbers() {
|
||||
|
@ -28,26 +28,25 @@
|
||||
|
||||
package org.jf.baksmali.Adaptors;
|
||||
|
||||
import org.jf.baksmali.baksmaliOptions;
|
||||
import org.jf.dexlib2.analysis.AnalyzedInstruction;
|
||||
import org.jf.dexlib2.analysis.RegisterType;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.jf.baksmali.baksmali;
|
||||
import org.jf.baksmali.main;
|
||||
import org.jf.dexlib.ClassDataItem;
|
||||
import org.jf.dexlib.Code.Analysis.AnalyzedInstruction;
|
||||
import org.jf.dexlib.Code.Analysis.MethodAnalyzer;
|
||||
import org.jf.dexlib.Code.Analysis.RegisterType;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
import java.util.BitSet;
|
||||
|
||||
public class PostInstructionRegisterInfoMethodItem extends MethodItem {
|
||||
private final AnalyzedInstruction analyzedInstruction;
|
||||
private final MethodAnalyzer methodAnalyzer;
|
||||
@Nonnull private final RegisterFormatter registerFormatter;
|
||||
@Nonnull private final AnalyzedInstruction analyzedInstruction;
|
||||
|
||||
public PostInstructionRegisterInfoMethodItem(AnalyzedInstruction analyzedInstruction, MethodAnalyzer methodAnalyzer,
|
||||
int codeAddress) {
|
||||
public PostInstructionRegisterInfoMethodItem(@Nonnull RegisterFormatter registerFormatter,
|
||||
@Nonnull AnalyzedInstruction analyzedInstruction,
|
||||
int codeAddress) {
|
||||
super(codeAddress);
|
||||
this.registerFormatter = registerFormatter;
|
||||
this.analyzedInstruction = analyzedInstruction;
|
||||
this.methodAnalyzer = methodAnalyzer;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -57,16 +56,16 @@ public class PostInstructionRegisterInfoMethodItem extends MethodItem {
|
||||
|
||||
@Override
|
||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||
int registerInfo = baksmali.registerInfo;
|
||||
int registerInfo = registerFormatter.options.registerInfo;
|
||||
int registerCount = analyzedInstruction.getRegisterCount();
|
||||
BitSet registers = new BitSet(registerCount);
|
||||
|
||||
if ((registerInfo & main.ALL) != 0) {
|
||||
if ((registerInfo & baksmaliOptions.ALL) != 0) {
|
||||
registers.set(0, registerCount);
|
||||
} else {
|
||||
if ((registerInfo & main.ALLPOST) != 0) {
|
||||
if ((registerInfo & baksmaliOptions.ALLPOST) != 0) {
|
||||
registers.set(0, registerCount);
|
||||
} else if ((registerInfo & main.DEST) != 0) {
|
||||
} else if ((registerInfo & baksmaliOptions.DEST) != 0) {
|
||||
addDestRegs(registers, registerCount);
|
||||
}
|
||||
}
|
||||
@ -76,16 +75,14 @@ public class PostInstructionRegisterInfoMethodItem extends MethodItem {
|
||||
|
||||
private void addDestRegs(BitSet printPostRegister, int registerCount) {
|
||||
for (int registerNum=0; registerNum<registerCount; registerNum++) {
|
||||
if (analyzedInstruction.getPreInstructionRegisterType(registerNum) !=
|
||||
analyzedInstruction.getPostInstructionRegisterType(registerNum)) {
|
||||
if (!analyzedInstruction.getPreInstructionRegisterType(registerNum).equals(
|
||||
analyzedInstruction.getPostInstructionRegisterType(registerNum))) {
|
||||
printPostRegister.set(registerNum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean writeRegisterInfo(IndentingWriter writer, BitSet registers) throws IOException {
|
||||
ClassDataItem.EncodedMethod encodedMethod = methodAnalyzer.getMethod();
|
||||
|
||||
int registerNum = registers.nextSetBit(0);
|
||||
if (registerNum < 0) {
|
||||
return false;
|
||||
@ -93,17 +90,11 @@ public class PostInstructionRegisterInfoMethodItem extends MethodItem {
|
||||
|
||||
writer.write('#');
|
||||
for (; registerNum >= 0; registerNum = registers.nextSetBit(registerNum + 1)) {
|
||||
|
||||
RegisterType registerType = analyzedInstruction.getPostInstructionRegisterType(registerNum);
|
||||
|
||||
RegisterFormatter.writeTo(writer, encodedMethod.codeItem, registerNum);
|
||||
registerFormatter.writeTo(writer, registerNum);
|
||||
writer.write('=');
|
||||
|
||||
if (registerType == null) {
|
||||
writer.write("null");
|
||||
} else {
|
||||
registerType.writeTo(writer);
|
||||
}
|
||||
registerType.writeTo(writer);
|
||||
writer.write(';');
|
||||
}
|
||||
return true;
|
||||
|
@ -28,30 +28,33 @@
|
||||
|
||||
package org.jf.baksmali.Adaptors;
|
||||
|
||||
import org.jf.baksmali.baksmaliOptions;
|
||||
import org.jf.dexlib2.analysis.AnalyzedInstruction;
|
||||
import org.jf.dexlib2.analysis.MethodAnalyzer;
|
||||
import org.jf.dexlib2.analysis.RegisterType;
|
||||
import org.jf.dexlib2.iface.instruction.*;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.jf.baksmali.baksmali;
|
||||
import org.jf.baksmali.main;
|
||||
import org.jf.dexlib.ClassDataItem;
|
||||
import org.jf.dexlib.Code.Analysis.AnalyzedInstruction;
|
||||
import org.jf.dexlib.Code.Analysis.MethodAnalyzer;
|
||||
import org.jf.dexlib.Code.Analysis.RegisterType;
|
||||
import org.jf.dexlib.Code.*;
|
||||
import org.jf.dexlib.Util.AccessFlags;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
import java.util.BitSet;
|
||||
|
||||
public class PreInstructionRegisterInfoMethodItem extends MethodItem {
|
||||
private static AnalyzedInstruction lastInstruction;
|
||||
|
||||
private final AnalyzedInstruction analyzedInstruction;
|
||||
private final MethodAnalyzer methodAnalyzer;
|
||||
private final int registerInfo;
|
||||
@Nonnull private final MethodAnalyzer methodAnalyzer;
|
||||
@Nonnull private final RegisterFormatter registerFormatter;
|
||||
@Nonnull private final AnalyzedInstruction analyzedInstruction;
|
||||
|
||||
public PreInstructionRegisterInfoMethodItem(AnalyzedInstruction analyzedInstruction, MethodAnalyzer methodAnalyzer,
|
||||
public PreInstructionRegisterInfoMethodItem(int registerInfo,
|
||||
@Nonnull MethodAnalyzer methodAnalyzer,
|
||||
@Nonnull RegisterFormatter registerFormatter,
|
||||
@Nonnull AnalyzedInstruction analyzedInstruction,
|
||||
int codeAddress) {
|
||||
super(codeAddress);
|
||||
this.analyzedInstruction = analyzedInstruction;
|
||||
this.registerInfo = registerInfo;
|
||||
this.methodAnalyzer = methodAnalyzer;
|
||||
this.registerFormatter = registerFormatter;
|
||||
this.analyzedInstruction = analyzedInstruction;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -61,39 +64,44 @@ public class PreInstructionRegisterInfoMethodItem extends MethodItem {
|
||||
|
||||
@Override
|
||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||
int registerInfo = baksmali.registerInfo;
|
||||
int registerCount = analyzedInstruction.getRegisterCount();
|
||||
BitSet registers = new BitSet(registerCount);
|
||||
BitSet mergeRegisters = null;
|
||||
|
||||
if ((registerInfo & main.ALL) != 0) {
|
||||
if ((registerInfo & baksmaliOptions.ALL) != 0) {
|
||||
registers.set(0, registerCount);
|
||||
} else {
|
||||
if ((registerInfo & main.ALLPRE) != 0) {
|
||||
if ((registerInfo & baksmaliOptions.ALLPRE) != 0) {
|
||||
registers.set(0, registerCount);
|
||||
} else {
|
||||
if ((registerInfo & main.ARGS) != 0) {
|
||||
if ((registerInfo & baksmaliOptions.ARGS) != 0) {
|
||||
addArgsRegs(registers);
|
||||
}
|
||||
if ((registerInfo & main.DIFFPRE) != 0) {
|
||||
addDiffRegs(registers);
|
||||
}
|
||||
if ((registerInfo & main.MERGE) != 0) {
|
||||
addMergeRegs(registers, registerCount);
|
||||
} else if ((registerInfo & main.FULLMERGE) != 0 &&
|
||||
if ((registerInfo & baksmaliOptions.MERGE) != 0) {
|
||||
if (analyzedInstruction.isBeginningInstruction()) {
|
||||
addParamRegs(registers, registerCount);
|
||||
}
|
||||
mergeRegisters = new BitSet(registerCount);
|
||||
addMergeRegs(mergeRegisters, registerCount);
|
||||
} else if ((registerInfo & baksmaliOptions.FULLMERGE) != 0 &&
|
||||
(analyzedInstruction.isBeginningInstruction())) {
|
||||
addParamRegs(registers, registerCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean printedSomething = false;
|
||||
if ((registerInfo & main.FULLMERGE) != 0) {
|
||||
printedSomething = writeFullMergeRegs(writer, registers, registerCount);
|
||||
if ((registerInfo & baksmaliOptions.FULLMERGE) != 0) {
|
||||
if (mergeRegisters == null) {
|
||||
mergeRegisters = new BitSet(registerCount);
|
||||
addMergeRegs(mergeRegisters, registerCount);
|
||||
}
|
||||
registers.or(mergeRegisters);
|
||||
} else if (mergeRegisters != null) {
|
||||
registers.or(mergeRegisters);
|
||||
mergeRegisters = null;
|
||||
}
|
||||
|
||||
printedSomething |= writeRegisterInfo(writer, registers, printedSomething);
|
||||
|
||||
return printedSomething;
|
||||
return writeRegisterInfo(writer, registers, mergeRegisters);
|
||||
}
|
||||
|
||||
private void addArgsRegs(BitSet registers) {
|
||||
@ -101,25 +109,25 @@ public class PreInstructionRegisterInfoMethodItem extends MethodItem {
|
||||
RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.getInstruction();
|
||||
|
||||
registers.set(instruction.getStartRegister(),
|
||||
instruction.getStartRegister() + instruction.getRegCount());
|
||||
instruction.getStartRegister() + instruction.getRegisterCount());
|
||||
} else if (analyzedInstruction.getInstruction() instanceof FiveRegisterInstruction) {
|
||||
FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.getInstruction();
|
||||
int regCount = instruction.getRegCount();
|
||||
int regCount = instruction.getRegisterCount();
|
||||
switch (regCount) {
|
||||
case 5:
|
||||
registers.set(instruction.getRegisterA());
|
||||
//fall through
|
||||
case 4:
|
||||
registers.set(instruction.getRegisterG());
|
||||
//fall through
|
||||
case 3:
|
||||
case 4:
|
||||
registers.set(instruction.getRegisterF());
|
||||
//fall through
|
||||
case 2:
|
||||
case 3:
|
||||
registers.set(instruction.getRegisterE());
|
||||
//fall through
|
||||
case 1:
|
||||
case 2:
|
||||
registers.set(instruction.getRegisterD());
|
||||
//fall through
|
||||
case 1:
|
||||
registers.set(instruction.getRegisterC());
|
||||
}
|
||||
} else if (analyzedInstruction.getInstruction() instanceof ThreeRegisterInstruction) {
|
||||
ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.getInstruction();
|
||||
@ -130,30 +138,13 @@ public class PreInstructionRegisterInfoMethodItem extends MethodItem {
|
||||
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.getInstruction();
|
||||
registers.set(instruction.getRegisterA());
|
||||
registers.set(instruction.getRegisterB());
|
||||
} else if (analyzedInstruction.getInstruction() instanceof SingleRegisterInstruction) {
|
||||
SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.getInstruction();
|
||||
} else if (analyzedInstruction.getInstruction() instanceof OneRegisterInstruction) {
|
||||
OneRegisterInstruction instruction = (OneRegisterInstruction)analyzedInstruction.getInstruction();
|
||||
registers.set(instruction.getRegisterA());
|
||||
}
|
||||
}
|
||||
|
||||
private void addDiffRegs(BitSet registers) {
|
||||
if (!analyzedInstruction.isBeginningInstruction()) {
|
||||
for (int i = 0; i < analyzedInstruction.getRegisterCount(); i++) {
|
||||
|
||||
if (lastInstruction.getPreInstructionRegisterType(i).category
|
||||
!= analyzedInstruction.getPreInstructionRegisterType(i).category) {
|
||||
registers.set(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
lastInstruction = analyzedInstruction;
|
||||
}
|
||||
|
||||
private void addMergeRegs(BitSet registers, int registerCount) {
|
||||
if (analyzedInstruction.isBeginningInstruction()) {
|
||||
addParamRegs(registers, registerCount);
|
||||
}
|
||||
|
||||
if (analyzedInstruction.getPredecessorCount() <= 1) {
|
||||
//in the common case of an instruction that only has a single predecessor which is the previous
|
||||
//instruction, the pre-instruction registers will always match the previous instruction's
|
||||
@ -165,118 +156,86 @@ public class PreInstructionRegisterInfoMethodItem extends MethodItem {
|
||||
RegisterType mergedRegisterType = analyzedInstruction.getPreInstructionRegisterType(registerNum);
|
||||
|
||||
for (AnalyzedInstruction predecessor: analyzedInstruction.getPredecessors()) {
|
||||
if (predecessor.getPostInstructionRegisterType(registerNum) != mergedRegisterType) {
|
||||
RegisterType predecessorRegisterType = predecessor.getPostInstructionRegisterType(registerNum);
|
||||
if (predecessorRegisterType.category != RegisterType.UNKNOWN &&
|
||||
!predecessorRegisterType.equals(mergedRegisterType)) {
|
||||
registers.set(registerNum);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addParamRegs(BitSet registers, int registerCount) {
|
||||
ClassDataItem.EncodedMethod encodedMethod = methodAnalyzer.getMethod();
|
||||
int parameterRegisterCount = encodedMethod.method.getPrototype().getParameterRegisterCount();
|
||||
if ((encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0) {
|
||||
parameterRegisterCount++;
|
||||
}
|
||||
|
||||
int parameterRegisterCount = methodAnalyzer.getParamRegisterCount();
|
||||
registers.set(registerCount-parameterRegisterCount, registerCount);
|
||||
}
|
||||
|
||||
private boolean writeFullMergeRegs(IndentingWriter writer, BitSet registers, int registerCount)
|
||||
throws IOException {
|
||||
if (analyzedInstruction.getPredecessorCount() <= 1) {
|
||||
return false;
|
||||
}
|
||||
private void writeFullMerge(IndentingWriter writer, int registerNum) throws IOException {
|
||||
registerFormatter.writeTo(writer, registerNum);
|
||||
writer.write('=');
|
||||
analyzedInstruction.getPreInstructionRegisterType(registerNum).writeTo(writer);
|
||||
writer.write(":merge{");
|
||||
|
||||
ClassDataItem.EncodedMethod encodedMethod = methodAnalyzer.getMethod();
|
||||
boolean first = true;
|
||||
|
||||
boolean firstRegister = true;
|
||||
for (AnalyzedInstruction predecessor: analyzedInstruction.getPredecessors()) {
|
||||
RegisterType predecessorRegisterType = predecessor.getPostInstructionRegisterType(registerNum);
|
||||
|
||||
for (int registerNum=0; registerNum<registerCount; registerNum++) {
|
||||
RegisterType mergedRegisterType = analyzedInstruction.getPreInstructionRegisterType(registerNum);
|
||||
boolean addRegister = false;
|
||||
|
||||
for (AnalyzedInstruction predecessor: analyzedInstruction.getPredecessors()) {
|
||||
RegisterType predecessorRegisterType = predecessor.getPostInstructionRegisterType(registerNum);
|
||||
if (predecessorRegisterType.category != RegisterType.Category.Unknown &&
|
||||
predecessorRegisterType != mergedRegisterType) {
|
||||
|
||||
addRegister = true;
|
||||
break;
|
||||
}
|
||||
if (!first) {
|
||||
writer.write(',');
|
||||
}
|
||||
|
||||
if (!addRegister) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (firstRegister) {
|
||||
firstRegister = false;
|
||||
if (predecessor.getInstructionIndex() == -1) {
|
||||
//the fake "StartOfMethod" instruction
|
||||
writer.write("Start:");
|
||||
} else {
|
||||
writer.write('\n');
|
||||
writer.write("0x");
|
||||
writer.printUnsignedLongAsHex(methodAnalyzer.getInstructionAddress(predecessor));
|
||||
writer.write(':');
|
||||
}
|
||||
predecessorRegisterType.writeTo(writer);
|
||||
|
||||
writer.write('#');
|
||||
RegisterFormatter.writeTo(writer, encodedMethod.codeItem, registerNum);
|
||||
writer.write('=');
|
||||
analyzedInstruction.getPreInstructionRegisterType(registerNum).writeTo(writer);
|
||||
writer.write(":merge{");
|
||||
|
||||
boolean first = true;
|
||||
|
||||
for (AnalyzedInstruction predecessor: analyzedInstruction.getPredecessors()) {
|
||||
RegisterType predecessorRegisterType = predecessor.getPostInstructionRegisterType(registerNum);
|
||||
|
||||
if (!first) {
|
||||
writer.write(',');
|
||||
}
|
||||
|
||||
if (predecessor.getInstructionIndex() == -1) {
|
||||
//the fake "StartOfMethod" instruction
|
||||
writer.write("Start:");
|
||||
} else {
|
||||
writer.write("0x");
|
||||
writer.printUnsignedLongAsHex(methodAnalyzer.getInstructionAddress(predecessor));
|
||||
writer.write(':');
|
||||
}
|
||||
predecessorRegisterType.writeTo(writer);
|
||||
|
||||
first = false;
|
||||
}
|
||||
writer.write('}');
|
||||
|
||||
registers.clear(registerNum);
|
||||
first = false;
|
||||
}
|
||||
return !firstRegister;
|
||||
writer.write('}');
|
||||
}
|
||||
|
||||
private boolean writeRegisterInfo(IndentingWriter writer, BitSet registers,
|
||||
boolean addNewline) throws IOException {
|
||||
ClassDataItem.EncodedMethod encodedMethod = methodAnalyzer.getMethod();
|
||||
|
||||
BitSet fullMergeRegisters) throws IOException {
|
||||
boolean firstRegister = true;
|
||||
boolean previousWasFullMerge = false;
|
||||
int registerNum = registers.nextSetBit(0);
|
||||
if (registerNum < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (addNewline) {
|
||||
writer.write('\n');
|
||||
}
|
||||
writer.write('#');
|
||||
for (; registerNum >= 0; registerNum = registers.nextSetBit(registerNum + 1)) {
|
||||
|
||||
RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(registerNum);
|
||||
|
||||
RegisterFormatter.writeTo(writer, encodedMethod.codeItem, registerNum);
|
||||
writer.write('=');
|
||||
|
||||
if (registerType == null) {
|
||||
writer.write("null");
|
||||
boolean fullMerge = fullMergeRegisters!=null && fullMergeRegisters.get(registerNum);
|
||||
if (fullMerge) {
|
||||
if (!firstRegister) {
|
||||
writer.write('\n');
|
||||
writer.write('#');
|
||||
}
|
||||
writeFullMerge(writer, registerNum);
|
||||
previousWasFullMerge = true;
|
||||
} else {
|
||||
if (previousWasFullMerge) {
|
||||
writer.write('\n');
|
||||
writer.write('#');
|
||||
previousWasFullMerge = false;
|
||||
}
|
||||
|
||||
RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(registerNum);
|
||||
|
||||
registerFormatter.writeTo(writer, registerNum);
|
||||
writer.write('=');
|
||||
|
||||
registerType.writeTo(writer);
|
||||
writer.write(';');
|
||||
}
|
||||
writer.write(';');
|
||||
|
||||
firstRegister = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -28,52 +28,35 @@
|
||||
|
||||
package org.jf.baksmali.Adaptors;
|
||||
|
||||
import org.jf.dexlib2.ReferenceType;
|
||||
import org.jf.dexlib2.iface.reference.*;
|
||||
import org.jf.dexlib2.util.ReferenceUtil;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.jf.dexlib.*;
|
||||
import org.jf.dexlib.Util.Utf8Utils;
|
||||
import org.jf.util.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ReferenceFormatter {
|
||||
public static void writeReference(IndentingWriter writer, Item item) throws IOException {
|
||||
switch (item.getItemType()) {
|
||||
case TYPE_METHOD_ID_ITEM:
|
||||
writeMethodReference(writer, (MethodIdItem)item);
|
||||
public static void writeStringReference(IndentingWriter writer, String item) throws IOException {
|
||||
writer.write('"');
|
||||
StringUtils.writeEscapedString(writer, item);
|
||||
writer.write('"');
|
||||
}
|
||||
|
||||
public static void writeReference(IndentingWriter writer, int referenceType,
|
||||
Reference reference) throws IOException {
|
||||
switch (referenceType) {
|
||||
case ReferenceType.STRING:
|
||||
writeStringReference(writer, ((StringReference)reference).getString());
|
||||
return;
|
||||
case TYPE_FIELD_ID_ITEM:
|
||||
writeFieldReference(writer, (FieldIdItem)item);
|
||||
case ReferenceType.TYPE:
|
||||
writer.write(((TypeReference)reference).getType());
|
||||
return;
|
||||
case TYPE_STRING_ID_ITEM:
|
||||
writeStringReference(writer, (StringIdItem)item);
|
||||
return;
|
||||
case TYPE_TYPE_ID_ITEM:
|
||||
writeTypeReference(writer, (TypeIdItem)item);
|
||||
case ReferenceType.METHOD:
|
||||
ReferenceUtil.writeMethodDescriptor(writer, (MethodReference)reference);
|
||||
return;
|
||||
case ReferenceType.FIELD:
|
||||
ReferenceUtil.writeFieldDescriptor(writer, (FieldReference)reference);
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeMethodReference(IndentingWriter writer, MethodIdItem item) throws IOException {
|
||||
writer.write(item.getContainingClass().getTypeDescriptor());
|
||||
writer.write("->");
|
||||
writer.write(item.getMethodName().getStringValue());
|
||||
writer.write(item.getPrototype().getPrototypeString());
|
||||
}
|
||||
|
||||
public static void writeFieldReference(IndentingWriter writer, FieldIdItem item) throws IOException {
|
||||
writer.write(item.getContainingClass().getTypeDescriptor());
|
||||
writer.write("->");
|
||||
writer.write(item.getFieldName().getStringValue());
|
||||
writer.write(':');
|
||||
writer.write(item.getFieldType().getTypeDescriptor());
|
||||
}
|
||||
|
||||
public static void writeStringReference(IndentingWriter writer, StringIdItem item) throws IOException {
|
||||
writer.write('"');
|
||||
Utf8Utils.writeEscapedString(writer, item.getStringValue());
|
||||
writer.write('"');
|
||||
}
|
||||
|
||||
public static void writeTypeReference(IndentingWriter writer, TypeIdItem item) throws IOException {
|
||||
writer.write(item.getTypeDescriptor());
|
||||
}
|
||||
}
|
||||
|
@ -28,17 +28,25 @@
|
||||
|
||||
package org.jf.baksmali.Adaptors;
|
||||
|
||||
import org.jf.baksmali.baksmaliOptions;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.jf.baksmali.baksmali;
|
||||
import org.jf.dexlib.CodeItem;
|
||||
import org.jf.dexlib.Util.AccessFlags;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This class contains the logic used for formatting registers
|
||||
*/
|
||||
public class RegisterFormatter {
|
||||
@Nonnull public final baksmaliOptions options;
|
||||
public final int registerCount;
|
||||
public final int parameterRegisterCount;
|
||||
|
||||
public RegisterFormatter(@Nonnull baksmaliOptions options, int registerCount, int parameterRegisterCount) {
|
||||
this.options = options;
|
||||
this.registerCount = registerCount;
|
||||
this.parameterRegisterCount = parameterRegisterCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write out the register range value used by Format3rc. If baksmali.noParameterRegisters is true, it will always
|
||||
@ -46,19 +54,11 @@ public class RegisterFormatter {
|
||||
* registers, and if so, use the p<n> format for both. If only the last register is a parameter register, it will
|
||||
* use the v<n> format for both, otherwise it would be confusing to have something like {v20 .. p1}
|
||||
* @param writer the <code>IndentingWriter</code> to write to
|
||||
* @param codeItem the <code>CodeItem</code> that the register is from
|
||||
* @param startRegister the first register in the range
|
||||
* @param lastRegister the last register in the range
|
||||
*/
|
||||
public static void writeRegisterRange(IndentingWriter writer, CodeItem codeItem, int startRegister,
|
||||
int lastRegister) throws IOException {
|
||||
assert lastRegister >= startRegister;
|
||||
|
||||
if (!baksmali.noParameterRegisters) {
|
||||
int parameterRegisterCount = codeItem.getParent().method.getPrototype().getParameterRegisterCount()
|
||||
+ (((codeItem.getParent().accessFlags & AccessFlags.STATIC.getValue())==0)?1:0);
|
||||
int registerCount = codeItem.getRegisterCount();
|
||||
|
||||
public void writeRegisterRange(IndentingWriter writer, int startRegister, int lastRegister) throws IOException {
|
||||
if (!options.noParameterRegisters) {
|
||||
assert startRegister <= lastRegister;
|
||||
|
||||
if (startRegister >= registerCount - parameterRegisterCount) {
|
||||
@ -83,14 +83,10 @@ public class RegisterFormatter {
|
||||
* and if so, formats it in the p<n> format instead.
|
||||
*
|
||||
* @param writer the <code>IndentingWriter</code> to write to
|
||||
* @param codeItem the <code>CodeItem</code> that the register is from
|
||||
* @param register the register number
|
||||
*/
|
||||
public static void writeTo(IndentingWriter writer, CodeItem codeItem, int register) throws IOException {
|
||||
if (!baksmali.noParameterRegisters) {
|
||||
int parameterRegisterCount = codeItem.getParent().method.getPrototype().getParameterRegisterCount()
|
||||
+ (((codeItem.getParent().accessFlags & AccessFlags.STATIC.getValue())==0)?1:0);
|
||||
int registerCount = codeItem.getRegisterCount();
|
||||
public void writeTo(IndentingWriter writer, int register) throws IOException {
|
||||
if (!options.noParameterRegisters) {
|
||||
if (register >= registerCount - parameterRegisterCount) {
|
||||
writer.write('p');
|
||||
writer.printSignedIntAsDec((register - (registerCount - parameterRegisterCount)));
|
||||
|
@ -28,16 +28,17 @@
|
||||
|
||||
package org.jf.baksmali.Adaptors;
|
||||
|
||||
import org.jf.dexlib.Code.Analysis.SyntheticAccessorResolver;
|
||||
import static org.jf.dexlib.Code.Analysis.SyntheticAccessorResolver.AccessedMember;
|
||||
import org.jf.dexlib2.ReferenceType;
|
||||
import org.jf.dexlib2.util.SyntheticAccessorResolver;
|
||||
import org.jf.util.ExceptionWithContext;
|
||||
import org.jf.util.IndentingWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class SyntheticAccessCommentMethodItem extends MethodItem {
|
||||
private final AccessedMember accessedMember;
|
||||
private final SyntheticAccessorResolver.AccessedMember accessedMember;
|
||||
|
||||
public SyntheticAccessCommentMethodItem(AccessedMember accessedMember, int codeAddress) {
|
||||
public SyntheticAccessCommentMethodItem(SyntheticAccessorResolver.AccessedMember accessedMember, int codeAddress) {
|
||||
super(codeAddress);
|
||||
this.accessedMember = accessedMember;
|
||||
}
|
||||
@ -48,15 +49,73 @@ public class SyntheticAccessCommentMethodItem extends MethodItem {
|
||||
}
|
||||
|
||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||
writer.write('#');
|
||||
if (accessedMember.accessedMemberType == SyntheticAccessorResolver.METHOD) {
|
||||
writer.write("calls: ");
|
||||
} else if (accessedMember.accessedMemberType == SyntheticAccessorResolver.GETTER) {
|
||||
writer.write("getter for: ");
|
||||
} else {
|
||||
writer.write("setter for: ");
|
||||
writer.write("# ");
|
||||
switch (accessedMember.accessedMemberType) {
|
||||
case SyntheticAccessorResolver.METHOD:
|
||||
writer.write("invokes: ");
|
||||
break;
|
||||
case SyntheticAccessorResolver.GETTER:
|
||||
writer.write("getter for: ");
|
||||
break;
|
||||
case SyntheticAccessorResolver.SETTER:
|
||||
writer.write("setter for: ");
|
||||
break;
|
||||
case SyntheticAccessorResolver.PREFIX_INCREMENT:
|
||||
writer.write("++operator for: ");
|
||||
break;
|
||||
case SyntheticAccessorResolver.POSTFIX_INCREMENT:
|
||||
writer.write("operator++ for: ");
|
||||
break;
|
||||
case SyntheticAccessorResolver.PREFIX_DECREMENT:
|
||||
writer.write("--operator for: ");
|
||||
break;
|
||||
case SyntheticAccessorResolver.POSTFIX_DECREMENT:
|
||||
writer.write("operator-- for: ");
|
||||
break;
|
||||
case SyntheticAccessorResolver.ADD_ASSIGNMENT:
|
||||
writer.write("+= operator for: ");
|
||||
break;
|
||||
case SyntheticAccessorResolver.SUB_ASSIGNMENT:
|
||||
writer.write("-= operator for: ");
|
||||
break;
|
||||
case SyntheticAccessorResolver.MUL_ASSIGNMENT:
|
||||
writer.write("*= operator for: ");
|
||||
break;
|
||||
case SyntheticAccessorResolver.DIV_ASSIGNMENT:
|
||||
writer.write("/= operator for: ");
|
||||
break;
|
||||
case SyntheticAccessorResolver.REM_ASSIGNMENT:
|
||||
writer.write("%= operator for: ");
|
||||
break;
|
||||
case SyntheticAccessorResolver.AND_ASSIGNMENT:
|
||||
writer.write("&= operator for: ");
|
||||
break;
|
||||
case SyntheticAccessorResolver.OR_ASSIGNMENT:
|
||||
writer.write("|= operator for: ");
|
||||
break;
|
||||
case SyntheticAccessorResolver.XOR_ASSIGNMENT:
|
||||
writer.write("^= operator for: ");
|
||||
break;
|
||||
case SyntheticAccessorResolver.SHL_ASSIGNMENT:
|
||||
writer.write("<<= operator for: ");
|
||||
break;
|
||||
case SyntheticAccessorResolver.SHR_ASSIGNMENT:
|
||||
writer.write(">>= operator for: ");
|
||||
break;
|
||||
case SyntheticAccessorResolver.USHR_ASSIGNMENT:
|
||||
writer.write(">>>= operator for: ");
|
||||
break;
|
||||
default:
|
||||
throw new ExceptionWithContext("Unknown access type: %d", accessedMember.accessedMemberType);
|
||||
}
|
||||
ReferenceFormatter.writeReference(writer, accessedMember.accessedMember);
|
||||
|
||||
int referenceType;
|
||||
if (accessedMember.accessedMemberType == SyntheticAccessorResolver.METHOD) {
|
||||
referenceType = ReferenceType.METHOD;
|
||||
} else {
|
||||
referenceType = ReferenceType.FIELD;
|
||||
}
|
||||
ReferenceFormatter.writeReference(writer, referenceType, accessedMember.accessedMember);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -29,14 +29,14 @@
|
||||
package org.jf.baksmali.Renderers;
|
||||
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.jf.dexlib.Util.Utf8Utils;
|
||||
import org.jf.util.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class CharRenderer {
|
||||
public static void writeTo(IndentingWriter writer, char val) throws IOException {
|
||||
writer.write('\'');
|
||||
Utf8Utils.writeEscapedChar(writer, val);
|
||||
StringUtils.writeEscapedChar(writer, val);
|
||||
writer.write('\'');
|
||||
}
|
||||
}
|
||||
|
@ -28,83 +28,37 @@
|
||||
|
||||
package org.jf.baksmali;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Ordering;
|
||||
import org.jf.baksmali.Adaptors.ClassDefinition;
|
||||
import org.jf.dexlib.ClassDefItem;
|
||||
import org.jf.dexlib.Code.Analysis.*;
|
||||
import org.jf.dexlib.DexFile;
|
||||
import org.jf.dexlib2.analysis.ClassPath;
|
||||
import org.jf.dexlib2.iface.ClassDef;
|
||||
import org.jf.dexlib2.iface.DexFile;
|
||||
import org.jf.dexlib2.util.SyntheticAccessorResolver;
|
||||
import org.jf.util.ClassFileNameHandler;
|
||||
import org.jf.util.IndentingWriter;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class baksmali {
|
||||
public static boolean noParameterRegisters = false;
|
||||
public static boolean useLocalsDirective = false;
|
||||
public static boolean useSequentialLabels = false;
|
||||
public static boolean outputDebugInfo = true;
|
||||
public static boolean addCodeOffsets = false;
|
||||
public static boolean noAccessorComments = false;
|
||||
public static boolean deodex = false;
|
||||
public static boolean verify = false;
|
||||
public static InlineMethodResolver inlineResolver = null;
|
||||
public static int registerInfo = 0;
|
||||
public static String bootClassPath;
|
||||
|
||||
public static SyntheticAccessorResolver syntheticAccessorResolver = null;
|
||||
|
||||
public static void disassembleDexFile(String dexFilePath, DexFile dexFile, boolean deodex, String outputDirectory,
|
||||
String[] classPathDirs, String bootClassPath, String extraBootClassPath,
|
||||
boolean noParameterRegisters, boolean useLocalsDirective,
|
||||
boolean useSequentialLabels, boolean outputDebugInfo, boolean addCodeOffsets,
|
||||
boolean noAccessorComments, int registerInfo, boolean verify,
|
||||
boolean ignoreErrors, String inlineTable, boolean checkPackagePrivateAccess)
|
||||
{
|
||||
baksmali.noParameterRegisters = noParameterRegisters;
|
||||
baksmali.useLocalsDirective = useLocalsDirective;
|
||||
baksmali.useSequentialLabels = useSequentialLabels;
|
||||
baksmali.outputDebugInfo = outputDebugInfo;
|
||||
baksmali.addCodeOffsets = addCodeOffsets;
|
||||
baksmali.noAccessorComments = noAccessorComments;
|
||||
baksmali.deodex = deodex;
|
||||
baksmali.registerInfo = registerInfo;
|
||||
baksmali.bootClassPath = bootClassPath;
|
||||
baksmali.verify = verify;
|
||||
|
||||
if (registerInfo != 0 || deodex || verify) {
|
||||
public static boolean disassembleDexFile(DexFile dexFile, final baksmaliOptions options) {
|
||||
if (options.registerInfo != 0 || options.deodex) {
|
||||
try {
|
||||
String[] extraBootClassPathArray = null;
|
||||
if (extraBootClassPath != null && extraBootClassPath.length() > 0) {
|
||||
assert extraBootClassPath.charAt(0) == ':';
|
||||
extraBootClassPathArray = extraBootClassPath.substring(1).split(":");
|
||||
}
|
||||
|
||||
if (dexFile.isOdex() && bootClassPath == null) {
|
||||
//ext.jar is a special case - it is typically the 2nd jar in the boot class path, but it also
|
||||
//depends on classes in framework.jar (typically the 3rd jar in the BCP). If the user didn't
|
||||
//specify a -c option, we should add framework.jar to the boot class path by default, so that it
|
||||
//"just works"
|
||||
if (extraBootClassPathArray == null && isExtJar(dexFilePath)) {
|
||||
extraBootClassPathArray = new String[] {"framework.jar"};
|
||||
}
|
||||
ClassPath.InitializeClassPathFromOdex(classPathDirs, extraBootClassPathArray, dexFilePath, dexFile,
|
||||
checkPackagePrivateAccess);
|
||||
Iterable<String> extraClassPathEntries;
|
||||
if (options.extraClassPathEntries != null) {
|
||||
extraClassPathEntries = options.extraClassPathEntries;
|
||||
} else {
|
||||
String[] bootClassPathArray = null;
|
||||
if (bootClassPath != null) {
|
||||
bootClassPathArray = bootClassPath.split(":");
|
||||
}
|
||||
ClassPath.InitializeClassPath(classPathDirs, bootClassPathArray, extraBootClassPathArray,
|
||||
dexFilePath, dexFile, checkPackagePrivateAccess);
|
||||
extraClassPathEntries = ImmutableList.of();
|
||||
}
|
||||
|
||||
if (inlineTable != null) {
|
||||
inlineResolver = new CustomInlineMethodResolver(inlineTable);
|
||||
}
|
||||
options.classPath = ClassPath.fromClassPath(options.bootClassPathDirs,
|
||||
Iterables.concat(options.bootClassPathEntries, extraClassPathEntries), dexFile,
|
||||
options.apiLevel);
|
||||
} catch (Exception ex) {
|
||||
System.err.println("\n\nError occured while loading boot class path files. Aborting.");
|
||||
ex.printStackTrace(System.err);
|
||||
@ -112,104 +66,125 @@ public class baksmali {
|
||||
}
|
||||
}
|
||||
|
||||
File outputDirectoryFile = new File(outputDirectory);
|
||||
File outputDirectoryFile = new File(options.outputDirectory);
|
||||
if (!outputDirectoryFile.exists()) {
|
||||
if (!outputDirectoryFile.mkdirs()) {
|
||||
System.err.println("Can't create the output directory " + outputDirectory);
|
||||
System.err.println("Can't create the output directory " + options.outputDirectory);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!noAccessorComments) {
|
||||
syntheticAccessorResolver = new SyntheticAccessorResolver(dexFile);
|
||||
}
|
||||
|
||||
//sort the classes, so that if we're on a case-insensitive file system and need to handle classes with file
|
||||
//name collisions, then we'll use the same name for each class, if the dex file goes through multiple
|
||||
//baksmali/smali cycles for some reason. If a class with a colliding name is added or removed, the filenames
|
||||
//may still change of course
|
||||
ArrayList<ClassDefItem> classDefItems = new ArrayList<ClassDefItem>(dexFile.ClassDefsSection.getItems());
|
||||
Collections.sort(classDefItems, new Comparator<ClassDefItem>() {
|
||||
public int compare(ClassDefItem classDefItem1, ClassDefItem classDefItem2) {
|
||||
return classDefItem1.getClassType().getTypeDescriptor().compareTo(classDefItem1.getClassType().getTypeDescriptor());
|
||||
}
|
||||
});
|
||||
List<? extends ClassDef> classDefs = Ordering.natural().sortedCopy(dexFile.getClasses());
|
||||
|
||||
ClassFileNameHandler fileNameHandler = new ClassFileNameHandler(outputDirectoryFile, ".smali");
|
||||
if (!options.noAccessorComments) {
|
||||
options.syntheticAccessorResolver = new SyntheticAccessorResolver(classDefs);
|
||||
}
|
||||
|
||||
for (ClassDefItem classDefItem: classDefItems) {
|
||||
/**
|
||||
* The path for the disassembly file is based on the package name
|
||||
* The class descriptor will look something like:
|
||||
* Ljava/lang/Object;
|
||||
* Where the there is leading 'L' and a trailing ';', and the parts of the
|
||||
* package name are separated by '/'
|
||||
*/
|
||||
final ClassFileNameHandler fileNameHandler = new ClassFileNameHandler(outputDirectoryFile, ".smali");
|
||||
|
||||
String classDescriptor = classDefItem.getClassType().getTypeDescriptor();
|
||||
ExecutorService executor = Executors.newFixedThreadPool(options.jobs);
|
||||
List<Future<Boolean>> tasks = Lists.newArrayList();
|
||||
|
||||
//validate that the descriptor is formatted like we expect
|
||||
if (classDescriptor.charAt(0) != 'L' ||
|
||||
classDescriptor.charAt(classDescriptor.length()-1) != ';') {
|
||||
System.err.println("Unrecognized class descriptor - " + classDescriptor + " - skipping class");
|
||||
continue;
|
||||
}
|
||||
|
||||
File smaliFile = fileNameHandler.getUniqueFilenameForClass(classDescriptor);
|
||||
|
||||
//create and initialize the top level string template
|
||||
ClassDefinition classDefinition = new ClassDefinition(classDefItem);
|
||||
|
||||
//write the disassembly
|
||||
Writer writer = null;
|
||||
try
|
||||
{
|
||||
File smaliParent = smaliFile.getParentFile();
|
||||
if (!smaliParent.exists()) {
|
||||
if (!smaliParent.mkdirs()) {
|
||||
System.err.println("Unable to create directory " + smaliParent.toString() + " - skipping class");
|
||||
continue;
|
||||
}
|
||||
for (final ClassDef classDef: classDefs) {
|
||||
tasks.add(executor.submit(new Callable<Boolean>() {
|
||||
@Override public Boolean call() throws Exception {
|
||||
return disassembleClass(classDef, fileNameHandler, options);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
if (!smaliFile.exists()){
|
||||
if (!smaliFile.createNewFile()) {
|
||||
System.err.println("Unable to create file " + smaliFile.toString() + " - skipping class");
|
||||
continue;
|
||||
boolean errorOccurred = false;
|
||||
for (Future<Boolean> task: tasks) {
|
||||
while(true) {
|
||||
try {
|
||||
if (!task.get()) {
|
||||
errorOccurred = true;
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
continue;
|
||||
} catch (ExecutionException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
BufferedWriter bufWriter = new BufferedWriter(new OutputStreamWriter(
|
||||
new FileOutputStream(smaliFile), "UTF8"));
|
||||
|
||||
writer = new IndentingWriter(bufWriter);
|
||||
classDefinition.writeTo((IndentingWriter)writer);
|
||||
} catch (Exception ex) {
|
||||
System.err.println("\n\nError occured while disassembling class " + classDescriptor.replace('/', '.') + " - skipping class");
|
||||
ex.printStackTrace();
|
||||
smaliFile.delete();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (writer != null) {
|
||||
try {
|
||||
writer.close();
|
||||
} catch (Throwable ex) {
|
||||
System.err.println("\n\nError occured while closing file " + smaliFile.toString());
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ignoreErrors && classDefinition.hadValidationErrors()) {
|
||||
System.exit(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
executor.shutdown();
|
||||
return !errorOccurred;
|
||||
}
|
||||
|
||||
private static final Pattern extJarPattern = Pattern.compile("(?:^|\\\\|/)ext.(?:jar|odex)$");
|
||||
private static boolean isExtJar(String dexFilePath) {
|
||||
Matcher m = extJarPattern.matcher(dexFilePath);
|
||||
return m.find();
|
||||
private static boolean disassembleClass(ClassDef classDef, ClassFileNameHandler fileNameHandler,
|
||||
baksmaliOptions options) {
|
||||
/**
|
||||
* The path for the disassembly file is based on the package name
|
||||
* The class descriptor will look something like:
|
||||
* Ljava/lang/Object;
|
||||
* Where the there is leading 'L' and a trailing ';', and the parts of the
|
||||
* package name are separated by '/'
|
||||
*/
|
||||
String classDescriptor = classDef.getType();
|
||||
|
||||
//validate that the descriptor is formatted like we expect
|
||||
if (classDescriptor.charAt(0) != 'L' ||
|
||||
classDescriptor.charAt(classDescriptor.length()-1) != ';') {
|
||||
System.err.println("Unrecognized class descriptor - " + classDescriptor + " - skipping class");
|
||||
return false;
|
||||
}
|
||||
|
||||
File smaliFile = fileNameHandler.getUniqueFilenameForClass(classDescriptor);
|
||||
|
||||
//create and initialize the top level string template
|
||||
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
|
||||
|
||||
//write the disassembly
|
||||
Writer writer = null;
|
||||
try
|
||||
{
|
||||
File smaliParent = smaliFile.getParentFile();
|
||||
if (!smaliParent.exists()) {
|
||||
if (!smaliParent.mkdirs()) {
|
||||
// check again, it's likely it was created in a different thread
|
||||
if (!smaliParent.exists()) {
|
||||
System.err.println("Unable to create directory " + smaliParent.toString() + " - skipping class");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!smaliFile.exists()){
|
||||
if (!smaliFile.createNewFile()) {
|
||||
System.err.println("Unable to create file " + smaliFile.toString() + " - skipping class");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
BufferedWriter bufWriter = new BufferedWriter(new OutputStreamWriter(
|
||||
new FileOutputStream(smaliFile), "UTF8"));
|
||||
|
||||
writer = new IndentingWriter(bufWriter);
|
||||
classDefinition.writeTo((IndentingWriter)writer);
|
||||
} catch (Exception ex) {
|
||||
System.err.println("\n\nError occured while disassembling class " + classDescriptor.replace('/', '.') + " - skipping class");
|
||||
ex.printStackTrace();
|
||||
// noinspection ResultOfMethodCallIgnored
|
||||
smaliFile.delete();
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (writer != null) {
|
||||
try {
|
||||
writer.close();
|
||||
} catch (Throwable ex) {
|
||||
System.err.println("\n\nError occured while closing file " + smaliFile.toString());
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.baksmali;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.jf.dexlib2.analysis.ClassPath;
|
||||
import org.jf.dexlib2.analysis.InlineMethodResolver;
|
||||
import org.jf.dexlib2.util.SyntheticAccessorResolver;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class baksmaliOptions {
|
||||
// register info values
|
||||
public static final int ALL = 1;
|
||||
public static final int ALLPRE = 2;
|
||||
public static final int ALLPOST = 4;
|
||||
public static final int ARGS = 8;
|
||||
public static final int DEST = 16;
|
||||
public static final int MERGE = 32;
|
||||
public static final int FULLMERGE = 64;
|
||||
|
||||
public int apiLevel = 15;
|
||||
public String outputDirectory = "out";
|
||||
public List<String> bootClassPathDirs = Lists.newArrayList();
|
||||
|
||||
public List<String> bootClassPathEntries = Lists.newArrayList();
|
||||
public List<String> extraClassPathEntries = Lists.newArrayList();
|
||||
|
||||
public boolean noParameterRegisters = false;
|
||||
public boolean useLocalsDirective = false;
|
||||
public boolean useSequentialLabels = false;
|
||||
public boolean outputDebugInfo = true;
|
||||
public boolean addCodeOffsets = false;
|
||||
public boolean noAccessorComments = false;
|
||||
public boolean deodex = false;
|
||||
public boolean ignoreErrors = false;
|
||||
public boolean checkPackagePrivateAccess = false;
|
||||
public InlineMethodResolver inlineResolver = null;
|
||||
public int registerInfo = 0;
|
||||
public ClassPath classPath = null;
|
||||
public int jobs = -1;
|
||||
|
||||
public SyntheticAccessorResolver syntheticAccessorResolver = null;
|
||||
|
||||
public void setBootClassPath(String bootClassPath) {
|
||||
bootClassPathEntries = Lists.newArrayList(bootClassPath.split(":"));
|
||||
}
|
||||
|
||||
public void addExtraClassPath(String extraClassPath) {
|
||||
if (extraClassPath.startsWith(":")) {
|
||||
extraClassPath = extraClassPath.substring(1);
|
||||
}
|
||||
extraClassPathEntries.addAll(Arrays.asList(extraClassPath.split(":")));
|
||||
}
|
||||
}
|
@ -28,79 +28,43 @@
|
||||
|
||||
package org.jf.baksmali;
|
||||
|
||||
import org.jf.dexlib.DexFile;
|
||||
import org.jf.dexlib.Util.ByteArrayAnnotatedOutput;
|
||||
import org.jf.dexlib2.Opcodes;
|
||||
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
|
||||
import org.jf.dexlib2.dexbacked.raw.RawDexFile;
|
||||
import org.jf.dexlib2.dexbacked.raw.util.DexAnnotator;
|
||||
import org.jf.util.ConsoleUtil;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
public class dump {
|
||||
public static void dump(DexFile dexFile, String dumpFileName, String outputDexFileName, boolean sort)
|
||||
throws IOException {
|
||||
|
||||
if (sort) {
|
||||
//sort all items, to guarantee a unique ordering
|
||||
dexFile.setSortAllItems(true);
|
||||
} else {
|
||||
//don't change the order
|
||||
dexFile.setInplace(true);
|
||||
}
|
||||
|
||||
ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
|
||||
|
||||
public static void dump(DexBackedDexFile dexFile, String dumpFileName, int apiLevel) throws IOException {
|
||||
if (dumpFileName != null) {
|
||||
out.enableAnnotations(120, true);
|
||||
}
|
||||
|
||||
dexFile.place();
|
||||
dexFile.writeTo(out);
|
||||
|
||||
//write the dump
|
||||
if (dumpFileName != null) {
|
||||
out.finishAnnotating();
|
||||
FileWriter writer = null;
|
||||
|
||||
Writer writer = null;
|
||||
|
||||
try {
|
||||
writer = new FileWriter(dumpFileName);
|
||||
out.writeAnnotationsTo(writer);
|
||||
writer = new BufferedWriter(new FileWriter(dumpFileName));
|
||||
|
||||
int consoleWidth = ConsoleUtil.getConsoleWidth();
|
||||
if (consoleWidth <= 0) {
|
||||
consoleWidth = 120;
|
||||
}
|
||||
|
||||
RawDexFile rawDexFile = new RawDexFile(new Opcodes(apiLevel), dexFile);
|
||||
DexAnnotator annotator = new DexAnnotator(rawDexFile, consoleWidth);
|
||||
annotator.writeAnnotations(writer);
|
||||
} catch (IOException ex) {
|
||||
System.err.println("\n\nThere was an error while dumping the dex file to " + dumpFileName);
|
||||
ex.printStackTrace();
|
||||
System.err.println("There was an error while dumping the dex file to " + dumpFileName);
|
||||
ex.printStackTrace(System.err);
|
||||
} finally {
|
||||
if (writer != null) {
|
||||
try {
|
||||
writer.close();
|
||||
} catch (IOException ex) {
|
||||
System.err.println("\n\nThere was an error while closing the dump file " + dumpFileName);
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//rewrite the dex file
|
||||
if (outputDexFileName != null) {
|
||||
byte[] bytes = out.toByteArray();
|
||||
|
||||
DexFile.calcSignature(bytes);
|
||||
DexFile.calcChecksum(bytes);
|
||||
|
||||
FileOutputStream fileOutputStream = null;
|
||||
try {
|
||||
fileOutputStream = new FileOutputStream(outputDexFileName);
|
||||
fileOutputStream.write(bytes);
|
||||
} catch (IOException ex) {
|
||||
System.err.println("\n\nThere was an error while writing the dex file " + outputDexFileName);
|
||||
ex.printStackTrace();
|
||||
} finally {
|
||||
if (fileOutputStream != null) {
|
||||
try {
|
||||
fileOutputStream.close();
|
||||
} catch (IOException ex) {
|
||||
System.err.println("\n\nThere was an error while closing the dex file " + outputDexFileName);
|
||||
ex.printStackTrace();
|
||||
System.err.println("There was an error while closing the dump file " + dumpFileName);
|
||||
ex.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,16 +28,20 @@
|
||||
|
||||
package org.jf.baksmali;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.commons.cli.*;
|
||||
import org.jf.dexlib.Code.Opcode;
|
||||
import org.jf.dexlib.DexFile;
|
||||
import org.jf.dexlib2.DexFileFactory;
|
||||
import org.jf.dexlib2.analysis.CustomInlineMethodResolver;
|
||||
import org.jf.dexlib2.analysis.InlineMethodResolver;
|
||||
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
|
||||
import org.jf.dexlib2.dexbacked.DexBackedOdexFile;
|
||||
import org.jf.util.ConsoleUtil;
|
||||
import org.jf.util.SmaliHelpFormatter;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
@ -50,16 +54,6 @@ public class main {
|
||||
private static final Options debugOptions;
|
||||
private static final Options options;
|
||||
|
||||
public static final int ALL = 1;
|
||||
public static final int ALLPRE = 2;
|
||||
public static final int ALLPOST = 4;
|
||||
public static final int ARGS = 8;
|
||||
public static final int DEST = 16;
|
||||
public static final int MERGE = 32;
|
||||
public static final int FULLMERGE = 64;
|
||||
|
||||
public static final int DIFFPRE = 128;
|
||||
|
||||
static {
|
||||
options = new Options();
|
||||
basicOptions = new Options();
|
||||
@ -67,14 +61,19 @@ public class main {
|
||||
buildOptions();
|
||||
|
||||
InputStream templateStream = baksmali.class.getClassLoader().getResourceAsStream("baksmali.properties");
|
||||
Properties properties = new Properties();
|
||||
String version = "(unknown)";
|
||||
try {
|
||||
properties.load(templateStream);
|
||||
version = properties.getProperty("application.version");
|
||||
} catch (IOException ex) {
|
||||
if (templateStream != null) {
|
||||
Properties properties = new Properties();
|
||||
String version = "(unknown)";
|
||||
try {
|
||||
properties.load(templateStream);
|
||||
version = properties.getProperty("application.version");
|
||||
} catch (IOException ex) {
|
||||
// ignore
|
||||
}
|
||||
VERSION = version;
|
||||
} else {
|
||||
VERSION = "[unknown version]";
|
||||
}
|
||||
VERSION = version;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -86,7 +85,7 @@ public class main {
|
||||
/**
|
||||
* Run!
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
public static void main(String[] args) throws IOException {
|
||||
Locale locale = new Locale("en", "US");
|
||||
Locale.setDefault(locale);
|
||||
|
||||
@ -100,43 +99,18 @@ public class main {
|
||||
return;
|
||||
}
|
||||
|
||||
baksmaliOptions options = new baksmaliOptions();
|
||||
|
||||
boolean disassemble = true;
|
||||
boolean doDump = false;
|
||||
boolean write = false;
|
||||
boolean sort = false;
|
||||
boolean fixRegisters = false;
|
||||
boolean noParameterRegisters = false;
|
||||
boolean useLocalsDirective = false;
|
||||
boolean useSequentialLabels = false;
|
||||
boolean outputDebugInfo = true;
|
||||
boolean addCodeOffsets = false;
|
||||
boolean noAccessorComments = false;
|
||||
boolean deodex = false;
|
||||
boolean verify = false;
|
||||
boolean ignoreErrors = false;
|
||||
boolean checkPackagePrivateAccess = false;
|
||||
|
||||
int apiLevel = 14;
|
||||
|
||||
int registerInfo = 0;
|
||||
|
||||
String outputDirectory = "out";
|
||||
String dumpFileName = null;
|
||||
String outputDexFileName = null;
|
||||
String inputDexFileName = null;
|
||||
String bootClassPath = null;
|
||||
StringBuffer extraBootClassPathEntries = new StringBuffer();
|
||||
List<String> bootClassPathDirs = new ArrayList<String>();
|
||||
bootClassPathDirs.add(".");
|
||||
String inlineTable = null;
|
||||
boolean jumboInstructions = false;
|
||||
boolean setBootClassPath = false;
|
||||
|
||||
String[] remainingArgs = commandLine.getArgs();
|
||||
Option[] clOptions = commandLine.getOptions();
|
||||
|
||||
Option[] options = commandLine.getOptions();
|
||||
|
||||
for (int i=0; i<options.length; i++) {
|
||||
Option option = options[i];
|
||||
for (int i=0; i<clOptions.length; i++) {
|
||||
Option option = clOptions[i];
|
||||
String opt = option.getOpt();
|
||||
|
||||
switch (opt.charAt(0)) {
|
||||
@ -144,8 +118,8 @@ public class main {
|
||||
version();
|
||||
return;
|
||||
case '?':
|
||||
while (++i < options.length) {
|
||||
if (options[i].getOpt().charAt(0) == '?') {
|
||||
while (++i < clOptions.length) {
|
||||
if (clOptions[i].getOpt().charAt(0) == '?') {
|
||||
usage(true);
|
||||
return;
|
||||
}
|
||||
@ -153,109 +127,96 @@ public class main {
|
||||
usage(false);
|
||||
return;
|
||||
case 'o':
|
||||
outputDirectory = commandLine.getOptionValue("o");
|
||||
options.outputDirectory = commandLine.getOptionValue("o");
|
||||
break;
|
||||
case 'p':
|
||||
noParameterRegisters = true;
|
||||
options.noParameterRegisters = true;
|
||||
break;
|
||||
case 'l':
|
||||
useLocalsDirective = true;
|
||||
options.useLocalsDirective = true;
|
||||
break;
|
||||
case 's':
|
||||
useSequentialLabels = true;
|
||||
options.useSequentialLabels = true;
|
||||
break;
|
||||
case 'b':
|
||||
outputDebugInfo = false;
|
||||
options.outputDebugInfo = false;
|
||||
break;
|
||||
case 'd':
|
||||
bootClassPathDirs.add(option.getValue());
|
||||
options.bootClassPathDirs.add(option.getValue());
|
||||
break;
|
||||
case 'f':
|
||||
addCodeOffsets = true;
|
||||
options.addCodeOffsets = true;
|
||||
break;
|
||||
case 'r':
|
||||
String[] values = commandLine.getOptionValues('r');
|
||||
int registerInfo = 0;
|
||||
|
||||
if (values == null || values.length == 0) {
|
||||
registerInfo = ARGS | DEST;
|
||||
registerInfo = baksmaliOptions.ARGS | baksmaliOptions.DEST;
|
||||
} else {
|
||||
for (String value: values) {
|
||||
if (value.equalsIgnoreCase("ALL")) {
|
||||
registerInfo |= ALL;
|
||||
registerInfo |= baksmaliOptions.ALL;
|
||||
} else if (value.equalsIgnoreCase("ALLPRE")) {
|
||||
registerInfo |= ALLPRE;
|
||||
registerInfo |= baksmaliOptions.ALLPRE;
|
||||
} else if (value.equalsIgnoreCase("ALLPOST")) {
|
||||
registerInfo |= ALLPOST;
|
||||
registerInfo |= baksmaliOptions.ALLPOST;
|
||||
} else if (value.equalsIgnoreCase("ARGS")) {
|
||||
registerInfo |= ARGS;
|
||||
registerInfo |= baksmaliOptions.ARGS;
|
||||
} else if (value.equalsIgnoreCase("DEST")) {
|
||||
registerInfo |= DEST;
|
||||
registerInfo |= baksmaliOptions.DEST;
|
||||
} else if (value.equalsIgnoreCase("MERGE")) {
|
||||
registerInfo |= MERGE;
|
||||
registerInfo |= baksmaliOptions.MERGE;
|
||||
} else if (value.equalsIgnoreCase("FULLMERGE")) {
|
||||
registerInfo |= FULLMERGE;
|
||||
registerInfo |= baksmaliOptions.FULLMERGE;
|
||||
} else {
|
||||
usage();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ((registerInfo & FULLMERGE) != 0) {
|
||||
registerInfo &= ~MERGE;
|
||||
if ((registerInfo & baksmaliOptions.FULLMERGE) != 0) {
|
||||
registerInfo &= ~baksmaliOptions.MERGE;
|
||||
}
|
||||
}
|
||||
options.registerInfo = registerInfo;
|
||||
break;
|
||||
case 'c':
|
||||
String bcp = commandLine.getOptionValue("c");
|
||||
if (bcp != null && bcp.charAt(0) == ':') {
|
||||
extraBootClassPathEntries.append(bcp);
|
||||
options.addExtraClassPath(bcp);
|
||||
} else {
|
||||
bootClassPath = bcp;
|
||||
setBootClassPath = true;
|
||||
options.setBootClassPath(bcp);
|
||||
}
|
||||
break;
|
||||
case 'x':
|
||||
deodex = true;
|
||||
options.deodex = true;
|
||||
break;
|
||||
case 'm':
|
||||
noAccessorComments = true;
|
||||
options.noAccessorComments = true;
|
||||
break;
|
||||
case 'a':
|
||||
apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
|
||||
if (apiLevel >= 17) {
|
||||
checkPackagePrivateAccess = true;
|
||||
}
|
||||
options.apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
|
||||
break;
|
||||
case 'j':
|
||||
options.jobs = Integer.parseInt(commandLine.getOptionValue("j"));
|
||||
break;
|
||||
case 'N':
|
||||
disassemble = false;
|
||||
break;
|
||||
case 'D':
|
||||
doDump = true;
|
||||
dumpFileName = commandLine.getOptionValue("D", inputDexFileName + ".dump");
|
||||
dumpFileName = commandLine.getOptionValue("D");
|
||||
break;
|
||||
case 'I':
|
||||
ignoreErrors = true;
|
||||
break;
|
||||
case 'J':
|
||||
jumboInstructions = true;
|
||||
break;
|
||||
case 'W':
|
||||
write = true;
|
||||
outputDexFileName = commandLine.getOptionValue("W");
|
||||
break;
|
||||
case 'S':
|
||||
sort = true;
|
||||
break;
|
||||
case 'F':
|
||||
fixRegisters = true;
|
||||
break;
|
||||
case 'V':
|
||||
verify = true;
|
||||
options.ignoreErrors = true;
|
||||
break;
|
||||
case 'T':
|
||||
inlineTable = commandLine.getOptionValue("T");
|
||||
options.inlineResolver = new CustomInlineMethodResolver(options.classPath, new File(commandLine.getOptionValue("T")));
|
||||
break;
|
||||
case 'K':
|
||||
checkPackagePrivateAccess = true;
|
||||
options.checkPackagePrivateAccess = true;
|
||||
break;
|
||||
default:
|
||||
assert false;
|
||||
@ -267,68 +228,60 @@ public class main {
|
||||
return;
|
||||
}
|
||||
|
||||
inputDexFileName = remainingArgs[0];
|
||||
|
||||
try {
|
||||
File dexFileFile = new File(inputDexFileName);
|
||||
if (!dexFileFile.exists()) {
|
||||
System.err.println("Can't find the file " + inputDexFileName);
|
||||
System.exit(1);
|
||||
if (options.jobs <= 0) {
|
||||
options.jobs = Runtime.getRuntime().availableProcessors();
|
||||
if (options.jobs > 6) {
|
||||
options.jobs = 6;
|
||||
}
|
||||
}
|
||||
|
||||
Opcode.updateMapsForApiLevel(apiLevel, jumboInstructions);
|
||||
String inputDexFileName = remainingArgs[0];
|
||||
|
||||
//Read in and parse the dex file
|
||||
DexFile dexFile = new DexFile(dexFileFile, !fixRegisters, false);
|
||||
|
||||
if (dexFile.isOdex()) {
|
||||
if (doDump) {
|
||||
System.err.println("-D cannot be used with on odex file. Ignoring -D");
|
||||
}
|
||||
if (write) {
|
||||
System.err.println("-W cannot be used with an odex file. Ignoring -W");
|
||||
}
|
||||
if (!deodex) {
|
||||
System.err.println("Warning: You are disassembling an odex file without deodexing it. You");
|
||||
System.err.println("won't be able to re-assemble the results unless you deodex it with the -x");
|
||||
System.err.println("option");
|
||||
}
|
||||
} else {
|
||||
deodex = false;
|
||||
|
||||
if (bootClassPath == null) {
|
||||
bootClassPath = "core.jar:ext.jar:framework.jar:android.policy.jar:services.jar";
|
||||
}
|
||||
}
|
||||
|
||||
if (disassemble) {
|
||||
String[] bootClassPathDirsArray = new String[bootClassPathDirs.size()];
|
||||
for (int i=0; i<bootClassPathDirsArray.length; i++) {
|
||||
bootClassPathDirsArray[i] = bootClassPathDirs.get(i);
|
||||
}
|
||||
|
||||
baksmali.disassembleDexFile(dexFileFile.getPath(), dexFile, deodex, outputDirectory,
|
||||
bootClassPathDirsArray, bootClassPath, extraBootClassPathEntries.toString(),
|
||||
noParameterRegisters, useLocalsDirective, useSequentialLabels, outputDebugInfo, addCodeOffsets,
|
||||
noAccessorComments, registerInfo, verify, ignoreErrors, inlineTable, checkPackagePrivateAccess);
|
||||
}
|
||||
|
||||
if ((doDump || write) && !dexFile.isOdex()) {
|
||||
try
|
||||
{
|
||||
dump.dump(dexFile, dumpFileName, outputDexFileName, sort);
|
||||
}catch (IOException ex) {
|
||||
System.err.println("Error occured while writing dump file");
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException ex) {
|
||||
System.err.println("\n\nUNEXPECTED TOP-LEVEL EXCEPTION:");
|
||||
ex.printStackTrace();
|
||||
File dexFileFile = new File(inputDexFileName);
|
||||
if (!dexFileFile.exists()) {
|
||||
System.err.println("Can't find the file " + inputDexFileName);
|
||||
System.exit(1);
|
||||
} catch (Throwable ex) {
|
||||
System.err.println("\n\nUNEXPECTED TOP-LEVEL ERROR:");
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
//Read in and parse the dex file
|
||||
DexBackedDexFile dexFile = DexFileFactory.loadDexFile(dexFileFile, options.apiLevel);
|
||||
|
||||
if (dexFile.isOdexFile()) {
|
||||
if (!options.deodex) {
|
||||
System.err.println("Warning: You are disassembling an odex file without deodexing it. You");
|
||||
System.err.println("won't be able to re-assemble the results unless you deodex it with the -x");
|
||||
System.err.println("option");
|
||||
}
|
||||
} else {
|
||||
options.deodex = false;
|
||||
}
|
||||
|
||||
if (!setBootClassPath && (options.deodex || options.registerInfo != 0)) {
|
||||
if (dexFile instanceof DexBackedOdexFile) {
|
||||
options.bootClassPathEntries = ((DexBackedOdexFile)dexFile).getDependencies();
|
||||
} else {
|
||||
options.bootClassPathEntries = getDefaultBootClassPathForApi(options.apiLevel);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.inlineResolver == null && dexFile instanceof DexBackedOdexFile) {
|
||||
options.inlineResolver =
|
||||
InlineMethodResolver.createInlineMethodResolver(((DexBackedOdexFile)dexFile).getOdexVersion());
|
||||
}
|
||||
|
||||
boolean errorOccurred = false;
|
||||
if (disassemble) {
|
||||
errorOccurred = !baksmali.disassembleDexFile(dexFile, options);
|
||||
}
|
||||
|
||||
if (doDump) {
|
||||
if (dumpFileName == null) {
|
||||
dumpFileName = commandLine.getOptionValue(inputDexFileName + ".dump");
|
||||
}
|
||||
dump.dump(dexFile, dumpFileName, options.apiLevel);
|
||||
}
|
||||
|
||||
if (errorOccurred) {
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
@ -363,6 +316,7 @@ public class main {
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
@SuppressWarnings("AccessStaticViaInstance")
|
||||
private static void buildOptions() {
|
||||
Option versionOption = OptionBuilder.withLongOpt("version")
|
||||
.withDescription("prints the version then exits")
|
||||
@ -441,11 +395,18 @@ public class main {
|
||||
|
||||
Option apiLevelOption = OptionBuilder.withLongOpt("api-level")
|
||||
.withDescription("The numeric api-level of the file being disassembled. If not " +
|
||||
"specified, it defaults to 14 (ICS).")
|
||||
"specified, it defaults to 15 (ICS).")
|
||||
.hasArg()
|
||||
.withArgName("API_LEVEL")
|
||||
.create("a");
|
||||
|
||||
Option jobsOption = OptionBuilder.withLongOpt("jobs")
|
||||
.withDescription("The number of threads to use. Defaults to the number of cores available, up to a " +
|
||||
"maximum of 6")
|
||||
.hasArg()
|
||||
.withArgName("NUM_THREADS")
|
||||
.create("j");
|
||||
|
||||
Option dumpOption = OptionBuilder.withLongOpt("dump-to")
|
||||
.withDescription("dumps the given dex file into a single annotated dump file named FILE" +
|
||||
" (<dexfile>.dump by default), along with the normal disassembly")
|
||||
@ -459,42 +420,22 @@ public class main {
|
||||
" behavior is to stop disassembling and exit once an error is encountered")
|
||||
.create("I");
|
||||
|
||||
Option jumboInstructionsOption = OptionBuilder.withLongOpt("jumbo-instructions")
|
||||
.withDescription("adds support for the jumbo opcodes that were temporarily available around the" +
|
||||
" ics timeframe. Note that support for these opcodes was removed from newer version of" +
|
||||
" dalvik. You shouldn't use this option unless you know the dex file contains these jumbo" +
|
||||
" opcodes.")
|
||||
.create("J");
|
||||
|
||||
Option noDisassemblyOption = OptionBuilder.withLongOpt("no-disassembly")
|
||||
.withDescription("suppresses the output of the disassembly")
|
||||
.create("N");
|
||||
|
||||
Option writeDexOption = OptionBuilder.withLongOpt("write-dex")
|
||||
.withDescription("additionally rewrites the input dex file to FILE")
|
||||
.hasArg()
|
||||
.withArgName("FILE")
|
||||
.create("W");
|
||||
|
||||
Option sortOption = OptionBuilder.withLongOpt("sort")
|
||||
.withDescription("sort the items in the dex file into a canonical order before dumping/writing")
|
||||
.create("S");
|
||||
|
||||
Option fixSignedRegisterOption = OptionBuilder.withLongOpt("fix-signed-registers")
|
||||
.withDescription("when dumping or rewriting, fix any registers in the debug info that are encoded as" +
|
||||
" a signed value")
|
||||
.create("F");
|
||||
|
||||
Option verifyDexOption = OptionBuilder.withLongOpt("verify")
|
||||
.withDescription("perform bytecode verification")
|
||||
.create("V");
|
||||
|
||||
Option inlineTableOption = OptionBuilder.withLongOpt("inline-table")
|
||||
.withDescription("specify a file containing a custom inline method table to use for deodexing")
|
||||
.hasArg()
|
||||
.withArgName("FILE")
|
||||
.create("T");
|
||||
|
||||
Option checkPackagePrivateAccess = OptionBuilder.withLongOpt("check-package-private-access")
|
||||
.withDescription("When deodexing, use the new virtual table generation logic that " +
|
||||
"prevents overriding an inaccessible package private method. This is a temporary option " +
|
||||
"that will be removed once this new functionality can be tied to a specific api level.")
|
||||
.create("K");
|
||||
|
||||
basicOptions.addOption(versionOption);
|
||||
basicOptions.addOption(helpOption);
|
||||
basicOptions.addOption(outputDirOption);
|
||||
@ -509,16 +450,13 @@ public class main {
|
||||
basicOptions.addOption(codeOffsetOption);
|
||||
basicOptions.addOption(noAccessorCommentsOption);
|
||||
basicOptions.addOption(apiLevelOption);
|
||||
basicOptions.addOption(jobsOption);
|
||||
|
||||
debugOptions.addOption(dumpOption);
|
||||
debugOptions.addOption(ignoreErrorsOption);
|
||||
debugOptions.addOption(jumboInstructionsOption);
|
||||
debugOptions.addOption(noDisassemblyOption);
|
||||
debugOptions.addOption(writeDexOption);
|
||||
debugOptions.addOption(sortOption);
|
||||
debugOptions.addOption(fixSignedRegisterOption);
|
||||
debugOptions.addOption(verifyDexOption);
|
||||
debugOptions.addOption(inlineTableOption);
|
||||
debugOptions.addOption(checkPackagePrivateAccess);
|
||||
|
||||
for (Object option: basicOptions.getOptions()) {
|
||||
options.addOption((Option)option);
|
||||
@ -527,4 +465,60 @@ public class main {
|
||||
options.addOption((Option)option);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static List<String> getDefaultBootClassPathForApi(int apiLevel) {
|
||||
if (apiLevel < 9) {
|
||||
return Lists.newArrayList(
|
||||
"/system/framework/core.jar",
|
||||
"/system/framework/ext.jar",
|
||||
"/system/framework/framework.jar",
|
||||
"/system/framework/android.policy.jar",
|
||||
"/system/framework/services.jar");
|
||||
} else if (apiLevel < 12) {
|
||||
return Lists.newArrayList(
|
||||
"/system/framework/core.jar",
|
||||
"/system/framework/bouncycastle.jar",
|
||||
"/system/framework/ext.jar",
|
||||
"/system/framework/framework.jar",
|
||||
"/system/framework/android.policy.jar",
|
||||
"/system/framework/services.jar",
|
||||
"/system/framework/core-junit.jar");
|
||||
} else if (apiLevel < 14) {
|
||||
return Lists.newArrayList(
|
||||
"/system/framework/core.jar",
|
||||
"/system/framework/apache-xml.jar",
|
||||
"/system/framework/bouncycastle.jar",
|
||||
"/system/framework/ext.jar",
|
||||
"/system/framework/framework.jar",
|
||||
"/system/framework/android.policy.jar",
|
||||
"/system/framework/services.jar",
|
||||
"/system/framework/core-junit.jar");
|
||||
} else if (apiLevel < 16) {
|
||||
return Lists.newArrayList(
|
||||
"/system/framework/core.jar",
|
||||
"/system/framework/core-junit.jar",
|
||||
"/system/framework/bouncycastle.jar",
|
||||
"/system/framework/ext.jar",
|
||||
"/system/framework/framework.jar",
|
||||
"/system/framework/android.policy.jar",
|
||||
"/system/framework/services.jar",
|
||||
"/system/framework/apache-xml.jar",
|
||||
"/system/framework/filterfw.jar");
|
||||
|
||||
} else {
|
||||
// this is correct as of api 17/4.2.2
|
||||
return Lists.newArrayList(
|
||||
"/system/framework/core.jar",
|
||||
"/system/framework/core-junit.jar",
|
||||
"/system/framework/bouncycastle.jar",
|
||||
"/system/framework/ext.jar",
|
||||
"/system/framework/framework.jar",
|
||||
"/system/framework/telephony-common.jar",
|
||||
"/system/framework/mms-common.jar",
|
||||
"/system/framework/android.policy.jar",
|
||||
"/system/framework/services.jar",
|
||||
"/system/framework/apache-xml.jar");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
application.version=${version}
|
@ -1 +0,0 @@
|
||||
application.version=1.4.2
|
@ -1,436 +0,0 @@
|
||||
group baksmali;
|
||||
|
||||
smaliFile(AccessFlags, ClassType, SuperType, SourceFile, Interfaces, Annotations, StaticFields,
|
||||
InstanceFields, DirectMethods, VirtualMethods) ::=
|
||||
<<
|
||||
.class <AccessFlags: {<it> }><ClassType>
|
||||
<if(SuperType)>
|
||||
.super <SuperType>
|
||||
<endif>
|
||||
<if(SourceFile)>
|
||||
|
||||
.source "<SourceFile>"
|
||||
|
||||
|
||||
<endif>
|
||||
<if(Interfaces)>
|
||||
|
||||
# interfaces
|
||||
<Interfaces: implement(it); separator="\n">
|
||||
|
||||
<endif>
|
||||
<if(Annotations)>
|
||||
|
||||
|
||||
# annotations
|
||||
<Annotations; separator="\n\n">
|
||||
|
||||
<endif>
|
||||
<if(StaticFields)>
|
||||
|
||||
|
||||
# static fields
|
||||
<StaticFields; separator="\n">
|
||||
|
||||
<endif>
|
||||
<if(InstanceFields)>
|
||||
|
||||
|
||||
# instance fields
|
||||
<InstanceFields; separator="\n">
|
||||
|
||||
<endif>
|
||||
<if(DirectMethods)>
|
||||
|
||||
|
||||
# direct methods
|
||||
<DirectMethods; separator="\n\n">
|
||||
|
||||
<endif>
|
||||
<if(VirtualMethods)>
|
||||
|
||||
|
||||
# virtual methods
|
||||
<VirtualMethods; separator="\n\n">
|
||||
|
||||
<endif>
|
||||
>>
|
||||
|
||||
|
||||
|
||||
|
||||
implement(interface) ::=
|
||||
<<
|
||||
.implements <interface>
|
||||
>>
|
||||
|
||||
|
||||
annotation(Visibility, AnnotationType, Elements) ::=
|
||||
<<
|
||||
.annotation <Visibility> <AnnotationType>
|
||||
<if(Elements)>
|
||||
<Elements; separator="\n">
|
||||
<endif>
|
||||
<if(Elements)>
|
||||
|
||||
|
||||
<endif>
|
||||
.end annotation
|
||||
>>
|
||||
|
||||
|
||||
|
||||
field(AccessFlags, FieldName, FieldType, Annotations, InitialValue, Comments) ::=
|
||||
<<
|
||||
<if(Comments)><Comments: {#<it>} ; separator="\n">
|
||||
|
||||
<endif>
|
||||
.field <AccessFlags: {<it> }><FieldName>:<FieldType><if(InitialValue)> = <InitialValue><endif>
|
||||
<if(Annotations)>
|
||||
<Annotations; separator="\n\n">
|
||||
.end field
|
||||
|
||||
<endif>
|
||||
>>
|
||||
|
||||
|
||||
method(AccessFlags, MethodName, Prototype, HasCode, RegistersDirective, RegisterCount, Parameters, Annotations,
|
||||
MethodItems) ::=
|
||||
<<
|
||||
.method <AccessFlags: {<it> }><MethodName><Prototype>
|
||||
<if(HasCode)>
|
||||
<RegistersDirective> <RegisterCount>
|
||||
<if(Parameters)>
|
||||
<Parameters; separator="\n">
|
||||
<endif>
|
||||
<if(Annotations)>
|
||||
<Annotations; separator="\n\n">
|
||||
<endif>
|
||||
|
||||
<MethodItems; separator="\n">
|
||||
<elseif(Annotations)>
|
||||
<Annotations; separator="\n\n">
|
||||
<endif>
|
||||
.end method
|
||||
>>
|
||||
|
||||
Parameter(ParameterName, Annotations) ::=
|
||||
<<
|
||||
.parameter<if(ParameterName)> "<ParameterName>"<endif><if(Annotations)>
|
||||
|
||||
<Annotations; separator="\n\n">
|
||||
.end parameter
|
||||
<endif>
|
||||
>>
|
||||
|
||||
Format10t(Opcode, TargetLabel) ::=
|
||||
<<
|
||||
<Opcode> <TargetLabel>
|
||||
>>
|
||||
|
||||
Format10x(Opcode) ::=
|
||||
<<
|
||||
<Opcode>
|
||||
>>
|
||||
|
||||
Format11n(Opcode, RegisterA, Literal) ::=
|
||||
<<
|
||||
<Opcode> <RegisterA>, <Literal>
|
||||
>>
|
||||
|
||||
Format11x(Opcode, RegisterA) ::=
|
||||
<<
|
||||
<Opcode> <RegisterA>
|
||||
>>
|
||||
|
||||
Format12x(Opcode, RegisterA, RegisterB) ::=
|
||||
<<
|
||||
<Opcode> <RegisterA>, <RegisterB>
|
||||
>>
|
||||
|
||||
Format20t(Opcode, TargetLabel) ::=
|
||||
<<
|
||||
<Opcode> <TargetLabel>
|
||||
>>
|
||||
|
||||
Format21c(Opcode, RegisterA, Reference) ::=
|
||||
<<
|
||||
<Opcode> <RegisterA>, <Reference>
|
||||
>>
|
||||
|
||||
Format21h(Opcode, RegisterA, Literal) ::=
|
||||
<<
|
||||
<Opcode> <RegisterA>, <Literal>
|
||||
>>
|
||||
|
||||
Format21s(Opcode, RegisterA, Literal) ::=
|
||||
<<
|
||||
<Opcode> <RegisterA>, <Literal>
|
||||
>>
|
||||
|
||||
Format21t(Opcode, RegisterA, TargetLabel) ::=
|
||||
<<
|
||||
<Opcode> <RegisterA>, <TargetLabel>
|
||||
>>
|
||||
|
||||
Format22b(Opcode, RegisterA, RegisterB, Literal) ::=
|
||||
<<
|
||||
<Opcode> <RegisterA>, <RegisterB>, <Literal>
|
||||
>>
|
||||
|
||||
Format22c(Opcode, RegisterA, RegisterB, Reference) ::=
|
||||
<<
|
||||
<Opcode> <RegisterA>, <RegisterB>, <Reference>
|
||||
>>
|
||||
|
||||
Format22cs(Opcode, RegisterA, RegisterB, FieldOffset) ::=
|
||||
<<
|
||||
<Opcode> <RegisterA>, <RegisterB>, field@<FieldOffset>
|
||||
>>
|
||||
|
||||
Format22s(Opcode, RegisterA, RegisterB, Literal) ::=
|
||||
<<
|
||||
<Opcode> <RegisterA>, <RegisterB>, <Literal>
|
||||
>>
|
||||
|
||||
Format22t(Opcode, RegisterA, RegisterB, TargetLabel) ::=
|
||||
<<
|
||||
<Opcode> <RegisterA>, <RegisterB>, <TargetLabel>
|
||||
>>
|
||||
|
||||
Format22x(Opcode, RegisterA, RegisterB) ::=
|
||||
<<
|
||||
<Opcode> <RegisterA>, <RegisterB>
|
||||
>>
|
||||
|
||||
Format23x(Opcode, RegisterA, RegisterB, RegisterC) ::=
|
||||
<<
|
||||
<Opcode> <RegisterA>, <RegisterB>, <RegisterC>
|
||||
>>
|
||||
|
||||
Format30t(Opcode, TargetLabel) ::=
|
||||
<<
|
||||
<Opcode> <TargetLabel>
|
||||
>>
|
||||
|
||||
Format31c(Opcode, RegisterA, Reference) ::=
|
||||
<<
|
||||
<Opcode> <RegisterA>, <Reference>
|
||||
>>
|
||||
|
||||
Format31i(Opcode, RegisterA, Literal) ::=
|
||||
<<
|
||||
<Opcode> <RegisterA>, <Literal>
|
||||
>>
|
||||
|
||||
Format31t(Opcode, RegisterA, TargetLabel) ::=
|
||||
<<
|
||||
<Opcode> <RegisterA>, <TargetLabel>
|
||||
>>
|
||||
|
||||
Format32x(Opcode, RegisterA, RegisterB) ::=
|
||||
<<
|
||||
<Opcode> <RegisterA>, <RegisterB>
|
||||
>>
|
||||
|
||||
Format35c(Opcode, Registers, Reference) ::=
|
||||
<<
|
||||
<Opcode> {<Registers; separator=", ">}, <Reference>
|
||||
>>
|
||||
|
||||
Format35s(Opcode, Registers, Reference) ::=
|
||||
<<
|
||||
<Opcode> {<Registers; separator=", ">}, <Reference>
|
||||
>>
|
||||
|
||||
Format35ms(Opcode, Registers, MethodIndex) ::=
|
||||
<<
|
||||
<Opcode> {<Registers; separator=", ">}, vtable@<MethodIndex>
|
||||
>>
|
||||
|
||||
Format3rc(Opcode, StartRegister, LastRegister, Reference) ::=
|
||||
<<
|
||||
<Opcode> {<StartRegister> .. <LastRegister>}, <Reference>
|
||||
>>
|
||||
|
||||
Format3rms(Opcode, StartRegister, LastRegister, MethodIndex) ::=
|
||||
<<
|
||||
<Opcode> {<StartRegister> .. <LastRegister>}, vtable@<MethodIndex>
|
||||
>>
|
||||
|
||||
Format51l(Opcode, RegisterA, Literal) ::=
|
||||
<<
|
||||
<Opcode> <RegisterA>, <Literal>
|
||||
>>
|
||||
|
||||
CommentedOutMethodItem(MethodItem) ::=
|
||||
<<
|
||||
#<MethodItem>
|
||||
>>
|
||||
|
||||
UnresolvedNullReference(Opcode, Register, UseInvokeRange, AddGoto) ::=
|
||||
<<
|
||||
<if(UseInvokeRange)>
|
||||
#Replaced unresolvable optimized invoke-*-range-quick instruction
|
||||
#with a generic method call that will throw a NullPointerException
|
||||
invoke-virtual/range {<Register> .. <Register>}, Ljava/lang/Object;->hashCode()I
|
||||
<if(AddGoto)>goto/32 0<endif>
|
||||
<else>
|
||||
#Replaced unresolvable optimized instruction with a throw
|
||||
throw <Register>
|
||||
<endif>
|
||||
>>
|
||||
|
||||
|
||||
ArrayData(Opcode, ElementWidth, Values, Dead) ::=
|
||||
<<
|
||||
<if(Dead)>#<endif>.array-data <ElementWidth>
|
||||
<if(Dead)>
|
||||
<Values: {# <it>}; separator="\n">
|
||||
<else>
|
||||
<Values: { <it>}; separator="\n">
|
||||
<endif>
|
||||
|
||||
<if(Dead)>#<endif>.end array-data
|
||||
>>
|
||||
|
||||
ArrayElement(Bytes) ::=
|
||||
<<
|
||||
<Bytes; format="unsigned",separator=" ">
|
||||
>>
|
||||
|
||||
PackedSwitchData(Opcode, FirstKey, Targets, Dead) ::=
|
||||
<<
|
||||
<if(Dead)>#<endif>.packed-switch <FirstKey>
|
||||
<if(Dead)>
|
||||
<Targets: {# <it>}; separator="\n">
|
||||
<else>
|
||||
<Targets: { <it>}; separator="\n">
|
||||
<endif>
|
||||
|
||||
<if(Dead)>#<endif>.end packed-switch
|
||||
>>
|
||||
|
||||
SparseSwitchData(Opcode, Targets, Dead) ::=
|
||||
<<
|
||||
<if(Dead)>#<endif>.sparse-switch
|
||||
<if(Dead)>
|
||||
<Targets: {# <it.Key> -> <it.Target>}; separator="\n">
|
||||
<else>
|
||||
<Targets: { <it.Key> -> <it.Target>}; separator="\n">
|
||||
<endif>
|
||||
|
||||
<if(Dead)>#<endif>.end sparse-switch
|
||||
>>
|
||||
|
||||
|
||||
Label(Prefix, Suffix) ::=
|
||||
<<
|
||||
:<Prefix><Suffix>
|
||||
>>
|
||||
|
||||
Line(Line) ::=
|
||||
<<
|
||||
.line <Line; format="decimal">
|
||||
>>
|
||||
|
||||
EndPrologue(Prologue) ::=
|
||||
<<
|
||||
.prologue
|
||||
>>
|
||||
|
||||
StartEpilogue(Epilogue) ::=
|
||||
<<
|
||||
.epilogue
|
||||
>>
|
||||
|
||||
StartLocal(Register, Name, Type, Signature) ::=
|
||||
<<
|
||||
.local <Register>, <Name>:<Type><if(Signature)>,"<Signature>"<endif>
|
||||
>>
|
||||
|
||||
EndLocal(Register, Name, Type, Signature) ::=
|
||||
<<
|
||||
.end local <Register> <if(Name)>#<Name>:<Type>,<if(Signature)>, "<Signature>"<endif><endif>
|
||||
>>
|
||||
|
||||
RestartLocal(Register, Name, Type, Signature) ::=
|
||||
<<
|
||||
.restart local <Register> <if(Name)>#<Name>:<Type>,<if(Signature)>, "<Signature>"<endif><endif>
|
||||
>>
|
||||
|
||||
SetFile(FileName) ::=
|
||||
<<
|
||||
.source "<FileName>"
|
||||
>>
|
||||
|
||||
Blank(Blank) ::=
|
||||
<<
|
||||
|
||||
>>
|
||||
|
||||
Catch(ExceptionType, StartLabel, EndLabel, HandlerLabel) ::=
|
||||
<<
|
||||
<if(ExceptionType)>.catch <ExceptionType><else>.catchall<endif> {<StartLabel> .. <EndLabel>} <HandlerLabel>
|
||||
>>
|
||||
|
||||
|
||||
StringReference(EscapedValue) ::=
|
||||
<<
|
||||
"<EscapedValue>"
|
||||
>>
|
||||
|
||||
FieldReference(ContainingClass, FieldName, FieldType) ::=
|
||||
<<
|
||||
<ContainingClass>-><FieldName>:<FieldType>
|
||||
>>
|
||||
|
||||
MethodReference(ContainingClass, MethodName, Prototype) ::=
|
||||
<<
|
||||
<ContainingClass>-><MethodName><Prototype>
|
||||
>>
|
||||
|
||||
TypeReference(TypeDescriptor) ::=
|
||||
<<
|
||||
<TypeDescriptor>
|
||||
>>
|
||||
|
||||
|
||||
SimpleEncodedValue(Value) ::=
|
||||
<<
|
||||
<Value>
|
||||
>>
|
||||
|
||||
EncodedIndexedItemReference(Value) ::=
|
||||
<<
|
||||
<Value>
|
||||
>>
|
||||
|
||||
ArrayEncodedValue(Value) ::=
|
||||
<<
|
||||
{
|
||||
<Value; separator=",\n">
|
||||
}
|
||||
>>
|
||||
|
||||
EnumEncodedValue(Value) ::=
|
||||
<<
|
||||
.enum <Value>
|
||||
>>
|
||||
|
||||
AnnotationEncodedValue(AnnotationType, Elements) ::=
|
||||
<<
|
||||
.subannotation <AnnotationType>
|
||||
<Elements; separator="\n">
|
||||
.end subannotation
|
||||
>>
|
||||
|
||||
AnnotationElement(Name, Value) ::=
|
||||
<<
|
||||
<Name> = <Value>
|
||||
>>
|
||||
|
||||
Comment(Comment) ::=
|
||||
<<
|
||||
#<Comment>
|
||||
>>
|
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.baksmali;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.io.Resources;
|
||||
import junit.framework.Assert;
|
||||
import org.jf.baksmali.Adaptors.ClassDefinition;
|
||||
import org.jf.dexlib2.DexFileFactory;
|
||||
import org.jf.dexlib2.analysis.ClassPath;
|
||||
import org.jf.dexlib2.iface.ClassDef;
|
||||
import org.jf.dexlib2.iface.DexFile;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
|
||||
public class AnalysisTest {
|
||||
|
||||
@Test
|
||||
public void ConstructorTest() throws IOException, URISyntaxException {
|
||||
runTest("ConstructorTest", true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void RegisterEqualityOnMergeTest() throws IOException, URISyntaxException {
|
||||
runTest("RegisterEqualityOnMergeTest", true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void UninitRefIdentityTest() throws IOException, URISyntaxException {
|
||||
runTest("UninitRefIdentityTest", true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void MultipleStartInstructionsTest() throws IOException, URISyntaxException {
|
||||
runTest("MultipleStartInstructionsTest", true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void DuplicateTest() throws IOException, URISyntaxException {
|
||||
runTest("DuplicateTest", false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void LocalTest() throws IOException, URISyntaxException {
|
||||
runTest("LocalTest", false);
|
||||
}
|
||||
|
||||
public void runTest(String test, boolean registerInfo) throws IOException, URISyntaxException {
|
||||
String dexFilePath = String.format("%s%sclasses.dex", test, File.separatorChar);
|
||||
|
||||
DexFile dexFile = DexFileFactory.loadDexFile(findResource(dexFilePath), 15);
|
||||
|
||||
baksmaliOptions options = new baksmaliOptions();
|
||||
if (registerInfo) {
|
||||
options.registerInfo = baksmaliOptions.ALL | baksmaliOptions.FULLMERGE;
|
||||
options.classPath = new ClassPath();
|
||||
}
|
||||
|
||||
for (ClassDef classDef: dexFile.getClasses()) {
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
IndentingWriter writer = new IndentingWriter(stringWriter);
|
||||
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
|
||||
classDefinition.writeTo(writer);
|
||||
writer.close();
|
||||
|
||||
String className = classDef.getType();
|
||||
String smaliPath = String.format("%s%s%s.smali", test, File.separatorChar,
|
||||
className.substring(1, className.length() - 1));
|
||||
String smaliContents = readResource(smaliPath);
|
||||
|
||||
Assert.assertEquals(smaliContents, stringWriter.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private File findResource(String resource) throws URISyntaxException {
|
||||
URL resUrl = Resources.getResource(resource);
|
||||
return new File(resUrl.toURI());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private String readResource(String resource) throws URISyntaxException, IOException {
|
||||
URL url = Resources.getResource(resource);
|
||||
return Resources.toString(url, Charsets.UTF_8);
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
.class public LConstructorTest;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
|
||||
# direct methods
|
||||
.method public constructor <init>()V
|
||||
.registers 4
|
||||
|
||||
#v0=(Uninit);v1=(Uninit);v2=(Uninit);p0=(UninitThis,LConstructorTest;);
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
#v0=(Uninit);v1=(Uninit);v2=(Uninit);p0=(Reference,LConstructorTest;);
|
||||
|
||||
#v0=(Uninit);v1=(Uninit);v2=(Uninit);p0=(Reference,LConstructorTest;);
|
||||
return-void
|
||||
#v0=(Uninit);v1=(Uninit);v2=(Uninit);p0=(Reference,LConstructorTest;);
|
||||
.end method
|
@ -0,0 +1,25 @@
|
||||
.class public LConstructorTest2;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
|
||||
# direct methods
|
||||
.method public constructor <init>()V
|
||||
.registers 4
|
||||
|
||||
#v0=(Uninit);v1=(Uninit);v2=(Uninit);p0=(UninitThis,LConstructorTest2;);
|
||||
if-eqz p0, :cond_3
|
||||
#v0=(Uninit);v1=(Uninit);v2=(Uninit);p0=(UninitThis,LConstructorTest2;);
|
||||
|
||||
#v0=(Uninit);v1=(Uninit);v2=(Uninit);p0=(UninitThis,LConstructorTest2;);
|
||||
nop
|
||||
#v0=(Uninit);v1=(Uninit);v2=(Uninit);p0=(UninitThis,LConstructorTest2;);
|
||||
|
||||
:cond_3
|
||||
#v0=(Uninit);v1=(Uninit);v2=(Uninit);p0=(UninitThis,LConstructorTest2;);
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
#v0=(Uninit);v1=(Uninit);v2=(Uninit);p0=(Reference,LConstructorTest2;);
|
||||
|
||||
#v0=(Uninit);v1=(Uninit);v2=(Uninit);p0=(Reference,LConstructorTest2;);
|
||||
return-void
|
||||
#v0=(Uninit);v1=(Uninit);v2=(Uninit);p0=(Reference,LConstructorTest2;);
|
||||
.end method
|
Binary file not shown.
@ -0,0 +1,30 @@
|
||||
.class public LDuplicateDirectMethods;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
|
||||
# direct methods
|
||||
.method private alah()V
|
||||
.registers 1
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private blah()V
|
||||
.registers 1
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
# duplicate method ignored
|
||||
# .method private blah()V
|
||||
# .registers 1
|
||||
|
||||
# return-void
|
||||
# .end method
|
||||
|
||||
|
||||
.method private clah()V
|
||||
.registers 1
|
||||
|
||||
return-void
|
||||
.end method
|
@ -0,0 +1,48 @@
|
||||
.class public LDuplicateDirectVirtualMethods;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
|
||||
# direct methods
|
||||
.method private blah()V
|
||||
.registers 1
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
# duplicate method ignored
|
||||
# .method private blah()V
|
||||
# .registers 1
|
||||
|
||||
# return-void
|
||||
# .end method
|
||||
|
||||
|
||||
|
||||
# virtual methods
|
||||
.method public alah()V
|
||||
.registers 1
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
# There is both a direct and virtual method with this signature.
|
||||
# You will need to rename one of these methods, including all references.
|
||||
.method public blah()V
|
||||
.registers 1
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
# duplicate method ignored
|
||||
# .method public blah()V
|
||||
# .registers 1
|
||||
|
||||
# return-void
|
||||
# .end method
|
||||
|
||||
|
||||
.method public clah()V
|
||||
.registers 1
|
||||
|
||||
return-void
|
||||
.end method
|
@ -0,0 +1,13 @@
|
||||
.class public LDuplicateInstanceFields;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
|
||||
# instance fields
|
||||
.field public alah:I
|
||||
|
||||
.field public blah:I
|
||||
|
||||
# duplicate field ignored
|
||||
# .field public blah:I
|
||||
|
||||
.field public clah:I
|
@ -0,0 +1,13 @@
|
||||
.class public LDuplicateStaticFields;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
|
||||
# static fields
|
||||
.field public static alah:I
|
||||
|
||||
.field public static blah:I
|
||||
|
||||
# duplicate field ignored
|
||||
# .field public static blah:I
|
||||
|
||||
.field public static clah:I
|
@ -0,0 +1,22 @@
|
||||
.class public LDuplicateStaticInstanceFields;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
|
||||
# static fields
|
||||
.field public static blah:I
|
||||
|
||||
# duplicate field ignored
|
||||
# .field public static blah:I
|
||||
|
||||
|
||||
# instance fields
|
||||
.field public alah:I
|
||||
|
||||
# There is both a static and instance field with this signature.
|
||||
# You will need to rename one of these fields, including all references.
|
||||
.field public blah:I
|
||||
|
||||
# duplicate field ignored
|
||||
# .field public blah:I
|
||||
|
||||
.field public clah:I
|
@ -0,0 +1,30 @@
|
||||
.class public LDuplicateVirtualMethods;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
|
||||
# virtual methods
|
||||
.method public alah()V
|
||||
.registers 1
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public blah()V
|
||||
.registers 1
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
# duplicate method ignored
|
||||
# .method public blah()V
|
||||
# .registers 1
|
||||
|
||||
# return-void
|
||||
# .end method
|
||||
|
||||
|
||||
.method public clah()V
|
||||
.registers 1
|
||||
|
||||
return-void
|
||||
.end method
|
Binary file not shown.
@ -0,0 +1,22 @@
|
||||
.class public LDuplicateDirectMethods;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
.method private alah()V
|
||||
.registers 1
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private blah()V
|
||||
.registers 1
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private blah()V
|
||||
.registers 1
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private clah()V
|
||||
.registers 1
|
||||
return-void
|
||||
.end method
|
@ -0,0 +1,32 @@
|
||||
.class public LDuplicateDirectVirtualMethods;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
.method public alah()V
|
||||
.registers 1
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private blah()V
|
||||
.registers 1
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private blah()V
|
||||
.registers 1
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public blah()V
|
||||
.registers 1
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public blah()V
|
||||
.registers 1
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public clah()V
|
||||
.registers 1
|
||||
return-void
|
||||
.end method
|
@ -0,0 +1,9 @@
|
||||
.class public LDuplicateInstanceFields;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
.field public alah:I
|
||||
|
||||
.field public blah:I
|
||||
.field public blah:I
|
||||
|
||||
.field public clah:I
|
@ -0,0 +1,9 @@
|
||||
.class public LDuplicateStaticFields;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
.field public static alah:I
|
||||
|
||||
.field public static blah:I
|
||||
.field public static blah:I
|
||||
|
||||
.field public static clah:I
|
@ -0,0 +1,11 @@
|
||||
.class public LDuplicateStaticInstanceFields;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
.field public alah:I
|
||||
|
||||
.field public blah:I
|
||||
.field public blah:I
|
||||
.field static public blah:I
|
||||
.field static public blah:I
|
||||
|
||||
.field public clah:I
|
@ -0,0 +1,22 @@
|
||||
.class public LDuplicateVirtualMethods;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
.method public alah()V
|
||||
.registers 1
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public blah()V
|
||||
.registers 1
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public blah()V
|
||||
.registers 1
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public clah()V
|
||||
.registers 1
|
||||
return-void
|
||||
.end method
|
@ -0,0 +1,3 @@
|
||||
The test dex file was produced from these smali files, using
|
||||
an old version of smali that doesn't check for field/method
|
||||
duplicates
|
@ -0,0 +1,31 @@
|
||||
.class public LLocalTest;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
|
||||
# direct methods
|
||||
.method public static method1()V
|
||||
.registers 10
|
||||
|
||||
.local v0, "blah! This local name has some spaces, a colon, even a \nnewline!":I, "some sig info:\nblah."
|
||||
.local v1, "blah! This local name has some spaces, a colon, even a \nnewline!":V, "some sig info:\nblah."
|
||||
.local v2, "blah! This local name has some spaces, a colon, even a \nnewline!":I
|
||||
.local v3, "blah! This local name has some spaces, a colon, even a \nnewline!":V
|
||||
.local v4, null:I, "some sig info:\nblah."
|
||||
.local v5, null:V, "some sig info:\nblah."
|
||||
.local v6, null:I
|
||||
.local v7
|
||||
.local v8
|
||||
.local v9
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public static method2(IJLjava/lang/String;)V
|
||||
.registers 10
|
||||
.param p0, "blah! This local name has some spaces, a colon, even a \nnewline!" # I
|
||||
.param p1 # J
|
||||
.annotation runtime LAnnotationWithValues;
|
||||
.end annotation
|
||||
.end param
|
||||
|
||||
return-void
|
||||
.end method
|
Binary file not shown.
@ -0,0 +1,46 @@
|
||||
.class public LMultipleStartInstructionsTest;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
|
||||
# direct methods
|
||||
.method public constructor <init>(Ljava/lang/String;)V
|
||||
.registers 4
|
||||
|
||||
:try_start_0
|
||||
#v0=(Uninit);v1=(Uninit);p0=(UninitThis,LMultipleStartInstructionsTest;);p1=(Reference,Ljava/lang/String;);
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
#v0=(Uninit);v1=(Uninit);p0=(Reference,LMultipleStartInstructionsTest;);p1=(Reference,Ljava/lang/String;);
|
||||
|
||||
#v0=(Uninit);v1=(Uninit);p0=(Reference,LMultipleStartInstructionsTest;);p1=(Reference,Ljava/lang/String;);
|
||||
const-string v0, "blah"
|
||||
#v0=(Reference,Ljava/lang/String;);v1=(Uninit);p0=(Reference,LMultipleStartInstructionsTest;);p1=(Reference,Ljava/lang/String;);
|
||||
|
||||
#v0=(Reference,Ljava/lang/String;);v1=(Uninit);p0=(Reference,LMultipleStartInstructionsTest;);p1=(Reference,Ljava/lang/String;);
|
||||
return-void
|
||||
#v0=(Reference,Ljava/lang/String;);v1=(Uninit);p0=(Reference,LMultipleStartInstructionsTest;);p1=(Reference,Ljava/lang/String;);
|
||||
:try_end_6
|
||||
.catchall {:try_start_0 .. :try_end_6} :catchall_6
|
||||
|
||||
:catchall_6
|
||||
:try_start_6
|
||||
#v0=(Uninit);v1=(Uninit);
|
||||
#p0=(Conflicted):merge{Start:(UninitThis,LMultipleStartInstructionsTest;),0x0:(Reference,LMultipleStartInstructionsTest;)}
|
||||
#p1=(Reference,Ljava/lang/String;);
|
||||
invoke-static {}, LMultipleStartInstructionsTest;->blah()V
|
||||
#v0=(Uninit);v1=(Uninit);p0=(Conflicted);p1=(Reference,Ljava/lang/String;);
|
||||
:try_end_9
|
||||
.catchall {:try_start_6 .. :try_end_9} :catchall_9
|
||||
|
||||
:catchall_9
|
||||
#v0=(Uninit);v1=(Uninit);
|
||||
#p0=(Conflicted):merge{Start:(UninitThis,LMultipleStartInstructionsTest;),0x0:(Reference,LMultipleStartInstructionsTest;),0x6:(Conflicted)}
|
||||
#p1=(Reference,Ljava/lang/String;);
|
||||
return-void
|
||||
#v0=(Uninit);v1=(Uninit);p0=(Conflicted);p1=(Reference,Ljava/lang/String;);
|
||||
.end method
|
||||
|
||||
.method public static blah()V
|
||||
.registers 0
|
||||
|
||||
return-void
|
||||
.end method
|
Binary file not shown.
@ -0,0 +1,37 @@
|
||||
.class public LRegisterEqualityOnMerge;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
|
||||
# direct methods
|
||||
.method public constructor <init>()V
|
||||
.registers 4
|
||||
|
||||
#v0=(Uninit);v1=(Uninit);v2=(Uninit);p0=(UninitThis,LRegisterEqualityOnMerge;);
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
#v0=(Uninit);v1=(Uninit);v2=(Uninit);p0=(Reference,LRegisterEqualityOnMerge;);
|
||||
|
||||
#v0=(Uninit);v1=(Uninit);v2=(Uninit);p0=(Reference,LRegisterEqualityOnMerge;);
|
||||
invoke-virtual {p0}, Ljava/lang/Object;->toString()Ljava/lang/String;
|
||||
#v0=(Uninit);v1=(Uninit);v2=(Uninit);p0=(Reference,LRegisterEqualityOnMerge;);
|
||||
|
||||
#v0=(Uninit);v1=(Uninit);v2=(Uninit);p0=(Reference,LRegisterEqualityOnMerge;);
|
||||
move-result-object v0
|
||||
#v0=(Reference,Ljava/lang/String;);v1=(Uninit);v2=(Uninit);p0=(Reference,LRegisterEqualityOnMerge;);
|
||||
|
||||
#v0=(Reference,Ljava/lang/String;);v1=(Uninit);v2=(Uninit);p0=(Reference,LRegisterEqualityOnMerge;);
|
||||
if-eqz v0, :cond_d
|
||||
#v0=(Reference,Ljava/lang/String;);v1=(Uninit);v2=(Uninit);p0=(Reference,LRegisterEqualityOnMerge;);
|
||||
|
||||
#v0=(Reference,Ljava/lang/String;);v1=(Uninit);v2=(Uninit);p0=(Reference,LRegisterEqualityOnMerge;);
|
||||
invoke-virtual {p0}, Ljava/lang/Object;->toString()Ljava/lang/String;
|
||||
#v0=(Reference,Ljava/lang/String;);v1=(Uninit);v2=(Uninit);p0=(Reference,LRegisterEqualityOnMerge;);
|
||||
|
||||
#v0=(Reference,Ljava/lang/String;);v1=(Uninit);v2=(Uninit);p0=(Reference,LRegisterEqualityOnMerge;);
|
||||
move-result-object v0
|
||||
#v0=(Reference,Ljava/lang/String;);v1=(Uninit);v2=(Uninit);p0=(Reference,LRegisterEqualityOnMerge;);
|
||||
|
||||
:cond_d
|
||||
#v0=(Reference,Ljava/lang/String;);v1=(Uninit);v2=(Uninit);p0=(Reference,LRegisterEqualityOnMerge;);
|
||||
return-void
|
||||
#v0=(Reference,Ljava/lang/String;);v1=(Uninit);v2=(Uninit);p0=(Reference,LRegisterEqualityOnMerge;);
|
||||
.end method
|
Binary file not shown.
@ -0,0 +1,50 @@
|
||||
.class public LUninitRefIdentityTest;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
|
||||
# direct methods
|
||||
.method public constructor <init>()V
|
||||
.registers 4
|
||||
|
||||
#v0=(Uninit);v1=(Uninit);v2=(Uninit);p0=(UninitThis,LUninitRefIdentityTest;);
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
#v0=(Uninit);v1=(Uninit);v2=(Uninit);p0=(Reference,LUninitRefIdentityTest;);
|
||||
|
||||
#v0=(Uninit);v1=(Uninit);v2=(Uninit);p0=(Reference,LUninitRefIdentityTest;);
|
||||
new-instance v0, Ljava/lang/String;
|
||||
#v0=(UninitRef,Ljava/lang/String;);v1=(Uninit);v2=(Uninit);p0=(Reference,LUninitRefIdentityTest;);
|
||||
|
||||
#v0=(UninitRef,Ljava/lang/String;);v1=(Uninit);v2=(Uninit);p0=(Reference,LUninitRefIdentityTest;);
|
||||
if-eqz v0, :cond_9
|
||||
#v0=(UninitRef,Ljava/lang/String;);v1=(Uninit);v2=(Uninit);p0=(Reference,LUninitRefIdentityTest;);
|
||||
|
||||
#v0=(UninitRef,Ljava/lang/String;);v1=(Uninit);v2=(Uninit);p0=(Reference,LUninitRefIdentityTest;);
|
||||
new-instance v0, Ljava/lang/String;
|
||||
#v0=(UninitRef,Ljava/lang/String;);v1=(Uninit);v2=(Uninit);p0=(Reference,LUninitRefIdentityTest;);
|
||||
|
||||
:cond_9
|
||||
#v0=(Conflicted):merge{0x5:(UninitRef,Ljava/lang/String;),0x7:(UninitRef,Ljava/lang/String;)}
|
||||
#v1=(Uninit);v2=(Uninit);p0=(Reference,LUninitRefIdentityTest;);
|
||||
invoke-direct {v0}, Ljava/lang/String;-><init>()V
|
||||
#v0=(Unknown);v1=(Uninit);v2=(Uninit);p0=(Reference,LUninitRefIdentityTest;);
|
||||
|
||||
#v0=(Unknown);v1=(Uninit);v2=(Uninit);p0=(Reference,LUninitRefIdentityTest;);
|
||||
return-void
|
||||
#v0=(Unknown);v1=(Uninit);v2=(Uninit);p0=(Reference,LUninitRefIdentityTest;);
|
||||
.end method
|
||||
|
||||
.method public constructor <init>(Ljava/lang/String;)V
|
||||
.registers 2
|
||||
|
||||
#p0=(UninitThis,LUninitRefIdentityTest;);p1=(Reference,Ljava/lang/String;);
|
||||
move-object p1, p0
|
||||
#p0=(UninitThis,LUninitRefIdentityTest;);p1=(UninitThis,LUninitRefIdentityTest;);
|
||||
|
||||
#p0=(UninitThis,LUninitRefIdentityTest;);p1=(UninitThis,LUninitRefIdentityTest;);
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
#p0=(Reference,LUninitRefIdentityTest;);p1=(Reference,LUninitRefIdentityTest;);
|
||||
|
||||
#p0=(Reference,LUninitRefIdentityTest;);p1=(Reference,LUninitRefIdentityTest;);
|
||||
return-void
|
||||
#p0=(Reference,LUninitRefIdentityTest;);p1=(Reference,LUninitRefIdentityTest;);
|
||||
.end method
|
Binary file not shown.
1
brut.apktool.smali/dexlib/.gitignore
vendored
1
brut.apktool.smali/dexlib/.gitignore
vendored
@ -1 +0,0 @@
|
||||
/target
|
@ -30,7 +30,7 @@
|
||||
*/
|
||||
|
||||
dependencies {
|
||||
compile project(':brut.j.dir')
|
||||
compile 'com.google.code.findbugs:jsr305:1.3.9'
|
||||
compile 'com.google.collections:google-collections:1.0'
|
||||
compile project(':util')
|
||||
compile depends.findbugs
|
||||
compile depends.guava
|
||||
}
|
@ -30,7 +30,7 @@ package org.jf.dexlib;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.jf.dexlib.Util.AnnotatedOutput;
|
||||
import org.jf.dexlib.Util.ExceptionWithContext;
|
||||
import org.jf.util.ExceptionWithContext;
|
||||
import org.jf.dexlib.Util.Input;
|
||||
import org.jf.dexlib.Util.ReadOnlyArrayList;
|
||||
|
||||
|
@ -30,6 +30,7 @@ package org.jf.dexlib;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.jf.dexlib.Util.*;
|
||||
import org.jf.util.ExceptionWithContext;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
@ -32,7 +32,7 @@ import org.jf.dexlib.Code.*;
|
||||
import org.jf.dexlib.Item;
|
||||
import org.jf.dexlib.ItemType;
|
||||
import org.jf.dexlib.MethodIdItem;
|
||||
import org.jf.dexlib.Util.ExceptionWithContext;
|
||||
import org.jf.util.ExceptionWithContext;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
@ -30,7 +30,7 @@ package org.jf.dexlib.Code.Analysis;
|
||||
|
||||
import org.jf.dexlib.*;
|
||||
import org.jf.dexlib.Util.AccessFlags;
|
||||
import org.jf.dexlib.Util.ExceptionWithContext;
|
||||
import org.jf.util.ExceptionWithContext;
|
||||
import org.jf.dexlib.Util.SparseArray;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
@ -44,8 +44,6 @@ import static org.jf.dexlib.ClassDataItem.EncodedField;
|
||||
import static org.jf.dexlib.ClassDataItem.EncodedMethod;
|
||||
|
||||
public class ClassPath {
|
||||
public static boolean dontLoadClassPath = false;
|
||||
|
||||
private static ClassPath theClassPath = null;
|
||||
|
||||
/**
|
||||
@ -263,10 +261,6 @@ public class ClassPath {
|
||||
|
||||
@Nonnull
|
||||
public static ClassDef getClassDef(String classType, boolean createUnresolvedClassDef) {
|
||||
if (dontLoadClassPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ClassDef classDef = theClassPath.classDefs.get(classType);
|
||||
if (classDef == null) {
|
||||
//if it's an array class, try to create it
|
||||
@ -543,7 +537,7 @@ public class ClassPath {
|
||||
}
|
||||
|
||||
public ClassDef getSuperclass() {
|
||||
return theClassPath.javaLangObjectClassDef;
|
||||
throw unresolvedValidationException();
|
||||
}
|
||||
|
||||
public int getClassDepth() {
|
||||
@ -599,10 +593,6 @@ public class ClassPath {
|
||||
|
||||
private final int classDepth;
|
||||
|
||||
// classes can only be public or package-private. Internally, any private/protected inner class is actually
|
||||
// package-private.
|
||||
private final boolean isPublic;
|
||||
|
||||
private final VirtualMethod[] vtable;
|
||||
|
||||
//this maps a method name of the form method(III)Ljava/lang/String; to an integer
|
||||
@ -645,7 +635,6 @@ public class ClassPath {
|
||||
implementedInterfaces.add(ClassPath.getClassDef("Ljava/lang/Cloneable;"));
|
||||
implementedInterfaces.add(ClassPath.getClassDef("Ljava/io/Serializable;"));
|
||||
isInterface = false;
|
||||
isPublic = true;
|
||||
|
||||
vtable = superclass.vtable;
|
||||
methodLookup = superclass.methodLookup;
|
||||
@ -663,7 +652,6 @@ public class ClassPath {
|
||||
this.superclass = null;
|
||||
implementedInterfaces = null;
|
||||
isInterface = false;
|
||||
isPublic = true;
|
||||
vtable = null;
|
||||
methodLookup = null;
|
||||
instanceFields = null;
|
||||
@ -677,7 +665,6 @@ public class ClassPath {
|
||||
this.superclass = ClassPath.getClassDef("Ljava/lang/Object;");
|
||||
implementedInterfaces = new TreeSet<ClassDef>();
|
||||
isInterface = false;
|
||||
isPublic = true;
|
||||
|
||||
vtable = superclass.vtable;
|
||||
methodLookup = superclass.methodLookup;
|
||||
@ -692,7 +679,6 @@ public class ClassPath {
|
||||
|
||||
protected ClassDef(UnresolvedClassInfo classInfo) {
|
||||
classType = classInfo.classType;
|
||||
isPublic = classInfo.isPublic;
|
||||
isInterface = classInfo.isInterface;
|
||||
|
||||
superclass = loadSuperclass(classInfo);
|
||||
@ -738,6 +724,14 @@ public class ClassPath {
|
||||
return superclass;
|
||||
}
|
||||
|
||||
VirtualMethod[] getVtable() {
|
||||
return vtable;
|
||||
}
|
||||
|
||||
SparseArray<FieldDef> getInstanceFields() {
|
||||
return instanceFields;
|
||||
}
|
||||
|
||||
public int getClassDepth() {
|
||||
return classDepth;
|
||||
}
|
||||
@ -746,10 +740,6 @@ public class ClassPath {
|
||||
return this.isInterface;
|
||||
}
|
||||
|
||||
public boolean isPublic() {
|
||||
return this.isPublic;
|
||||
}
|
||||
|
||||
public boolean extendsClass(ClassDef superclassDef) {
|
||||
if (superclassDef == null) {
|
||||
return false;
|
||||
@ -1225,7 +1215,7 @@ public class ClassPath {
|
||||
}
|
||||
}
|
||||
|
||||
private static class VirtualMethod {
|
||||
static class VirtualMethod {
|
||||
public String containingClass;
|
||||
public String method;
|
||||
public boolean isPackagePrivate;
|
||||
@ -1238,7 +1228,6 @@ public class ClassPath {
|
||||
private static class UnresolvedClassInfo {
|
||||
public final String dexFilePath;
|
||||
public final String classType;
|
||||
public final boolean isPublic;
|
||||
public final boolean isInterface;
|
||||
public final String superclassType;
|
||||
public final String[] interfaces;
|
||||
@ -1252,7 +1241,6 @@ public class ClassPath {
|
||||
|
||||
classType = classDefItem.getClassType().getTypeDescriptor();
|
||||
|
||||
isPublic = (classDefItem.getAccessFlags() & AccessFlags.PUBLIC.getValue()) != 0;
|
||||
isInterface = (classDefItem.getAccessFlags() & AccessFlags.INTERFACE.getValue()) != 0;
|
||||
|
||||
TypeIdItem superclassType = classDefItem.getSuperclass();
|
||||
|
@ -64,21 +64,19 @@ public class DeodexUtil {
|
||||
return inlineMethodResolver.resolveExecuteInline(instruction);
|
||||
}
|
||||
|
||||
public FieldIdItem lookupField(ClassPath.ClassDef accessingClass, ClassPath.ClassDef instanceClass,
|
||||
int fieldOffset) {
|
||||
ClassPath.FieldDef field = instanceClass.getInstanceField(fieldOffset);
|
||||
public FieldIdItem lookupField(ClassPath.ClassDef classDef, int fieldOffset) {
|
||||
ClassPath.FieldDef field = classDef.getInstanceField(fieldOffset);
|
||||
if (field == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return parseAndResolveField(accessingClass, instanceClass, field);
|
||||
return parseAndResolveField(classDef, field);
|
||||
}
|
||||
|
||||
private static final Pattern shortMethodPattern = Pattern.compile("([^(]+)\\(([^)]*)\\)(.+)");
|
||||
|
||||
public MethodIdItem lookupVirtualMethod(ClassPath.ClassDef accessingClass, ClassPath.ClassDef instanceClass,
|
||||
int methodIndex) {
|
||||
String method = instanceClass.getVirtualMethod(methodIndex);
|
||||
public MethodIdItem lookupVirtualMethod(ClassPath.ClassDef classDef, int methodIndex) {
|
||||
String method = classDef.getVirtualMethod(methodIndex);
|
||||
if (method == null) {
|
||||
return null;
|
||||
}
|
||||
@ -93,20 +91,20 @@ public class DeodexUtil {
|
||||
String methodParams = m.group(2);
|
||||
String methodRet = m.group(3);
|
||||
|
||||
if (instanceClass instanceof ClassPath.UnresolvedClassDef) {
|
||||
if (classDef instanceof ClassPath.UnresolvedClassDef) {
|
||||
//if this is an unresolved class, the only way getVirtualMethod could have found a method is if the virtual
|
||||
//method being looked up was a method on java.lang.Object.
|
||||
instanceClass = ClassPath.getClassDef("Ljava/lang/Object;");
|
||||
} else if (instanceClass.isInterface()) {
|
||||
instanceClass = instanceClass.getSuperclass();
|
||||
assert instanceClass != null;
|
||||
classDef = ClassPath.getClassDef("Ljava/lang/Object;");
|
||||
} else if (classDef.isInterface()) {
|
||||
classDef = classDef.getSuperclass();
|
||||
assert classDef != null;
|
||||
}
|
||||
|
||||
return parseAndResolveMethod(accessingClass, instanceClass, methodName, methodParams, methodRet);
|
||||
return parseAndResolveMethod(classDef, methodName, methodParams, methodRet);
|
||||
}
|
||||
|
||||
private MethodIdItem parseAndResolveMethod(ClassPath.ClassDef accessingClass, ClassPath.ClassDef definingClass,
|
||||
String methodName, String methodParams, String methodRet) {
|
||||
private MethodIdItem parseAndResolveMethod(ClassPath.ClassDef classDef, String methodName, String methodParams,
|
||||
String methodRet) {
|
||||
StringIdItem methodNameItem = StringIdItem.lookupStringIdItem(dexFile, methodName);
|
||||
if (methodNameItem == null) {
|
||||
return null;
|
||||
@ -199,14 +197,14 @@ public class DeodexUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
ClassPath.ClassDef methodClassDef = definingClass;
|
||||
ClassPath.ClassDef methodClassDef = classDef;
|
||||
|
||||
do {
|
||||
TypeIdItem classTypeItem = TypeIdItem.lookupTypeIdItem(dexFile, methodClassDef.getClassType());
|
||||
|
||||
if (classTypeItem != null) {
|
||||
MethodIdItem methodIdItem = MethodIdItem.lookupMethodIdItem(dexFile, classTypeItem, protoItem, methodNameItem);
|
||||
if (methodIdItem != null && checkClassAccess(accessingClass, methodClassDef)) {
|
||||
if (methodIdItem != null) {
|
||||
return methodIdItem;
|
||||
}
|
||||
}
|
||||
@ -216,28 +214,7 @@ public class DeodexUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean checkClassAccess(ClassPath.ClassDef accessingClass, ClassPath.ClassDef definingClass) {
|
||||
return definingClass.isPublic() ||
|
||||
getPackage(accessingClass.getClassType()).equals(getPackage(definingClass.getClassType()));
|
||||
}
|
||||
|
||||
private static String getPackage(String classRef) {
|
||||
int lastSlash = classRef.lastIndexOf('/');
|
||||
if (lastSlash < 0) {
|
||||
return "";
|
||||
}
|
||||
return classRef.substring(1, lastSlash);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param accessingClass The class that contains the field reference. I.e. the class being deodexed
|
||||
* @param instanceClass The inferred class type of the object that the field is being accessed on
|
||||
* @param field The field being accessed
|
||||
* @return The FieldIdItem of the resolved field
|
||||
*/
|
||||
private FieldIdItem parseAndResolveField(ClassPath.ClassDef accessingClass, ClassPath.ClassDef instanceClass,
|
||||
ClassPath.FieldDef field) {
|
||||
private FieldIdItem parseAndResolveField(ClassPath.ClassDef classDef, ClassPath.FieldDef field) {
|
||||
String definingClass = field.definingClass;
|
||||
String fieldName = field.name;
|
||||
String fieldType = field.type;
|
||||
@ -252,7 +229,7 @@ public class DeodexUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
ClassPath.ClassDef fieldClass = instanceClass;
|
||||
ClassPath.ClassDef fieldClass = classDef;
|
||||
|
||||
ArrayList<ClassPath.ClassDef> parents = new ArrayList<ClassPath.ClassDef>();
|
||||
parents.add(fieldClass);
|
||||
@ -271,7 +248,7 @@ public class DeodexUtil {
|
||||
}
|
||||
|
||||
FieldIdItem fieldIdItem = FieldIdItem.lookupFieldIdItem(dexFile, classTypeItem, fieldTypeItem, fieldNameItem);
|
||||
if (fieldIdItem != null && checkClassAccess(accessingClass, fieldClass)) {
|
||||
if (fieldIdItem != null) {
|
||||
return fieldIdItem;
|
||||
}
|
||||
}
|
||||
@ -306,8 +283,7 @@ public class DeodexUtil {
|
||||
private void loadMethod(DeodexUtil deodexUtil) {
|
||||
ClassPath.ClassDef classDef = ClassPath.getClassDef(classType);
|
||||
|
||||
this.methodIdItem = deodexUtil.parseAndResolveMethod(classDef, classDef, methodName, parameters,
|
||||
returnType);
|
||||
this.methodIdItem = deodexUtil.parseAndResolveMethod(classDef, methodName, parameters, returnType);
|
||||
}
|
||||
|
||||
public String getMethodString() {
|
||||
|
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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.dexlib.Code.Analysis;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.commons.cli.*;
|
||||
import org.jf.dexlib.ClassDefItem;
|
||||
import org.jf.dexlib.DexFile;
|
||||
import org.jf.dexlib.Util.SparseArray;
|
||||
import org.jf.util.ConsoleUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class DumpFields {
|
||||
private static final Options options;
|
||||
|
||||
static {
|
||||
options = new Options();
|
||||
buildOptions();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
CommandLineParser parser = new PosixParser();
|
||||
CommandLine commandLine;
|
||||
|
||||
try {
|
||||
commandLine = parser.parse(options, args);
|
||||
} catch (ParseException ex) {
|
||||
usage();
|
||||
return;
|
||||
}
|
||||
|
||||
String[] remainingArgs = commandLine.getArgs();
|
||||
|
||||
Option[] parsedOptions = commandLine.getOptions();
|
||||
ArrayList<String> bootClassPathDirs = Lists.newArrayList();
|
||||
String outFile = "fields.txt";
|
||||
|
||||
for (int i=0; i<parsedOptions.length; i++) {
|
||||
Option option = parsedOptions[i];
|
||||
String opt = option.getOpt();
|
||||
|
||||
switch (opt.charAt(0)) {
|
||||
case 'd':
|
||||
bootClassPathDirs.add(option.getValue());
|
||||
break;
|
||||
case 'o':
|
||||
outFile = option.getValue();
|
||||
break;
|
||||
default:
|
||||
assert false;
|
||||
}
|
||||
}
|
||||
|
||||
if (remainingArgs.length != 1) {
|
||||
usage();
|
||||
return;
|
||||
}
|
||||
|
||||
String inputDexFileName = remainingArgs[0];
|
||||
|
||||
File dexFileFile = new File(inputDexFileName);
|
||||
if (!dexFileFile.exists()) {
|
||||
System.err.println("Can't find the file " + inputDexFileName);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
DexFile dexFile = new DexFile(dexFileFile);
|
||||
String[] bootClassPaths = new String[bootClassPathDirs.size()];
|
||||
|
||||
int j = 0;
|
||||
for (String bootClassPathDir: bootClassPathDirs) {
|
||||
bootClassPaths[j++] = bootClassPathDir;
|
||||
}
|
||||
|
||||
ClassPath.InitializeClassPathFromOdex(bootClassPaths, null, inputDexFileName, dexFile, false);
|
||||
FileOutputStream outStream = new FileOutputStream(outFile);
|
||||
|
||||
for (ClassDefItem classDefItem: dexFile.ClassDefsSection.getItems()) {
|
||||
ClassPath.ClassDef classDef = ClassPath.getClassDef(classDefItem.getClassType());
|
||||
SparseArray<ClassPath.FieldDef> fields = classDef.getInstanceFields();
|
||||
String className = "Class " + classDef.getClassType() + " : " + fields.size() + " instance fields\n";
|
||||
outStream.write(className.getBytes());
|
||||
for (int i=0;i<fields.size();i++) {
|
||||
String field = fields.keyAt(i) + ":" + fields.valueAt(i).type + " " + fields.valueAt(i).name + "\n";
|
||||
outStream.write(field.getBytes());
|
||||
}
|
||||
outStream.write("\n".getBytes());
|
||||
}
|
||||
outStream.close();
|
||||
} catch (IOException ex) {
|
||||
System.out.println("IOException thrown when trying to open a dex file or write out vtables: " + ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the usage message.
|
||||
*/
|
||||
private static void usage() {
|
||||
int consoleWidth = ConsoleUtil.getConsoleWidth();
|
||||
if (consoleWidth <= 0) {
|
||||
consoleWidth = 80;
|
||||
}
|
||||
|
||||
System.out.println("java -cp baksmali.jar org.jf.dexlib.Code.Analysis.DumpFields -d path/to/jar/files <dex-file>");
|
||||
}
|
||||
|
||||
private static void buildOptions() {
|
||||
Option classPathDirOption = OptionBuilder.withLongOpt("bootclasspath-dir")
|
||||
.withDescription("the base folder to look for the bootclasspath files in. Defaults to the current " +
|
||||
"directory")
|
||||
.hasArg()
|
||||
.withArgName("DIR")
|
||||
.create("d");
|
||||
|
||||
Option outputFileOption = OptionBuilder.withLongOpt("out-file")
|
||||
.withDescription("output file")
|
||||
.hasArg()
|
||||
.withArgName("FILE")
|
||||
.create("o");
|
||||
|
||||
options.addOption(classPathDirOption);
|
||||
options.addOption(outputFileOption);
|
||||
}
|
||||
}
|
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* 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.dexlib.Code.Analysis;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.commons.cli.*;
|
||||
import org.jf.dexlib.ClassDefItem;
|
||||
import org.jf.dexlib.DexFile;
|
||||
import org.jf.util.ConsoleUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class DumpVtables {
|
||||
private static final Options options;
|
||||
|
||||
static {
|
||||
options = new Options();
|
||||
buildOptions();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
CommandLineParser parser = new PosixParser();
|
||||
CommandLine commandLine;
|
||||
|
||||
try {
|
||||
commandLine = parser.parse(options, args);
|
||||
} catch (ParseException ex) {
|
||||
usage();
|
||||
return;
|
||||
}
|
||||
|
||||
String[] remainingArgs = commandLine.getArgs();
|
||||
|
||||
Option[] parsedOptions = commandLine.getOptions();
|
||||
ArrayList<String> bootClassPathDirs = Lists.newArrayList();
|
||||
String outFile = "vtables.txt";
|
||||
|
||||
for (int i=0; i<parsedOptions.length; i++) {
|
||||
Option option = parsedOptions[i];
|
||||
String opt = option.getOpt();
|
||||
|
||||
switch (opt.charAt(0)) {
|
||||
case 'd':
|
||||
bootClassPathDirs.add(option.getValue());
|
||||
break;
|
||||
case 'o':
|
||||
outFile = option.getValue();
|
||||
break;
|
||||
default:
|
||||
assert false;
|
||||
}
|
||||
}
|
||||
|
||||
if (remainingArgs.length != 1) {
|
||||
usage();
|
||||
return;
|
||||
}
|
||||
|
||||
String inputDexFileName = remainingArgs[0];
|
||||
|
||||
File dexFileFile = new File(inputDexFileName);
|
||||
if (!dexFileFile.exists()) {
|
||||
System.err.println("Can't find the file " + inputDexFileName);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
DexFile dexFile = new DexFile(dexFileFile);
|
||||
String[] bootClassPaths = new String[bootClassPathDirs.size()];
|
||||
|
||||
int j = 0;
|
||||
for (String bootClassPathDir: bootClassPathDirs) {
|
||||
bootClassPaths[j++] = bootClassPathDir;
|
||||
}
|
||||
|
||||
ClassPath.InitializeClassPathFromOdex(bootClassPaths, null, inputDexFileName, dexFile, false);
|
||||
FileOutputStream outStream = new FileOutputStream(outFile);
|
||||
|
||||
for (ClassDefItem classDefItem: dexFile.ClassDefsSection.getItems()) {
|
||||
ClassPath.ClassDef classDef = ClassPath.getClassDef(classDefItem.getClassType());
|
||||
ClassPath.VirtualMethod[] methods = classDef.getVtable();
|
||||
String className = "Class " + classDef.getClassType() + " extends " + classDef.getSuperclass().getClassType() + " : " + methods.length + " methods\n";
|
||||
outStream.write(className.getBytes());
|
||||
for (int i=0;i<methods.length;i++) {
|
||||
String method = i + ":" + methods[i].containingClass + "->" + methods[i].method + "\n";
|
||||
outStream.write(method.getBytes());
|
||||
}
|
||||
outStream.write("\n".getBytes());
|
||||
}
|
||||
outStream.close();
|
||||
} catch (IOException ex) {
|
||||
System.out.println("IOException thrown when trying to open a dex file or write out vtables: " + ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the usage message.
|
||||
*/
|
||||
private static void usage() {
|
||||
int consoleWidth = ConsoleUtil.getConsoleWidth();
|
||||
if (consoleWidth <= 0) {
|
||||
consoleWidth = 80;
|
||||
}
|
||||
|
||||
System.out.println("java -cp baksmali.jar org.jf.dexlib.Code.Analysis.DumpVtables -d path/to/jar/files <dex-file>");
|
||||
}
|
||||
|
||||
private static void buildOptions() {
|
||||
Option classPathDirOption = OptionBuilder.withLongOpt("bootclasspath-dir")
|
||||
.withDescription("the base folder to look for the bootclasspath files in. Defaults to the current " +
|
||||
"directory")
|
||||
.hasArg()
|
||||
.withArgName("DIR")
|
||||
.create("d");
|
||||
|
||||
Option outputFileOption = OptionBuilder.withLongOpt("out-file")
|
||||
.withDescription("output file")
|
||||
.hasArg()
|
||||
.withArgName("FILE")
|
||||
.create("o");
|
||||
|
||||
options.addOption(classPathDirOption);
|
||||
options.addOption(outputFileOption);
|
||||
}
|
||||
}
|
@ -32,7 +32,7 @@ import org.jf.dexlib.*;
|
||||
import org.jf.dexlib.Code.*;
|
||||
import org.jf.dexlib.Code.Format.*;
|
||||
import org.jf.dexlib.Util.AccessFlags;
|
||||
import org.jf.dexlib.Util.ExceptionWithContext;
|
||||
import org.jf.util.ExceptionWithContext;
|
||||
import org.jf.dexlib.Util.SparseArray;
|
||||
|
||||
import java.util.BitSet;
|
||||
@ -703,34 +703,28 @@ public class MethodAnalyzer {
|
||||
analyzeConstString(analyzedInstruction);
|
||||
return true;
|
||||
case CONST_CLASS:
|
||||
case CONST_CLASS_JUMBO:
|
||||
analyzeConstClass(analyzedInstruction);
|
||||
return true;
|
||||
case MONITOR_ENTER:
|
||||
case MONITOR_EXIT:
|
||||
return true;
|
||||
case CHECK_CAST:
|
||||
case CHECK_CAST_JUMBO:
|
||||
analyzeCheckCast(analyzedInstruction);
|
||||
return true;
|
||||
case INSTANCE_OF:
|
||||
case INSTANCE_OF_JUMBO:
|
||||
analyzeInstanceOf(analyzedInstruction);
|
||||
return true;
|
||||
case ARRAY_LENGTH:
|
||||
analyzeArrayLength(analyzedInstruction);
|
||||
return true;
|
||||
case NEW_INSTANCE:
|
||||
case NEW_INSTANCE_JUMBO:
|
||||
analyzeNewInstance(analyzedInstruction);
|
||||
return true;
|
||||
case NEW_ARRAY:
|
||||
case NEW_ARRAY_JUMBO:
|
||||
analyzeNewArray(analyzedInstruction);
|
||||
return true;
|
||||
case FILLED_NEW_ARRAY:
|
||||
case FILLED_NEW_ARRAY_RANGE:
|
||||
case FILLED_NEW_ARRAY_JUMBO:
|
||||
return true;
|
||||
case FILL_ARRAY_DATA:
|
||||
analyzeArrayDataOrSwitch(analyzedInstruction);
|
||||
@ -793,86 +787,58 @@ public class MethodAnalyzer {
|
||||
case APUT_OBJECT:
|
||||
return true;
|
||||
case IGET:
|
||||
case IGET_JUMBO:
|
||||
analyze32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Integer);
|
||||
return true;
|
||||
case IGET_BOOLEAN:
|
||||
case IGET_BOOLEAN_JUMBO:
|
||||
analyze32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Boolean);
|
||||
return true;
|
||||
case IGET_BYTE:
|
||||
case IGET_BYTE_JUMBO:
|
||||
analyze32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Byte);
|
||||
return true;
|
||||
case IGET_CHAR:
|
||||
case IGET_CHAR_JUMBO:
|
||||
analyze32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Char);
|
||||
return true;
|
||||
case IGET_SHORT:
|
||||
case IGET_SHORT_JUMBO:
|
||||
analyze32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Short);
|
||||
return true;
|
||||
case IGET_WIDE:
|
||||
case IGET_WIDE_JUMBO:
|
||||
case IGET_OBJECT:
|
||||
case IGET_OBJECT_JUMBO:
|
||||
analyzeIgetWideObject(analyzedInstruction);
|
||||
return true;
|
||||
case IPUT:
|
||||
case IPUT_JUMBO:
|
||||
case IPUT_BOOLEAN:
|
||||
case IPUT_BOOLEAN_JUMBO:
|
||||
case IPUT_BYTE:
|
||||
case IPUT_BYTE_JUMBO:
|
||||
case IPUT_CHAR:
|
||||
case IPUT_CHAR_JUMBO:
|
||||
case IPUT_SHORT:
|
||||
case IPUT_SHORT_JUMBO:
|
||||
case IPUT_WIDE:
|
||||
case IPUT_WIDE_JUMBO:
|
||||
case IPUT_OBJECT:
|
||||
case IPUT_OBJECT_JUMBO:
|
||||
return true;
|
||||
case SGET:
|
||||
case SGET_JUMBO:
|
||||
analyze32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Integer);
|
||||
return true;
|
||||
case SGET_BOOLEAN:
|
||||
case SGET_BOOLEAN_JUMBO:
|
||||
analyze32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Boolean);
|
||||
return true;
|
||||
case SGET_BYTE:
|
||||
case SGET_BYTE_JUMBO:
|
||||
analyze32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Byte);
|
||||
return true;
|
||||
case SGET_CHAR:
|
||||
case SGET_CHAR_JUMBO:
|
||||
analyze32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Char);
|
||||
return true;
|
||||
case SGET_SHORT:
|
||||
case SGET_SHORT_JUMBO:
|
||||
analyze32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Short);
|
||||
return true;
|
||||
case SGET_WIDE:
|
||||
case SGET_WIDE_JUMBO:
|
||||
case SGET_OBJECT:
|
||||
case SGET_OBJECT_JUMBO:
|
||||
analyzeSgetWideObject(analyzedInstruction);
|
||||
return true;
|
||||
case SPUT:
|
||||
case SPUT_JUMBO:
|
||||
case SPUT_BOOLEAN:
|
||||
case SPUT_BOOLEAN_JUMBO:
|
||||
case SPUT_BYTE:
|
||||
case SPUT_BYTE_JUMBO:
|
||||
case SPUT_CHAR:
|
||||
case SPUT_CHAR_JUMBO:
|
||||
case SPUT_SHORT:
|
||||
case SPUT_SHORT_JUMBO:
|
||||
case SPUT_WIDE:
|
||||
case SPUT_WIDE_JUMBO:
|
||||
case SPUT_OBJECT:
|
||||
case SPUT_OBJECT_JUMBO:
|
||||
return true;
|
||||
case INVOKE_VIRTUAL:
|
||||
case INVOKE_SUPER:
|
||||
@ -883,18 +849,13 @@ public class MethodAnalyzer {
|
||||
case INVOKE_STATIC:
|
||||
case INVOKE_INTERFACE:
|
||||
case INVOKE_VIRTUAL_RANGE:
|
||||
case INVOKE_VIRTUAL_JUMBO:
|
||||
case INVOKE_SUPER_RANGE:
|
||||
case INVOKE_SUPER_JUMBO:
|
||||
return true;
|
||||
case INVOKE_DIRECT_RANGE:
|
||||
case INVOKE_DIRECT_JUMBO:
|
||||
analyzeInvokeDirectRange(analyzedInstruction);
|
||||
return true;
|
||||
case INVOKE_STATIC_RANGE:
|
||||
case INVOKE_STATIC_JUMBO:
|
||||
case INVOKE_INTERFACE_RANGE:
|
||||
case INVOKE_INTERFACE_JUMBO:
|
||||
return true;
|
||||
case NEG_INT:
|
||||
case NOT_INT:
|
||||
@ -1115,23 +1076,6 @@ public class MethodAnalyzer {
|
||||
case SPUT_OBJECT_VOLATILE:
|
||||
analyzePutGetVolatile(analyzedInstruction);
|
||||
return true;
|
||||
case INVOKE_OBJECT_INIT_JUMBO:
|
||||
analyzeInvokeObjectInitJumbo(analyzedInstruction);
|
||||
return true;
|
||||
case IGET_VOLATILE_JUMBO:
|
||||
case IGET_WIDE_VOLATILE_JUMBO:
|
||||
case IGET_OBJECT_VOLATILE_JUMBO:
|
||||
case IPUT_VOLATILE_JUMBO:
|
||||
case IPUT_WIDE_VOLATILE_JUMBO:
|
||||
case IPUT_OBJECT_VOLATILE_JUMBO:
|
||||
case SGET_VOLATILE_JUMBO:
|
||||
case SGET_WIDE_VOLATILE_JUMBO:
|
||||
case SGET_OBJECT_VOLATILE_JUMBO:
|
||||
case SPUT_VOLATILE_JUMBO:
|
||||
case SPUT_WIDE_VOLATILE_JUMBO:
|
||||
case SPUT_OBJECT_VOLATILE_JUMBO:
|
||||
analyzePutGetVolatile(analyzedInstruction);
|
||||
return true;
|
||||
default:
|
||||
assert false;
|
||||
return true;
|
||||
@ -1197,7 +1141,6 @@ public class MethodAnalyzer {
|
||||
case CONST_STRING_JUMBO:
|
||||
return;
|
||||
case CONST_CLASS:
|
||||
case CONST_CLASS_JUMBO:
|
||||
verifyConstClass(analyzedInstruction);
|
||||
return;
|
||||
case MONITOR_ENTER:
|
||||
@ -1205,18 +1148,15 @@ public class MethodAnalyzer {
|
||||
verifyMonitor(analyzedInstruction);
|
||||
return;
|
||||
case CHECK_CAST:
|
||||
case CHECK_CAST_JUMBO:
|
||||
verifyCheckCast(analyzedInstruction);
|
||||
return;
|
||||
case INSTANCE_OF:
|
||||
case INSTANCE_OF_JUMBO:
|
||||
verifyInstanceOf(analyzedInstruction);
|
||||
return;
|
||||
case ARRAY_LENGTH:
|
||||
verifyArrayLength(analyzedInstruction);
|
||||
return;
|
||||
case NEW_INSTANCE:
|
||||
case NEW_INSTANCE_JUMBO:
|
||||
verifyNewInstance(analyzedInstruction);
|
||||
return;
|
||||
case NEW_ARRAY:
|
||||
@ -1626,19 +1566,6 @@ public class MethodAnalyzer {
|
||||
case IPUT_OBJECT_VOLATILE:
|
||||
case SGET_OBJECT_VOLATILE:
|
||||
case SPUT_OBJECT_VOLATILE:
|
||||
case INVOKE_OBJECT_INIT_JUMBO:
|
||||
case IGET_VOLATILE_JUMBO:
|
||||
case IGET_WIDE_VOLATILE_JUMBO:
|
||||
case IGET_OBJECT_VOLATILE_JUMBO:
|
||||
case IPUT_VOLATILE_JUMBO:
|
||||
case IPUT_WIDE_VOLATILE_JUMBO:
|
||||
case IPUT_OBJECT_VOLATILE_JUMBO:
|
||||
case SGET_VOLATILE_JUMBO:
|
||||
case SGET_WIDE_VOLATILE_JUMBO:
|
||||
case SGET_OBJECT_VOLATILE_JUMBO:
|
||||
case SPUT_VOLATILE_JUMBO:
|
||||
case SPUT_WIDE_VOLATILE_JUMBO:
|
||||
case SPUT_OBJECT_VOLATILE_JUMBO:
|
||||
//TODO: throw validation exception?
|
||||
default:
|
||||
assert false;
|
||||
@ -2434,7 +2361,7 @@ public class MethodAnalyzer {
|
||||
RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
|
||||
assert arrayRegisterType != null;
|
||||
|
||||
if (! ClassPath.dontLoadClassPath && arrayRegisterType.category != RegisterType.Category.Null) {
|
||||
if (arrayRegisterType.category != RegisterType.Category.Null) {
|
||||
assert arrayRegisterType.type != null;
|
||||
if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
|
||||
throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s",
|
||||
@ -2503,7 +2430,7 @@ public class MethodAnalyzer {
|
||||
RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
|
||||
assert arrayRegisterType != null;
|
||||
|
||||
if (! ClassPath.dontLoadClassPath && arrayRegisterType.category != RegisterType.Category.Null) {
|
||||
if (arrayRegisterType.category != RegisterType.Category.Null) {
|
||||
assert arrayRegisterType.type != null;
|
||||
if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
|
||||
throw new ValidationException(String.format("Cannot use aget-object with non-array type %s",
|
||||
@ -3578,14 +3505,7 @@ public class MethodAnalyzer {
|
||||
return false;
|
||||
}
|
||||
|
||||
ClassPath.ClassDef accessingClass =
|
||||
ClassPath.getClassDef(this.encodedMethod.method.getContainingClass(), false);
|
||||
if (accessingClass == null) {
|
||||
throw new ExceptionWithContext(String.format("Could not find ClassDef for current class: %s",
|
||||
this.encodedMethod.method.getContainingClass()));
|
||||
}
|
||||
|
||||
FieldIdItem fieldIdItem = deodexUtil.lookupField(accessingClass, objectRegisterType.type, fieldOffset);
|
||||
FieldIdItem fieldIdItem = deodexUtil.lookupField(objectRegisterType.type, fieldOffset);
|
||||
if (fieldIdItem == null) {
|
||||
throw new ValidationException(String.format("Could not resolve the field in class %s at offset %d",
|
||||
objectRegisterType.type.getClassType(), fieldOffset));
|
||||
@ -3628,16 +3548,12 @@ public class MethodAnalyzer {
|
||||
}
|
||||
|
||||
MethodIdItem methodIdItem = null;
|
||||
ClassPath.ClassDef accessingClass =
|
||||
ClassPath.getClassDef(this.encodedMethod.method.getContainingClass(), false);
|
||||
if (accessingClass == null) {
|
||||
throw new ExceptionWithContext(String.format("Could not find ClassDef for current class: %s",
|
||||
this.encodedMethod.method.getContainingClass()));
|
||||
}
|
||||
if (isSuper) {
|
||||
if (accessingClass.getSuperclass() != null) {
|
||||
methodIdItem = deodexUtil.lookupVirtualMethod(accessingClass, accessingClass.getSuperclass(),
|
||||
methodIndex);
|
||||
ClassPath.ClassDef classDef = ClassPath.getClassDef(this.encodedMethod.method.getContainingClass(), false);
|
||||
assert classDef != null;
|
||||
|
||||
if (classDef.getSuperclass() != null) {
|
||||
methodIdItem = deodexUtil.lookupVirtualMethod(classDef.getSuperclass(), methodIndex);
|
||||
}
|
||||
|
||||
if (methodIdItem == null) {
|
||||
@ -3645,10 +3561,10 @@ public class MethodAnalyzer {
|
||||
//of from the superclass (although the superclass method is still what would actually be called).
|
||||
//And so the MethodIdItem for the superclass method may not be in the dex file. Let's try to get the
|
||||
//MethodIdItem for the method in the current class instead
|
||||
methodIdItem = deodexUtil.lookupVirtualMethod(accessingClass, accessingClass, methodIndex);
|
||||
methodIdItem = deodexUtil.lookupVirtualMethod(classDef, methodIndex);
|
||||
}
|
||||
} else{
|
||||
methodIdItem = deodexUtil.lookupVirtualMethod(accessingClass, objectRegisterType.type, methodIndex);
|
||||
methodIdItem = deodexUtil.lookupVirtualMethod(objectRegisterType.type, methodIndex);
|
||||
}
|
||||
|
||||
if (methodIdItem == null) {
|
||||
@ -3706,23 +3622,12 @@ public class MethodAnalyzer {
|
||||
|
||||
if (analyzedInstruction.instruction.opcode.isOdexedStaticVolatile()) {
|
||||
SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
|
||||
if (analyzedInstruction.instruction.opcode.format == Format.Format21c) {
|
||||
deodexedInstruction = new Instruction21c(opcode, (byte)instruction.getRegisterA(), fieldIdItem);
|
||||
} else {
|
||||
assert(analyzedInstruction.instruction.opcode.format == Format.Format41c);
|
||||
deodexedInstruction = new Instruction41c(opcode, (byte)instruction.getRegisterA(), fieldIdItem);
|
||||
}
|
||||
deodexedInstruction = new Instruction21c(opcode, (byte)instruction.getRegisterA(), fieldIdItem);
|
||||
} else {
|
||||
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
|
||||
|
||||
if (analyzedInstruction.instruction.opcode.format == Format.Format22c) {
|
||||
deodexedInstruction = new Instruction22c(opcode, (byte)instruction.getRegisterA(),
|
||||
(byte)instruction.getRegisterB(), fieldIdItem);
|
||||
} else {
|
||||
assert(analyzedInstruction.instruction.opcode.format == Format.Format52c);
|
||||
deodexedInstruction = new Instruction52c(opcode, (byte)instruction.getRegisterA(),
|
||||
(byte)instruction.getRegisterB(), fieldIdItem);
|
||||
}
|
||||
deodexedInstruction = new Instruction22c(opcode, (byte)instruction.getRegisterA(),
|
||||
(byte)instruction.getRegisterB(), fieldIdItem);
|
||||
}
|
||||
|
||||
analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
|
||||
@ -3733,17 +3638,6 @@ public class MethodAnalyzer {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void analyzeInvokeObjectInitJumbo(AnalyzedInstruction analyzedInstruction) {
|
||||
Instruction5rc instruction = (Instruction5rc)analyzedInstruction.instruction;
|
||||
|
||||
Instruction5rc deodexedInstruction = new Instruction5rc(Opcode.INVOKE_DIRECT_JUMBO,
|
||||
instruction.getRegCount(), instruction.getStartRegister(), instruction.getReferencedItem());
|
||||
|
||||
analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
|
||||
|
||||
analyzeInstruction(analyzedInstruction);
|
||||
}
|
||||
|
||||
private static boolean checkArrayFieldAssignment(RegisterType.Category arrayFieldCategory,
|
||||
RegisterType.Category instructionCategory) {
|
||||
if (arrayFieldCategory == instructionCategory) {
|
||||
|
@ -178,107 +178,6 @@ public class OdexedFieldInstructionMapper {
|
||||
}
|
||||
};
|
||||
|
||||
private static Opcode[][][][] jumboOpcodeMap = new Opcode[][][][] {
|
||||
//get opcodes
|
||||
new Opcode[][][] {
|
||||
//iget volatile
|
||||
new Opcode[][] {
|
||||
//odexed
|
||||
new Opcode[] {
|
||||
/*Z*/ Opcode.IGET_VOLATILE_JUMBO,
|
||||
/*B*/ Opcode.IGET_VOLATILE_JUMBO,
|
||||
/*S*/ Opcode.IGET_VOLATILE_JUMBO,
|
||||
/*C*/ Opcode.IGET_VOLATILE_JUMBO,
|
||||
/*I,F*/ Opcode.IGET_VOLATILE_JUMBO,
|
||||
/*J,D*/ Opcode.IGET_WIDE_VOLATILE_JUMBO,
|
||||
/*L,[*/ Opcode.IGET_OBJECT_VOLATILE_JUMBO
|
||||
},
|
||||
//deodexed
|
||||
new Opcode[] {
|
||||
/*Z*/ Opcode.IGET_BOOLEAN_JUMBO,
|
||||
/*B*/ Opcode.IGET_BYTE_JUMBO,
|
||||
/*S*/ Opcode.IGET_SHORT_JUMBO,
|
||||
/*C*/ Opcode.IGET_CHAR_JUMBO,
|
||||
/*I,F*/ Opcode.IGET_JUMBO,
|
||||
/*J,D*/ Opcode.IGET_WIDE_JUMBO,
|
||||
/*L,[*/ Opcode.IGET_OBJECT_JUMBO
|
||||
}
|
||||
},
|
||||
//sget volatile
|
||||
new Opcode[][] {
|
||||
//odexed
|
||||
new Opcode[] {
|
||||
/*Z*/ Opcode.SGET_VOLATILE_JUMBO,
|
||||
/*B*/ Opcode.SGET_VOLATILE_JUMBO,
|
||||
/*S*/ Opcode.SGET_VOLATILE_JUMBO,
|
||||
/*C*/ Opcode.SGET_VOLATILE_JUMBO,
|
||||
/*I,F*/ Opcode.SGET_VOLATILE_JUMBO,
|
||||
/*J,D*/ Opcode.SGET_WIDE_VOLATILE_JUMBO,
|
||||
/*L,[*/ Opcode.SGET_OBJECT_VOLATILE_JUMBO
|
||||
},
|
||||
//deodexed
|
||||
new Opcode[] {
|
||||
/*Z*/ Opcode.SGET_BOOLEAN_JUMBO,
|
||||
/*B*/ Opcode.SGET_BYTE_JUMBO,
|
||||
/*S*/ Opcode.SGET_SHORT_JUMBO,
|
||||
/*C*/ Opcode.SGET_CHAR_JUMBO,
|
||||
/*I,F*/ Opcode.SGET_JUMBO,
|
||||
/*J,D*/ Opcode.SGET_WIDE_JUMBO,
|
||||
/*L,[*/ Opcode.SGET_OBJECT_JUMBO
|
||||
}
|
||||
}
|
||||
},
|
||||
//put opcodes
|
||||
new Opcode[][][] {
|
||||
//iput volatile
|
||||
new Opcode[][] {
|
||||
//odexed
|
||||
new Opcode[] {
|
||||
/*Z*/ Opcode.IPUT_VOLATILE_JUMBO,
|
||||
/*B*/ Opcode.IPUT_VOLATILE_JUMBO,
|
||||
/*S*/ Opcode.IPUT_VOLATILE_JUMBO,
|
||||
/*C*/ Opcode.IPUT_VOLATILE_JUMBO,
|
||||
/*I,F*/ Opcode.IPUT_VOLATILE_JUMBO,
|
||||
/*J,D*/ Opcode.IPUT_WIDE_VOLATILE_JUMBO,
|
||||
/*L,[*/ Opcode.IPUT_OBJECT_VOLATILE_JUMBO
|
||||
},
|
||||
//deodexed
|
||||
new Opcode[] {
|
||||
/*Z*/ Opcode.IPUT_BOOLEAN_JUMBO,
|
||||
/*B*/ Opcode.IPUT_BYTE_JUMBO,
|
||||
/*S*/ Opcode.IPUT_SHORT_JUMBO,
|
||||
/*C*/ Opcode.IPUT_CHAR_JUMBO,
|
||||
/*I,F*/ Opcode.IPUT_JUMBO,
|
||||
/*J,D*/ Opcode.IPUT_WIDE_JUMBO,
|
||||
/*L,[*/ Opcode.IPUT_OBJECT_JUMBO
|
||||
}
|
||||
},
|
||||
//sput volatile
|
||||
new Opcode[][] {
|
||||
//odexed
|
||||
new Opcode[] {
|
||||
/*Z*/ Opcode.SPUT_VOLATILE_JUMBO,
|
||||
/*B*/ Opcode.SPUT_VOLATILE_JUMBO,
|
||||
/*S*/ Opcode.SPUT_VOLATILE_JUMBO,
|
||||
/*C*/ Opcode.SPUT_VOLATILE_JUMBO,
|
||||
/*I,F*/ Opcode.SPUT_VOLATILE_JUMBO,
|
||||
/*J,D*/ Opcode.SPUT_WIDE_VOLATILE_JUMBO,
|
||||
/*L,[*/ Opcode.SPUT_OBJECT_VOLATILE_JUMBO
|
||||
},
|
||||
//deodexed
|
||||
new Opcode[] {
|
||||
/*Z*/ Opcode.SPUT_BOOLEAN_JUMBO,
|
||||
/*B*/ Opcode.SPUT_BYTE_JUMBO,
|
||||
/*S*/ Opcode.SPUT_SHORT_JUMBO,
|
||||
/*C*/ Opcode.SPUT_CHAR_JUMBO,
|
||||
/*I,F*/ Opcode.SPUT_JUMBO,
|
||||
/*J,D*/ Opcode.SPUT_WIDE_JUMBO,
|
||||
/*L,[*/ Opcode.SPUT_OBJECT_JUMBO
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private static int getTypeIndex(char type) {
|
||||
switch (type) {
|
||||
case 'Z':
|
||||
@ -315,20 +214,14 @@ public class OdexedFieldInstructionMapper {
|
||||
}
|
||||
|
||||
static Opcode getAndCheckDeodexedOpcodeForOdexedOpcode(String fieldType, Opcode odexedOpcode) {
|
||||
boolean jumbo = odexedOpcode.isJumboOpcode();
|
||||
int opcodeType = odexedOpcode.setsRegister()?0:1;
|
||||
int opcodeSubType = getOpcodeSubtype(odexedOpcode);
|
||||
int typeIndex = getTypeIndex(fieldType.charAt(0));
|
||||
|
||||
Opcode correctOdexedOpcode, deodexedOpcode;
|
||||
|
||||
if (jumbo) {
|
||||
correctOdexedOpcode = jumboOpcodeMap[opcodeType][opcodeSubType-1][0][typeIndex];
|
||||
deodexedOpcode = jumboOpcodeMap[opcodeType][opcodeSubType-1][1][typeIndex];
|
||||
} else {
|
||||
correctOdexedOpcode = opcodeMap[opcodeType][opcodeSubType][0][typeIndex];
|
||||
deodexedOpcode = opcodeMap[opcodeType][opcodeSubType][1][typeIndex];
|
||||
}
|
||||
correctOdexedOpcode = opcodeMap[opcodeType][opcodeSubType][0][typeIndex];
|
||||
deodexedOpcode = opcodeMap[opcodeType][opcodeSubType][1][typeIndex];
|
||||
|
||||
if (correctOdexedOpcode != odexedOpcode) {
|
||||
throw new ValidationException(String.format("Incorrect field type \"%s\" for %s", fieldType,
|
||||
|
@ -28,7 +28,7 @@
|
||||
|
||||
package org.jf.dexlib.Code.Analysis;
|
||||
|
||||
import org.jf.dexlib.Util.ExceptionWithContext;
|
||||
import org.jf.util.ExceptionWithContext;
|
||||
|
||||
public class ValidationException extends ExceptionWithContext {
|
||||
private int codeAddress;
|
||||
|
@ -60,10 +60,7 @@ public enum Format {
|
||||
Format3rc(Instruction3rc.Factory, 6),
|
||||
Format3rmi(Instruction3rmi.Factory, 6),
|
||||
Format3rms(Instruction3rms.Factory, 6),
|
||||
Format41c(Instruction41c.Factory, 8),
|
||||
Format51l(Instruction51l.Factory, 10),
|
||||
Format52c(Instruction52c.Factory, 10),
|
||||
Format5rc(Instruction5rc.Factory, 10),
|
||||
ArrayData(null, -1, true),
|
||||
PackedSwitchData(null, -1, true),
|
||||
SparseSwitchData(null, -1, true),
|
||||
|
@ -100,11 +100,7 @@ public class Instruction21c extends InstructionWithReference implements SingleRe
|
||||
return null;
|
||||
}
|
||||
|
||||
if (jumboOpcode.format == Format.Format31c) {
|
||||
return new Instruction31c(jumboOpcode, (short)getRegisterA(), getReferencedItem());
|
||||
}
|
||||
|
||||
return new Instruction41c(jumboOpcode, getRegisterA(), getReferencedItem());
|
||||
return new Instruction31c(jumboOpcode, (short)getRegisterA(), getReferencedItem());
|
||||
}
|
||||
|
||||
private static class Factory implements Instruction.InstructionFactory {
|
||||
|
@ -37,8 +37,7 @@ import org.jf.dexlib.Item;
|
||||
import org.jf.dexlib.Util.AnnotatedOutput;
|
||||
import org.jf.dexlib.Util.NumberUtils;
|
||||
|
||||
public class Instruction22c extends InstructionWithReference implements TwoRegisterInstruction,
|
||||
InstructionWithJumboVariant {
|
||||
public class Instruction22c extends InstructionWithReference implements TwoRegisterInstruction {
|
||||
public static final Instruction.InstructionFactory Factory = new Factory();
|
||||
private byte regA;
|
||||
private byte regB;
|
||||
@ -89,15 +88,6 @@ public class Instruction22c extends InstructionWithReference implements TwoRegis
|
||||
return regB;
|
||||
}
|
||||
|
||||
public Instruction makeJumbo() {
|
||||
Opcode jumboOpcode = opcode.getJumboOpcode();
|
||||
if (jumboOpcode == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Instruction52c(jumboOpcode, getRegisterA(), getRegisterB(), getReferencedItem());
|
||||
}
|
||||
|
||||
private static class Factory implements Instruction.InstructionFactory {
|
||||
public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
|
||||
return new Instruction22c(dexFile, opcode, buffer, bufferIndex);
|
||||
|
@ -41,8 +41,7 @@ import org.jf.dexlib.Util.NumberUtils;
|
||||
|
||||
import static org.jf.dexlib.Code.Opcode.*;
|
||||
|
||||
public class Instruction3rc extends InstructionWithReference implements RegisterRangeInstruction,
|
||||
InstructionWithJumboVariant {
|
||||
public class Instruction3rc extends InstructionWithReference implements RegisterRangeInstruction {
|
||||
public static final Instruction.InstructionFactory Factory = new Factory();
|
||||
private byte regCount;
|
||||
private short startReg;
|
||||
@ -131,15 +130,6 @@ public class Instruction3rc extends InstructionWithReference implements Register
|
||||
}
|
||||
}
|
||||
|
||||
public Instruction makeJumbo() {
|
||||
Opcode jumboOpcode = opcode.getJumboOpcode();
|
||||
if (jumboOpcode == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Instruction5rc(jumboOpcode, getRegCount(), getStartRegister(), getReferencedItem());
|
||||
}
|
||||
|
||||
private static class Factory implements Instruction.InstructionFactory {
|
||||
public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
|
||||
return new Instruction3rc(dexFile, opcode, buffer, bufferIndex);
|
||||
|
@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011, 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.dexlib.Code.Format;
|
||||
|
||||
import org.jf.dexlib.Code.Instruction;
|
||||
import org.jf.dexlib.Code.InstructionWithReference;
|
||||
import org.jf.dexlib.Code.Opcode;
|
||||
import org.jf.dexlib.Code.SingleRegisterInstruction;
|
||||
import org.jf.dexlib.DexFile;
|
||||
import org.jf.dexlib.Item;
|
||||
import org.jf.dexlib.TypeIdItem;
|
||||
import org.jf.dexlib.Util.AnnotatedOutput;
|
||||
import org.jf.dexlib.Util.NumberUtils;
|
||||
|
||||
public class Instruction41c extends InstructionWithJumboReference implements SingleRegisterInstruction {
|
||||
public static final InstructionFactory Factory = new Factory();
|
||||
private short regA;
|
||||
|
||||
public Instruction41c(Opcode opcode, int regA, Item referencedItem) {
|
||||
super(opcode, referencedItem);
|
||||
|
||||
if (regA >= 1 << 16) {
|
||||
throw new RuntimeException("The register number must be less than v65536");
|
||||
}
|
||||
|
||||
if (opcode == Opcode.NEW_INSTANCE_JUMBO) {
|
||||
assert referencedItem instanceof TypeIdItem;
|
||||
if (((TypeIdItem)referencedItem).getTypeDescriptor().charAt(0) != 'L') {
|
||||
throw new RuntimeException("Only class references can be used with the new-instance/jumbo opcode");
|
||||
}
|
||||
}
|
||||
|
||||
this.regA = (short)regA;
|
||||
}
|
||||
|
||||
private Instruction41c(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
|
||||
super(dexFile, opcode, buffer, bufferIndex);
|
||||
|
||||
if (opcode == Opcode.NEW_INSTANCE_JUMBO &&
|
||||
((TypeIdItem)this.getReferencedItem()).getTypeDescriptor().charAt(0) != 'L') {
|
||||
|
||||
throw new RuntimeException("Only class references can be used with the new-instance/jumbo opcode");
|
||||
}
|
||||
|
||||
this.regA = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 6);
|
||||
}
|
||||
|
||||
protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
|
||||
out.writeByte(0xFF);
|
||||
out.writeByte(opcode.value);
|
||||
out.writeInt(getReferencedItem().getIndex());
|
||||
out.writeShort(getRegisterA());
|
||||
}
|
||||
|
||||
public Format getFormat() {
|
||||
return Format.Format41c;
|
||||
}
|
||||
|
||||
public int getRegisterA() {
|
||||
return regA & 0xFFFF;
|
||||
}
|
||||
|
||||
private static class Factory implements InstructionFactory {
|
||||
public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
|
||||
return new Instruction41c(dexFile, opcode, buffer, bufferIndex);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011, 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.dexlib.Code.Format;
|
||||
|
||||
import org.jf.dexlib.Code.Instruction;
|
||||
import org.jf.dexlib.Code.Opcode;
|
||||
import org.jf.dexlib.Code.TwoRegisterInstruction;
|
||||
import org.jf.dexlib.DexFile;
|
||||
import org.jf.dexlib.Item;
|
||||
import org.jf.dexlib.Util.AnnotatedOutput;
|
||||
import org.jf.dexlib.Util.NumberUtils;
|
||||
|
||||
public class Instruction52c extends InstructionWithJumboReference implements TwoRegisterInstruction {
|
||||
public static final InstructionFactory Factory = new Factory();
|
||||
private short regA;
|
||||
private short regB;
|
||||
|
||||
public Instruction52c(Opcode opcode, int regA, int regB, Item referencedItem) {
|
||||
super(opcode, referencedItem);
|
||||
|
||||
if (regA >= 1 << 16) {
|
||||
throw new RuntimeException("The register number must be less than v65536");
|
||||
}
|
||||
|
||||
if (regB >= 1 << 16) {
|
||||
throw new RuntimeException("The register number must be less than v65536");
|
||||
}
|
||||
|
||||
this.regA = (short)regA;
|
||||
this.regB = (short)regB;
|
||||
}
|
||||
|
||||
private Instruction52c(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
|
||||
super(dexFile, opcode, buffer, bufferIndex);
|
||||
|
||||
this.regA = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 6);
|
||||
this.regB = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 8);
|
||||
}
|
||||
|
||||
protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
|
||||
out.writeByte(0xFF);
|
||||
out.writeByte(opcode.value);
|
||||
out.writeInt(getReferencedItem().getIndex());
|
||||
out.writeShort(getRegisterA());
|
||||
out.writeShort(getRegisterB());
|
||||
}
|
||||
|
||||
public Format getFormat() {
|
||||
return Format.Format52c;
|
||||
}
|
||||
|
||||
public int getRegisterA() {
|
||||
return regA & 0xFFFF;
|
||||
}
|
||||
|
||||
public int getRegisterB() {
|
||||
return regB & 0xFFFF;
|
||||
}
|
||||
|
||||
private static class Factory implements InstructionFactory {
|
||||
public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
|
||||
return new Instruction52c(dexFile, opcode, buffer, bufferIndex);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011, 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.dexlib.Code.Format;
|
||||
|
||||
import org.jf.dexlib.Code.Instruction;
|
||||
import org.jf.dexlib.Code.InstructionWithReference;
|
||||
import org.jf.dexlib.Code.Opcode;
|
||||
import org.jf.dexlib.Code.RegisterRangeInstruction;
|
||||
import org.jf.dexlib.DexFile;
|
||||
import org.jf.dexlib.Item;
|
||||
import org.jf.dexlib.MethodIdItem;
|
||||
import org.jf.dexlib.TypeIdItem;
|
||||
import org.jf.dexlib.Util.AnnotatedOutput;
|
||||
import org.jf.dexlib.Util.NumberUtils;
|
||||
|
||||
import static org.jf.dexlib.Code.Opcode.*;
|
||||
|
||||
public class Instruction5rc extends InstructionWithJumboReference implements RegisterRangeInstruction {
|
||||
public static final InstructionFactory Factory = new Factory();
|
||||
private short regCount;
|
||||
private short startReg;
|
||||
|
||||
public Instruction5rc(Opcode opcode, int regCount, int startReg, Item referencedItem) {
|
||||
super(opcode, referencedItem);
|
||||
|
||||
if (regCount >= 1 << 16) {
|
||||
throw new RuntimeException("regCount must be less than 65536");
|
||||
}
|
||||
if (regCount < 0) {
|
||||
throw new RuntimeException("regCount cannot be negative");
|
||||
}
|
||||
|
||||
if (startReg >= 1 << 16) {
|
||||
throw new RuntimeException("The beginning register of the range must be less than 65536");
|
||||
}
|
||||
if (startReg < 0) {
|
||||
throw new RuntimeException("The beginning register of the range cannot be negative");
|
||||
}
|
||||
|
||||
this.regCount = (short)regCount;
|
||||
this.startReg = (short)startReg;
|
||||
|
||||
checkItem(opcode, referencedItem, regCount);
|
||||
}
|
||||
|
||||
private Instruction5rc(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
|
||||
super(dexFile, opcode, buffer, bufferIndex);
|
||||
|
||||
this.regCount = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 6);
|
||||
this.startReg = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 8);
|
||||
|
||||
checkItem(opcode, getReferencedItem(), getRegCount());
|
||||
}
|
||||
|
||||
protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
|
||||
out.writeByte(0xff);
|
||||
out.writeByte(opcode.value);
|
||||
out.writeInt(this.getReferencedItem().getIndex());
|
||||
out.writeShort(regCount);
|
||||
out.writeShort(startReg);
|
||||
}
|
||||
|
||||
public Format getFormat() {
|
||||
return Format.Format5rc;
|
||||
}
|
||||
|
||||
public int getRegCount() {
|
||||
return regCount & 0xFFFF;
|
||||
}
|
||||
|
||||
public int getStartRegister() {
|
||||
return startReg & 0xFFFF;
|
||||
}
|
||||
|
||||
private static void checkItem(Opcode opcode, Item item, int regCount) {
|
||||
if (opcode == FILLED_NEW_ARRAY_JUMBO) {
|
||||
//check data for filled-new-array/jumbo opcode
|
||||
String type = ((TypeIdItem) item).getTypeDescriptor();
|
||||
if (type.charAt(0) != '[') {
|
||||
throw new RuntimeException("The type must be an array type");
|
||||
}
|
||||
if (type.charAt(1) == 'J' || type.charAt(1) == 'D') {
|
||||
throw new RuntimeException("The type cannot be an array of longs or doubles");
|
||||
}
|
||||
} else if (opcode.value >= INVOKE_VIRTUAL_JUMBO.value && opcode.value <= INVOKE_INTERFACE_JUMBO.value ||
|
||||
opcode == INVOKE_OBJECT_INIT_JUMBO) {
|
||||
//check data for invoke-*/range opcodes
|
||||
MethodIdItem methodIdItem = (MethodIdItem) item;
|
||||
int parameterRegisterCount = methodIdItem.getPrototype().getParameterRegisterCount();
|
||||
if (opcode != INVOKE_STATIC_JUMBO) {
|
||||
parameterRegisterCount++;
|
||||
}
|
||||
if (parameterRegisterCount != regCount) {
|
||||
throw new RuntimeException("regCount does not match the number of arguments of the method");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class Factory implements InstructionFactory {
|
||||
public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
|
||||
return new Instruction5rc(dexFile, opcode, buffer, bufferIndex);
|
||||
}
|
||||
}
|
||||
}
|
@ -30,8 +30,7 @@ package org.jf.dexlib.Code;
|
||||
|
||||
import org.jf.dexlib.Code.Format.*;
|
||||
import org.jf.dexlib.DexFile;
|
||||
import org.jf.dexlib.Util.ExceptionWithContext;
|
||||
import org.jf.dexlib.Util.Hex;
|
||||
import org.jf.util.ExceptionWithContext;
|
||||
|
||||
public class InstructionIterator {
|
||||
public static void IterateInstructions(DexFile dexFile, byte[] insns, ProcessInstructionDelegate delegate) {
|
||||
|
@ -62,16 +62,16 @@ public enum Opcode
|
||||
CONST_WIDE_HIGH16((short)0x19, "const-wide/high16", ReferenceType.none, Format.Format21h, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
CONST_STRING((short)0x1a, "const-string", ReferenceType.string, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0x1b),
|
||||
CONST_STRING_JUMBO((short)0x1b, "const-string/jumbo", ReferenceType.string, Format.Format31c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
CONST_CLASS((short)0x1c, "const-class", ReferenceType.type, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff00),
|
||||
CONST_CLASS((short)0x1c, "const-class", ReferenceType.type, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MONITOR_ENTER((short)0x1d, "monitor-enter", ReferenceType.none, Format.Format11x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
MONITOR_EXIT((short)0x1e, "monitor-exit", ReferenceType.none, Format.Format11x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
CHECK_CAST((short)0x1f, "check-cast", ReferenceType.type, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff01),
|
||||
INSTANCE_OF((short)0x20, "instance-of", ReferenceType.type, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff02),
|
||||
CHECK_CAST((short)0x1f, "check-cast", ReferenceType.type, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
INSTANCE_OF((short)0x20, "instance-of", ReferenceType.type, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
ARRAY_LENGTH((short)0x21, "array-length", ReferenceType.none, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
NEW_INSTANCE((short)0x22, "new-instance", ReferenceType.type, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff03),
|
||||
NEW_ARRAY((short)0x23, "new-array", ReferenceType.type, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff04),
|
||||
NEW_INSTANCE((short)0x22, "new-instance", ReferenceType.type, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
NEW_ARRAY((short)0x23, "new-array", ReferenceType.type, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
FILLED_NEW_ARRAY((short)0x24, "filled-new-array", ReferenceType.type, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
FILLED_NEW_ARRAY_RANGE((short)0x25, "filled-new-array/range", ReferenceType.type, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT, (short)0xff05),
|
||||
FILLED_NEW_ARRAY_RANGE((short)0x25, "filled-new-array/range", ReferenceType.type, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
FILL_ARRAY_DATA((short)0x26, "fill-array-data", ReferenceType.none, Format.Format31t, Opcode.CAN_CONTINUE),
|
||||
THROW((short)0x27, "throw", ReferenceType.none, Format.Format11x, Opcode.CAN_THROW),
|
||||
GOTO((short)0x28, "goto", ReferenceType.none, Format.Format10t),
|
||||
@ -110,44 +110,44 @@ public enum Opcode
|
||||
APUT_BYTE((short)0x4f, "aput-byte", ReferenceType.none, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
APUT_CHAR((short)0x50, "aput-char", ReferenceType.none, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
APUT_SHORT((short)0x51, "aput-short", ReferenceType.none, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IGET((short)0x52, "iget", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff06),
|
||||
IGET_WIDE((short)0x53, "iget-wide", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER, (short)0xff07),
|
||||
IGET_OBJECT((short)0x54, "iget-object", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff08),
|
||||
IGET_BOOLEAN((short)0x55, "iget-boolean", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff09),
|
||||
IGET_BYTE((short)0x56, "iget-byte", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff0a),
|
||||
IGET_CHAR((short)0x57, "iget-char", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff0b),
|
||||
IGET_SHORT((short)0x58, "iget-short", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff0c),
|
||||
IPUT((short)0x59, "iput", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff0d),
|
||||
IPUT_WIDE((short)0x5a, "iput-wide", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff0e),
|
||||
IPUT_OBJECT((short)0x5b, "iput-object", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff0f),
|
||||
IPUT_BOOLEAN((short)0x5c, "iput-boolean", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff10),
|
||||
IPUT_BYTE((short)0x5d, "iput-byte", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff11),
|
||||
IPUT_CHAR((short)0x5e, "iput-char", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff12),
|
||||
IPUT_SHORT((short)0x5f, "iput-short", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff13),
|
||||
SGET((short)0x60, "sget", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff14),
|
||||
SGET_WIDE((short)0x61, "sget-wide", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER, (short)0xff15),
|
||||
SGET_OBJECT((short)0x62, "sget-object", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff16),
|
||||
SGET_BOOLEAN((short)0x63, "sget-boolean", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff17),
|
||||
SGET_BYTE((short)0x64, "sget-byte", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff18),
|
||||
SGET_CHAR((short)0x65, "sget-char", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff19),
|
||||
SGET_SHORT((short)0x66, "sget-short", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff1a),
|
||||
SPUT((short)0x67, "sput", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff1b),
|
||||
SPUT_WIDE((short)0x68, "sput-wide", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff1c),
|
||||
SPUT_OBJECT((short)0x69, "sput-object", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff1d),
|
||||
SPUT_BOOLEAN((short)0x6a, "sput-boolean", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff1e),
|
||||
SPUT_BYTE((short)0x6b, "sput-byte", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff1f),
|
||||
SPUT_CHAR((short)0x6c, "sput-char", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff20),
|
||||
SPUT_SHORT((short)0x6d, "sput-short", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff21),
|
||||
IGET((short)0x52, "iget", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IGET_WIDE((short)0x53, "iget-wide", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
IGET_OBJECT((short)0x54, "iget-object", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IGET_BOOLEAN((short)0x55, "iget-boolean", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IGET_BYTE((short)0x56, "iget-byte", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IGET_CHAR((short)0x57, "iget-char", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IGET_SHORT((short)0x58, "iget-short", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IPUT((short)0x59, "iput", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IPUT_WIDE((short)0x5a, "iput-wide", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IPUT_OBJECT((short)0x5b, "iput-object", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IPUT_BOOLEAN((short)0x5c, "iput-boolean", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IPUT_BYTE((short)0x5d, "iput-byte", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IPUT_CHAR((short)0x5e, "iput-char", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IPUT_SHORT((short)0x5f, "iput-short", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
SGET((short)0x60, "sget", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SGET_WIDE((short)0x61, "sget-wide", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
SGET_OBJECT((short)0x62, "sget-object", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SGET_BOOLEAN((short)0x63, "sget-boolean", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SGET_BYTE((short)0x64, "sget-byte", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SGET_CHAR((short)0x65, "sget-char", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SGET_SHORT((short)0x66, "sget-short", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SPUT((short)0x67, "sput", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
SPUT_WIDE((short)0x68, "sput-wide", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
SPUT_OBJECT((short)0x69, "sput-object", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
SPUT_BOOLEAN((short)0x6a, "sput-boolean", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
SPUT_BYTE((short)0x6b, "sput-byte", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
SPUT_CHAR((short)0x6c, "sput-char", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
SPUT_SHORT((short)0x6d, "sput-short", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
INVOKE_VIRTUAL((short)0x6e, "invoke-virtual", ReferenceType.method, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_SUPER((short)0x6f, "invoke-super", ReferenceType.method, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_DIRECT((short)0x70, "invoke-direct", ReferenceType.method, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE),
|
||||
INVOKE_STATIC((short)0x71, "invoke-static", ReferenceType.method, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_INTERFACE((short)0x72, "invoke-interface", ReferenceType.method, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_VIRTUAL_RANGE((short)0x74, "invoke-virtual/range", ReferenceType.method, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT, (short)0xff22),
|
||||
INVOKE_SUPER_RANGE((short)0x75, "invoke-super/range", ReferenceType.method, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT, (short)0xff23),
|
||||
INVOKE_DIRECT_RANGE((short)0x76, "invoke-direct/range", ReferenceType.method, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE, (short)0xff24),
|
||||
INVOKE_STATIC_RANGE((short)0x77, "invoke-static/range", ReferenceType.method, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT, (short)0xff25),
|
||||
INVOKE_INTERFACE_RANGE((short)0x78, "invoke-interface/range", ReferenceType.method, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT, (short)0xff26),
|
||||
INVOKE_VIRTUAL_RANGE((short)0x74, "invoke-virtual/range", ReferenceType.method, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_SUPER_RANGE((short)0x75, "invoke-super/range", ReferenceType.method, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_DIRECT_RANGE((short)0x76, "invoke-direct/range", ReferenceType.method, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE),
|
||||
INVOKE_STATIC_RANGE((short)0x77, "invoke-static/range", ReferenceType.method, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_INTERFACE_RANGE((short)0x78, "invoke-interface/range", ReferenceType.method, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
NEG_INT((short)0x7b, "neg-int", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
NOT_INT((short)0x7c, "not-int", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
NEG_LONG((short)0x7d, "neg-long", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
@ -282,61 +282,7 @@ public enum Opcode
|
||||
|
||||
IPUT_OBJECT_VOLATILE((short)0xfc, "iput-object-volatile", ReferenceType.field, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
SGET_OBJECT_VOLATILE((short)0xfd, "sget-object-volatile", ReferenceType.field, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SPUT_OBJECT_VOLATILE((short)0xfe, "sput-object-volatile", ReferenceType.field, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
|
||||
CONST_CLASS_JUMBO((short)0xff00, "const-class/jumbo", ReferenceType.type, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
|
||||
CHECK_CAST_JUMBO((short)0xff01, "check-cast/jumbo", ReferenceType.type, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
|
||||
INSTANCE_OF_JUMBO((short)0xff02, "instance-of/jumbo", ReferenceType.type, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
|
||||
NEW_INSTANCE_JUMBO((short)0xff03, "new-instance/jumbo", ReferenceType.type, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
|
||||
NEW_ARRAY_JUMBO((short)0xff04, "new-array/jumbo", ReferenceType.type, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
|
||||
FILLED_NEW_ARRAY_JUMBO((short)0xff05, "filled-new-array/jumbo", ReferenceType.type, Format.Format5rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.JUMBO_OPCODE),
|
||||
IGET_JUMBO((short)0xff06, "iget/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
|
||||
IGET_WIDE_JUMBO((short)0xff07, "iget-wide/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER | Opcode.JUMBO_OPCODE),
|
||||
IGET_OBJECT_JUMBO((short)0xff08, "iget-object/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
|
||||
IGET_BOOLEAN_JUMBO((short)0xff09, "iget-boolean/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
|
||||
IGET_BYTE_JUMBO((short)0xff0a, "iget-byte/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
|
||||
IGET_CHAR_JUMBO((short)0xff0b, "iget-char/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
|
||||
IGET_SHORT_JUMBO((short)0xff0c, "iget-short/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
|
||||
IPUT_JUMBO((short)0xff0d, "iput/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
|
||||
IPUT_WIDE_JUMBO((short)0xff0e, "iput-wide/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
|
||||
IPUT_OBJECT_JUMBO((short)0xff0f, "iput-object/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
|
||||
IPUT_BOOLEAN_JUMBO((short)0xff10, "iput-boolean/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
|
||||
IPUT_BYTE_JUMBO((short)0xff11, "iput-byte/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
|
||||
IPUT_CHAR_JUMBO((short)0xff12, "iput-char/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
|
||||
IPUT_SHORT_JUMBO((short)0xff13, "iput-short/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
|
||||
SGET_JUMBO((short)0xff14, "sget/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
|
||||
SGET_WIDE_JUMBO((short)0xff15, "sget-wide/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER | Opcode.JUMBO_OPCODE),
|
||||
SGET_OBJECT_JUMBO((short)0xff16, "sget-object/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
|
||||
SGET_BOOLEAN_JUMBO((short)0xff17, "sget-boolean/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
|
||||
SGET_BYTE_JUMBO((short)0xff18, "sget-byte/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
|
||||
SGET_CHAR_JUMBO((short)0xff19, "sget-char/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
|
||||
SGET_SHORT_JUMBO((short)0xff1a, "sget-short/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
|
||||
SPUT_JUMBO((short)0xff1b, "sput/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
|
||||
SPUT_WIDE_JUMBO((short)0xff1c, "sput-wide/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
|
||||
SPUT_OBJECT_JUMBO((short)0xff1d, "sput-object/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
|
||||
SPUT_BOOLEAN_JUMBO((short)0xff1e, "sput-boolean/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
|
||||
SPUT_BYTE_JUMBO((short)0xff1f, "sput-byte/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
|
||||
SPUT_CHAR_JUMBO((short)0xff20, "sput-char/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
|
||||
SPUT_SHORT_JUMBO((short)0xff21, "sput-short/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
|
||||
INVOKE_VIRTUAL_JUMBO((short)0xff22, "invoke-virtual/jumbo", ReferenceType.method, Format.Format5rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.JUMBO_OPCODE),
|
||||
INVOKE_SUPER_JUMBO((short)0xff23, "invoke-super/jumbo", ReferenceType.method, Format.Format5rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.JUMBO_OPCODE),
|
||||
INVOKE_DIRECT_JUMBO((short)0xff24, "invoke-direct/jumbo", ReferenceType.method, Format.Format5rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.JUMBO_OPCODE | Opcode.CAN_INITIALIZE_REFERENCE),
|
||||
INVOKE_STATIC_JUMBO((short)0xff25, "invoke-static/jumbo", ReferenceType.method, Format.Format5rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.JUMBO_OPCODE),
|
||||
INVOKE_INTERFACE_JUMBO((short)0xff26, "invoke-interface/jumbo", ReferenceType.method, Format.Format5rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.JUMBO_OPCODE),
|
||||
|
||||
INVOKE_OBJECT_INIT_JUMBO((short)0xfff2, "invoke-object-init/jumbo", ReferenceType.method, Format.Format5rc, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.JUMBO_OPCODE | Opcode.CAN_INITIALIZE_REFERENCE),
|
||||
IGET_VOLATILE_JUMBO((short)0xfff3, "iget-volatile/jumbo", ReferenceType.field, Format.Format52c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
|
||||
IGET_WIDE_VOLATILE_JUMBO((short)0xfff4, "iget-wide-volatile/jumbo", ReferenceType.field, Format.Format52c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER | Opcode.JUMBO_OPCODE),
|
||||
IGET_OBJECT_VOLATILE_JUMBO((short)0xfff5, "iget-object-volatile/jumbo", ReferenceType.field, Format.Format52c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
|
||||
IPUT_VOLATILE_JUMBO((short)0xfff6, "iput-volatile/jumbo", ReferenceType.field, Format.Format52c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
|
||||
IPUT_WIDE_VOLATILE_JUMBO((short)0xfff7, "iput-wide-volatile/jumbo", ReferenceType.field, Format.Format52c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
|
||||
IPUT_OBJECT_VOLATILE_JUMBO((short)0xfff8, "iput-object-volatile/jumbo", ReferenceType.field, Format.Format52c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
|
||||
SGET_VOLATILE_JUMBO((short)0xfff9, "sget-volatile/jumbo", ReferenceType.field, Format.Format41c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
|
||||
SGET_WIDE_VOLATILE_JUMBO((short)0xfffa, "sget-wide-volatile/jumbo", ReferenceType.field, Format.Format41c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER | Opcode.JUMBO_OPCODE),
|
||||
SGET_OBJECT_VOLATILE_JUMBO((short)0xfffb, "sget-object-volatile/jumbo", ReferenceType.field, Format.Format41c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
|
||||
SPUT_VOLATILE_JUMBO((short)0xfffc, "sput-volatile/jumbo", ReferenceType.field, Format.Format41c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
|
||||
SPUT_WIDE_VOLATILE_JUMBO((short)0xfffd, "sput-wide-volatile/jumbo", ReferenceType.field, Format.Format41c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
|
||||
SPUT_OBJECT_VOLATILE_JUMBO((short)0xfffe, "sput-object-volatile/jumbo", ReferenceType.field, Format.Format41c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE);
|
||||
SPUT_OBJECT_VOLATILE((short)0xfe, "sput-object-volatile", ReferenceType.field, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE);
|
||||
|
||||
private static Opcode[] opcodesByValue;
|
||||
private static Opcode[] expandedOpcodesByValue;
|
||||
@ -426,7 +372,7 @@ public enum Opcode
|
||||
* based on the idiosyncrasies of that api level
|
||||
* @param apiLevel
|
||||
*/
|
||||
public static void updateMapsForApiLevel(int apiLevel, boolean includeJumbo) {
|
||||
public static void updateMapsForApiLevel(int apiLevel) {
|
||||
if (apiLevel < 5) {
|
||||
removeOpcodes(THROW_VERIFICATION_ERROR);
|
||||
}
|
||||
@ -445,21 +391,6 @@ public enum Opcode
|
||||
removeOpcodes(INVOKE_OBJECT_INIT_RANGE);
|
||||
addOpcodes(INVOKE_DIRECT_EMPTY);
|
||||
}
|
||||
if (apiLevel < 14 || !includeJumbo) {
|
||||
removeOpcodes(CONST_CLASS_JUMBO, CHECK_CAST_JUMBO, INSTANCE_OF_JUMBO, NEW_INSTANCE_JUMBO,
|
||||
NEW_ARRAY_JUMBO, FILLED_NEW_ARRAY_JUMBO, IGET_JUMBO, IGET_WIDE_JUMBO, IGET_OBJECT_JUMBO,
|
||||
IGET_BOOLEAN_JUMBO, IGET_BYTE_JUMBO, IGET_CHAR_JUMBO, IGET_SHORT_JUMBO, IPUT_JUMBO, IPUT_WIDE_JUMBO,
|
||||
IPUT_OBJECT_JUMBO, IPUT_BOOLEAN_JUMBO, IPUT_BYTE_JUMBO, IPUT_CHAR_JUMBO, IPUT_SHORT_JUMBO,
|
||||
SGET_JUMBO, SGET_WIDE_JUMBO, SGET_OBJECT_JUMBO, SGET_BOOLEAN_JUMBO, SGET_BYTE_JUMBO,
|
||||
SGET_CHAR_JUMBO, SGET_SHORT_JUMBO, SPUT_JUMBO, SPUT_WIDE_JUMBO, SPUT_OBJECT_JUMBO,
|
||||
SPUT_BOOLEAN_JUMBO, SPUT_BYTE_JUMBO, SPUT_CHAR_JUMBO, SPUT_SHORT_JUMBO, INVOKE_VIRTUAL_JUMBO,
|
||||
INVOKE_SUPER_JUMBO, INVOKE_DIRECT_JUMBO, INVOKE_STATIC_JUMBO, INVOKE_INTERFACE_JUMBO,
|
||||
INVOKE_OBJECT_INIT_JUMBO, IGET_VOLATILE_JUMBO, IGET_WIDE_VOLATILE_JUMBO,
|
||||
IGET_OBJECT_VOLATILE_JUMBO, IPUT_VOLATILE_JUMBO, IPUT_WIDE_VOLATILE_JUMBO,
|
||||
IPUT_OBJECT_VOLATILE_JUMBO, SGET_VOLATILE_JUMBO, SGET_WIDE_VOLATILE_JUMBO,
|
||||
SGET_OBJECT_VOLATILE_JUMBO, SPUT_VOLATILE_JUMBO, SPUT_WIDE_VOLATILE_JUMBO,
|
||||
SPUT_OBJECT_VOLATILE_JUMBO);
|
||||
}
|
||||
}
|
||||
|
||||
public final short value;
|
||||
|
@ -33,6 +33,8 @@ import org.jf.dexlib.Code.*;
|
||||
import org.jf.dexlib.Debug.DebugInstructionIterator;
|
||||
import org.jf.dexlib.Debug.DebugOpcode;
|
||||
import org.jf.dexlib.Util.*;
|
||||
import org.jf.util.AlignmentUtils;
|
||||
import org.jf.util.ExceptionWithContext;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -28,8 +28,10 @@
|
||||
|
||||
package org.jf.dexlib;
|
||||
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.jf.dexlib.Util.*;
|
||||
import org.jf.util.AlignmentUtils;
|
||||
import org.jf.util.ExceptionWithContext;
|
||||
import org.jf.util.Hex;
|
||||
|
||||
import java.io.*;
|
||||
import java.security.DigestException;
|
||||
@ -39,7 +41,8 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.zip.Adler32;
|
||||
import brut.directory.ZipExtFile;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
/**
|
||||
* <h3>Main use cases</h3>
|
||||
@ -287,13 +290,13 @@ public class DexFile
|
||||
|
||||
InputStream inputStream = null;
|
||||
Input in = null;
|
||||
ZipExtFile zipFile = null;
|
||||
ZipFile zipFile = null;
|
||||
|
||||
try {
|
||||
//do we have a zip file?
|
||||
if (magic[0] == 0x50 && magic[1] == 0x4B) {
|
||||
zipFile = new ZipExtFile(file);
|
||||
ZipArchiveEntry zipEntry = zipFile.getEntry("classes.dex");
|
||||
zipFile = new ZipFile(file);
|
||||
ZipEntry zipEntry = zipFile.getEntry("classes.dex");
|
||||
if (zipEntry == null) {
|
||||
throw new NoClassesDexException("zip file " + file.getName() + " does not contain a classes.dex " +
|
||||
"file");
|
||||
|
@ -33,7 +33,7 @@ import org.jf.dexlib.StringIdItem;
|
||||
import org.jf.dexlib.Util.AnnotatedOutput;
|
||||
import org.jf.dexlib.Util.EncodedValueUtils;
|
||||
import org.jf.dexlib.Util.Input;
|
||||
import org.jf.dexlib.Util.Utf8Utils;
|
||||
import org.jf.util.StringUtils;
|
||||
|
||||
public class StringEncodedValue extends EncodedValue {
|
||||
public final StringIdItem value;
|
||||
@ -65,7 +65,7 @@ public class StringEncodedValue extends EncodedValue {
|
||||
|
||||
if (out.annotates()) {
|
||||
out.annotate(1, "value_type=" + ValueType.VALUE_STRING.name() + ",value_arg=" + (bytes.length - 1));
|
||||
out.annotate(bytes.length, "value: \"" + Utf8Utils.escapeString(value.getStringValue()) + "\"");
|
||||
out.annotate(bytes.length, "value: \"" + StringUtils.escapeString(value.getStringValue()) + "\"");
|
||||
}
|
||||
|
||||
out.writeByte(ValueType.VALUE_STRING.value | ((bytes.length - 1) << 5));
|
||||
|
@ -31,7 +31,7 @@ package org.jf.dexlib;
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.jf.dexlib.Util.AnnotatedOutput;
|
||||
import org.jf.dexlib.Util.Input;
|
||||
import org.jf.dexlib.Util.Utf8Utils;
|
||||
import org.jf.util.StringUtils;
|
||||
|
||||
public class HeaderItem extends Item<HeaderItem> {
|
||||
/**
|
||||
@ -182,7 +182,7 @@ public class HeaderItem extends Item<HeaderItem> {
|
||||
magicBuilder.append((char)MAGIC_VALUES[magic_index][i]);
|
||||
}
|
||||
|
||||
out.annotate("magic: " + Utf8Utils.escapeString(magicBuilder.toString()));
|
||||
out.annotate("magic: " + StringUtils.escapeString(magicBuilder.toString()));
|
||||
out.write(MAGIC_VALUES[magic_index]);
|
||||
|
||||
out.annotate("checksum");
|
||||
|
@ -28,7 +28,7 @@
|
||||
|
||||
package org.jf.dexlib;
|
||||
|
||||
import org.jf.dexlib.Util.ExceptionWithContext;
|
||||
import org.jf.util.ExceptionWithContext;
|
||||
import org.jf.dexlib.Util.Input;
|
||||
|
||||
public class IndexedSection<T extends Item> extends Section<T> {
|
||||
|
@ -29,9 +29,9 @@
|
||||
package org.jf.dexlib;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.jf.dexlib.Util.AlignmentUtils;
|
||||
import org.jf.util.AlignmentUtils;
|
||||
import org.jf.dexlib.Util.AnnotatedOutput;
|
||||
import org.jf.dexlib.Util.ExceptionWithContext;
|
||||
import org.jf.util.ExceptionWithContext;
|
||||
import org.jf.dexlib.Util.Input;
|
||||
|
||||
public abstract class Item<T extends Item> implements Comparable<T> {
|
||||
|
@ -28,7 +28,7 @@
|
||||
|
||||
package org.jf.dexlib;
|
||||
|
||||
import org.jf.dexlib.Util.ExceptionWithContext;
|
||||
import org.jf.util.ExceptionWithContext;
|
||||
import org.jf.dexlib.Util.SparseArray;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -28,7 +28,7 @@
|
||||
|
||||
package org.jf.dexlib;
|
||||
|
||||
import org.jf.dexlib.Util.AlignmentUtils;
|
||||
import org.jf.util.AlignmentUtils;
|
||||
import org.jf.dexlib.Util.AnnotatedOutput;
|
||||
import org.jf.dexlib.Util.Input;
|
||||
|
||||
|
@ -31,7 +31,8 @@ package org.jf.dexlib;
|
||||
import org.jf.dexlib.Util.AnnotatedOutput;
|
||||
import org.jf.dexlib.Util.Input;
|
||||
import org.jf.dexlib.Util.Leb128Utils;
|
||||
import org.jf.dexlib.Util.Utf8Utils;
|
||||
import org.jf.util.StringUtils;
|
||||
import org.jf.util.Utf8Utils;
|
||||
|
||||
public class StringDataItem extends Item<StringDataItem> {
|
||||
private int hashCode = 0;
|
||||
@ -103,7 +104,7 @@ public class StringDataItem extends Item<StringDataItem> {
|
||||
")");
|
||||
out.writeUnsignedLeb128(stringValue.length());
|
||||
|
||||
out.annotate(encodedValue.length + 1, "string_data: \"" + Utf8Utils.escapeString(stringValue) + "\"");
|
||||
out.annotate(encodedValue.length + 1, "string_data: \"" + StringUtils.escapeString(stringValue) + "\"");
|
||||
} else {
|
||||
out.writeUnsignedLeb128(stringValue.length());
|
||||
}
|
||||
@ -118,7 +119,7 @@ public class StringDataItem extends Item<StringDataItem> {
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public String getConciseIdentity() {
|
||||
return "string_data_item: \"" + Utf8Utils.escapeString(getStringValue()) + "\"";
|
||||
return "string_data_item: \"" + StringUtils.escapeString(getStringValue()) + "\"";
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user