Merge branch 'wip-2.0'

Conflicts:
	brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfigFlags.java
This commit is contained in:
Connor Tumbleson 2014-04-13 20:30:09 -05:00
commit ab60872bbf
869 changed files with 63973 additions and 37419 deletions

2
.gitignore vendored
View File

@ -25,3 +25,5 @@ build/
*.setting
bin/
*.iml
.idea/*
/out

44
CHANGES
View File

@ -1,11 +1,47 @@
v1.5.3 (TBA)
-Updated to smali/baksmali to v1.4.2
v2.0.0 (TBA)
-Updated to smali/baksmali to v2.0.3
-Updated to Gradle 1.8
-Fixed (issue #8) - Correctly uses -c to retain original manifest and META-INF. (Thanks M1cha)
-Fixed (issue #63) - Correctly handles apk's that have unknown files outside of the standard aapt allowed resources.
-Fixed (issue #202) - Includes modified aapt to force package id on build. (Thanks M1cha)
-Fixed (issue #403) - Uses new usage output to cleanup organization of features.
-Fixed (issue #359) - Correctly handles malformed 9patch images. (Thanks Felipe Richards)
-Fixed (issue #401) - Uses versionInfo meta to correctly parse versionName and versionCode.
-Fixed (issue #440) - Include aapt binaries within Apktool to have closer control over build.
-Fixed (issue #439) - Correctly handles apk's that have have the general access bit enabled for encryption.
-Fixed (issue #339) - Re-enables debug mode ( -d flag) to fix smali debugging. (Thanks Ryszard)
-Fixed (issue #177) - Adapted output of smali to make breakpoint setting easier in different IDEs. (Thanks Ryszard)
-Fixed (issue #391) - Fixes characters (& and <) from being double escaped in <item>'s of arrays.xml
-Fixed (issue #260) - Fixes "Multiple substitution" errors with positional and exactly 1 non-positional argument.
-Fixed (issue #427) - Correctly handles `--frame-path` on [b]uild
-Fixed (issue #396) - Correctly handle android:debuggable while in debug mode.
-Fixed (issue #340) - Fixed superclass errors on debug mode.
-Updated to Gradle 1.4
-Fixed (issue #458) - Fixed pkg id not being correctly set in framework files.
-Fixed (issue #469) - Added (-m / --match-original)
-Fixed (issue #326) - Fixed PNG increasing brightness on build (Thanks Christiaan)
-Fixed (issue #448) - Merge smali2 into Apktool
-Fixed (issue #496) - Fixes Windows builds caused by java.nio problems
-Fixed (issue #510) - Any error output is sent stderr instead of stdout
-Fixed (issue #426) - Filename too long (JesusFreke)
-Fixed (issue #524) - INSTALL_FAILED_DEXOPT fix (JesusFreke)
-Fixed (issue #473) - multiple package frameworks are treated correctly.
-Fixed (issue #531) - JAR disassembling borking is fixed
-Fixed (issue #550) - Correctly labels incorrect type handling of <array>
-Fixed (issue #571) - Fixed truncated strings (Thanks jtmuhone)
-Fixed (issue #578) - Fixed apk's with multiple empty types via ignoring them
-Fixed (issue #589) - Fixed apk's with one package named "android" from improper decoding.
-Fixed (issue #601) - Make StringBlock thread safe (Thanks aluedeke)
-Fixed (issue #238) - Fixed truncated UTF-16 strings
-Fixed (issue #584) - Fixed horrible spacing, aligned for 4 spaces.
-Fixed (issue #196) - Fixed style crash due to malformed styles.
-Fixed (issue #603) - Fixed unknown files prefixed with common name from being ignored.
-Fixed (issue #606) - Fixed unknown files being ignored when -r is used.
-Fixed (issue #609) - Fixed handling renamed manifests with ("android", "com.htc" and "miui").
-Fixed issue with non-URI standard characters in apk name (Thanks rover12421)
-Added output to list Apktool version to help debugging.
-Updated known bytes for configurations to 38 (from addition of layout direction)
-Fixed NPE when handling odex apks even with --no-src specified. (Thanks Rodrigo Chiossi)
-Fixed (issue #427) - Correctly handles `--frame-path` on [b]uild
-Fixed locale problems when locale changes meaning of default letters in windows .bat script (Thanks Adem666)
v1.5.2 (Released February 2 - 2013) Codename: Bug Fixes
-Fixed (issue #299) - output smali filename errors to screen during rebuild instead of filestream

22
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,22 @@
# Contributing
A couple of quick tips to ease the submission process.
* You may use [Github](https://github.com/iBotPeaches/Apktool), [GoogleCode](http://code.google.com/p/android-apktool/source/list),
or [Bitbucket](https://bitbucket.org/iBotPeaches/apktool/) to submit a pull request.
* Please reference the bug number from our [issue list](https://code.google.com/p/android-apktool/issues/list) in any pull requests to help associate fixes with bugs.
* If possible, add unit-tests for any bugs that you fix.
* [Building](http://code.google.com/p/android-apktool/wiki/BuildApktool) via Gradle will automatically run unit-tests. The build will end if any test fails.
* [IntelliJ IDEA](http://www.jetbrains.com/idea/) is our IDE of choice. It has built in debugger support along with Gradle integration.
* For changes to smali/baksmali please see their [page](http://code.google.com/p/smali/) for more information.
## Code Styles
* A rough guideline based on [AOSP Guidelines](https://source.android.com/source/code-style.html).
* A tab counts as 4 spaces and we use 4 spaces.
* Our right margin is 120 characters long.

View File

@ -1,11 +1,19 @@
# Apktool #
http://code.google.com/p/android-apktool/
### Apktool
It is a tool for reverse engineering 3rd party, closed, binary Android apps. It can decode resources to nearly original form and rebuild them after making some modifications; it makes possible to debug smali code step by step. Also it makes working with app easier because of project-like files structure and automation of some repetitive tasks like building apk, etc.
Links
- Wiki http://code.google.com/p/android-apktool/w/list
- Bug Reports http://code.google.com/p/android-apktool/issues/list
- Changelog/Information http://code.google.com/p/android-apktool/wiki/Changelog
- XDA Post http://forum.xda-developers.com/showthread.php?p=28366939
- Source (Github) https://github.com/iBotPeaches/Apktool
- Source (GoogleCode) http://code.google.com/p/android-apktool/source/list
It is NOT intended for piracy and other non-legal uses. It could be used for localizing, adding some features or support for custom platforms and other GOOD purposes. Just try to be fair with authors of an app, that you use and probably like.
#### Support
- [Project Page](http://code.google.com/p/android-apktool/)
- [#apktool on freenode](http://webchat.freenode.net/?channels=apktool)
#### Links
- [How to Build](https://code.google.com/p/android-apktool/wiki/BuildApktool)
- [Wiki](http://code.google.com/p/android-apktool/w/list)
- [Bug Reports](http://code.google.com/p/android-apktool/issues/list)
- [Changelog/Information](http://code.google.com/p/android-apktool/wiki/Changelog)
- [XDA Post](http://forum.xda-developers.com/showthread.php?p=28366939)
- [Source (Github)](https://github.com/iBotPeaches/Apktool)
- [Source (GoogleCode)](http://code.google.com/p/android-apktool/source/list)
- [Source (Bitbucket)](https://bitbucket.org/iBotPeaches/apktool/)

View File

@ -1 +0,0 @@
/target

View File

@ -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(':brut.apktool.smali: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)

View File

@ -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");
}

View File

@ -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);

View File

@ -28,81 +28,77 @@
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.dexbacked.DexBackedDexFile.InvalidItemIndex;
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 = null;
try {
fieldRef = (FieldReference)ins.getReference();
} catch (InvalidItemIndex ex) {
// just ignore it for now. We'll deal with it later, when processing the instructions
// themselves
}
if (fieldRef != null &&
fieldRef.getDefiningClass().equals((classDef.getType()))) {
fieldsSetInStaticConstructor.add(ReferenceUtil.getShortFieldDescriptor(fieldRef));
}
break;
}
}
}
}
}
}
return fieldsSetInStaticConstructor;
}
public void writeTo(IndentingWriter writer) throws IOException {
@ -111,238 +107,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, options);
} 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, options);
} else {
MethodDefinition methodDefinition = new MethodDefinition(this, method, methodImpl);
methodDefinition.writeTo(methodWriter);
}
}
}

View File

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

View File

@ -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;
}
}

View File

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

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

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

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

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

View File

@ -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);

View File

@ -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");
}

View File

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

View File

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

View File

@ -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;
}

View File

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

View File

@ -28,36 +28,46 @@
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);
if (elementWidth == 4)
writeResourceId(writer, number.intValue());
writer.write("\n");
}
writer.deindent(4);
writer.write(".end array-data");

View File

@ -28,26 +28,35 @@
package org.jf.baksmali.Adaptors.Format;
import org.jf.baksmali.Adaptors.MethodDefinition;
import org.jf.baksmali.Adaptors.MethodDefinition.InvalidSwitchPayload;
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.baksmali.baksmaliOptions;
import org.jf.dexlib2.Opcode;
import org.jf.dexlib2.ReferenceType;
import org.jf.dexlib2.VerificationError;
import org.jf.dexlib2.dexbacked.DexBackedDexFile.InvalidItemIndex;
import org.jf.dexlib2.iface.instruction.*;
import org.jf.dexlib2.iface.instruction.formats.Instruction20bc;
import org.jf.dexlib2.iface.instruction.formats.Instruction31t;
import org.jf.dexlib2.iface.instruction.formats.UnknownInstruction;
import org.jf.dexlib2.iface.reference.Reference;
import org.jf.dexlib2.util.ReferenceUtil;
import org.jf.util.ExceptionWithContext;
import org.jf.util.IndentingWriter;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.Map;
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;
}
@ -56,64 +65,155 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
return 100;
}
private boolean isAllowedOdex(@Nonnull Opcode opcode) {
baksmaliOptions options = methodDef.classDef.options;
if (options.allowOdex) {
return true;
}
if (methodDef.classDef.options.apiLevel >= 14) {
return false;
}
return opcode.isOdexedInstanceVolatile() || opcode.isOdexedStaticVolatile() ||
opcode == Opcode.THROW_VERIFICATION_ERROR;
}
@Override
public boolean writeTo(IndentingWriter writer) throws IOException {
switch (instruction.getFormat()) {
Opcode opcode = instruction.getOpcode();
String verificationErrorName = null;
String referenceString = null;
boolean commentOutInstruction = false;
if (instruction instanceof Instruction20bc) {
int verificationError = ((Instruction20bc)instruction).getVerificationError();
verificationErrorName = VerificationError.getVerificationErrorName(verificationError);
if (verificationErrorName == null) {
writer.write("#was invalid verification error type: ");
writer.printSignedIntAsDec(verificationError);
writer.write("\n");
verificationErrorName = "generic-error";
}
}
if (instruction instanceof ReferenceInstruction) {
ReferenceInstruction referenceInstruction = (ReferenceInstruction)instruction;
try {
Reference reference = referenceInstruction.getReference();
referenceString = ReferenceUtil.getReferenceString(reference);
assert referenceString != null;
} catch (InvalidItemIndex ex) {
writer.write("#");
writer.write(ex.getMessage());
writer.write("\n");
commentOutInstruction = true;
referenceString = String.format("%s@%d",
ReferenceType.toString(referenceInstruction.getReferenceType()),
ex.getInvalidIndex());
} catch (ReferenceType.InvalidReferenceTypeException ex) {
writer.write("#invalid reference type: ");
writer.printSignedIntAsDec(ex.getReferenceType());
commentOutInstruction = true;
referenceString = "invalid_reference";
}
}
if (instruction instanceof Instruction31t) {
Opcode payloadOpcode;
switch (instruction.getOpcode()) {
case PACKED_SWITCH:
payloadOpcode = Opcode.PACKED_SWITCH_PAYLOAD;
break;
case SPARSE_SWITCH:
payloadOpcode = Opcode.SPARSE_SWITCH_PAYLOAD;
break;
case FILL_ARRAY_DATA:
payloadOpcode = Opcode.ARRAY_PAYLOAD;
break;
default:
throw new ExceptionWithContext("Invalid 31t opcode: %s", instruction.getOpcode());
}
try {
methodDef.findSwitchPayload(this.codeAddress + ((Instruction31t)instruction).getCodeOffset(),
payloadOpcode);
} catch (InvalidSwitchPayload ex) {
writer.write("#invalid payload reference");
commentOutInstruction = true;
}
}
if (opcode.odexOnly()) {
if (!isAllowedOdex(opcode)) {
writer.write("#disallowed odex opcode\n");
commentOutInstruction = true;
}
}
if (commentOutInstruction) {
writer.write("#");
}
switch (instruction.getOpcode().format) {
case Format10t:
writeOpcode(writer);
writer.write(' ');
writeTargetLabel(writer);
return true;
break;
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);
return true;
break;
case Format11n:
writeOpcode(writer);
writer.write(' ');
writeFirstRegister(writer);
writer.write(", ");
writeLiteral(writer);
return true;
break;
case Format11x:
writeOpcode(writer);
writer.write(' ');
writeFirstRegister(writer);
return true;
break;
case Format12x:
writeOpcode(writer);
writer.write(' ');
writeFirstRegister(writer);
writer.write(", ");
writeSecondRegister(writer);
return true;
break;
case Format20bc:
writeOpcode(writer);
writer.write(' ');
writeVerificationErrorType(writer);
writer.write(verificationErrorName);
writer.write(", ");
writeReference(writer);
return true;
writer.write(referenceString);
break;
case Format20t:
case Format30t:
writeOpcode(writer);
writer.write(' ');
writeTargetLabel(writer);
return true;
break;
case Format21c:
case Format31c:
case Format41c:
writeOpcode(writer);
writer.write(' ');
writeFirstRegister(writer);
writer.write(", ");
writeReference(writer);
return true;
case Format21h:
writer.write(referenceString);
break;
case Format21ih:
case Format21lh:
case Format21s:
case Format31i:
case Format51l:
@ -122,7 +222,9 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
writeFirstRegister(writer);
writer.write(", ");
writeLiteral(writer);
return true;
if (instruction.getOpcode().setsWideRegister() == false)
writeResourceId(writer);
break;
case Format21t:
case Format31t:
writeOpcode(writer);
@ -130,7 +232,7 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
writeFirstRegister(writer);
writer.write(", ");
writeTargetLabel(writer);
return true;
break;
case Format22b:
case Format22s:
writeOpcode(writer);
@ -140,17 +242,16 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
writeSecondRegister(writer);
writer.write(", ");
writeLiteral(writer);
return true;
break;
case Format22c:
case Format52c:
writeOpcode(writer);
writer.write(' ');
writeFirstRegister(writer);
writer.write(", ");
writeSecondRegister(writer);
writer.write(", ");
writeReference(writer);
return true;
writer.write(referenceString);
break;
case Format22cs:
writeOpcode(writer);
writer.write(' ');
@ -159,7 +260,7 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
writeSecondRegister(writer);
writer.write(", ");
writeFieldOffset(writer);
return true;
break;
case Format22t:
writeOpcode(writer);
writer.write(' ');
@ -168,7 +269,7 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
writeSecondRegister(writer);
writer.write(", ");
writeTargetLabel(writer);
return true;
break;
case Format22x:
case Format32x:
writeOpcode(writer);
@ -176,7 +277,7 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
writeFirstRegister(writer);
writer.write(", ");
writeSecondRegister(writer);
return true;
break;
case Format23x:
writeOpcode(writer);
writer.write(' ');
@ -185,71 +286,77 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
writeSecondRegister(writer);
writer.write(", ");
writeThirdRegister(writer);
return true;
break;
case Format35c:
writeOpcode(writer);
writer.write(' ');
writeInvokeRegisters(writer);
writer.write(", ");
writeReference(writer);
return true;
writer.write(referenceString);
break;
case Format35mi:
writeOpcode(writer);
writer.write(' ');
writeInvokeRegisters(writer);
writer.write(", ");
writeInlineIndex(writer);
return true;
break;
case Format35ms:
writeOpcode(writer);
writer.write(' ');
writeInvokeRegisters(writer);
writer.write(", ");
writeVtableIndex(writer);
return true;
break;
case Format3rc:
case Format5rc:
writeOpcode(writer);
writer.write(' ');
writeInvokeRangeRegisters(writer);
writer.write(", ");
writeReference(writer);
return true;
writer.write(referenceString);
break;
case Format3rmi:
writeOpcode(writer);
writer.write(' ');
writeInvokeRangeRegisters(writer);
writer.write(", ");
writeInlineIndex(writer);
return true;
break;
case Format3rms:
writeOpcode(writer);
writer.write(' ');
writeInvokeRangeRegisters(writer);
writer.write(", ");
writeVtableIndex(writer);
return true;
break;
default:
assert false;
return false;
}
assert false;
return false;
if (commentOutInstruction) {
writer.write("\nnop");
}
return true;
}
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 +364,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 +407,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 +415,44 @@ 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 writeResourceId(IndentingWriter writer) throws IOException {
writeResourceId(writer, ((NarrowLiteralInstruction)instruction).getNarrowLiteral());
}
protected void writeResourceId(IndentingWriter writer, int val) throws IOException {
Map<Integer,String> resourceIds = methodDef.classDef.options.resourceIds;
String resource = resourceIds.get(Integer.valueOf(val));
if (resource != null) {
writer.write(" # ");
writer.write(resource);
}
}
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());
}
protected void writeReference(IndentingWriter writer) throws IOException {
Item item = ((InstructionWithReference)instruction).getReferencedItem();
ReferenceFormatter.writeReference(writer, item);
}
protected void writeVerificationErrorType(IndentingWriter writer) throws IOException {
VerificationErrorType validationErrorType = ((Instruction20bc)instruction).getValidationErrorType();
writer.write(validationErrorType.getName());
writer.write("vtable@");
writer.printSignedIntAsDec(((VtableIndexInstruction)instruction).getVtableIndex());
}
}

View File

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

View File

@ -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_";
}

View File

@ -30,60 +30,64 @@ 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');
int key = firstKey;
for (PackedSwitchTarget target: targets) {
target.writeTargetTo(writer);
writeResourceId(writer, key);
writer.write('\n');
key++;
}
writer.deindent(4);
writer.write(".end packed-switch");
@ -95,19 +99,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);
}
}
}

View File

@ -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,9 +68,10 @@ 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);
writeResourceId(writer, target.getKey());
writer.write('\n');
}
writer.deindent(4);
@ -92,24 +80,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);
}
}
}

View File

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

View File

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

View File

@ -28,297 +28,282 @@
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.baksmali.baksmaliOptions;
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.dexbacked.DexBackedDexFile.InvalidItemIndex;
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.InstructionOffsetMap.InvalidInstructionOffset;
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);
for (int i=0; i<instructions.size(); i++) {
Instruction instruction = instructions.get(i);
Opcode opcode = instruction.getOpcode();
if (opcode == Opcode.PACKED_SWITCH) {
boolean valid = true;
int codeOffset = instructionOffsetMap.getInstructionCodeOffset(i);
int targetOffset = codeOffset + ((OffsetInstruction)instruction).getCodeOffset();
try {
targetOffset = findSwitchPayload(targetOffset, Opcode.PACKED_SWITCH_PAYLOAD);
} catch (InvalidSwitchPayload ex) {
valid = false;
}
if (valid) {
packedSwitchMap.append(targetOffset, codeOffset);
}
} else if (opcode == Opcode.SPARSE_SWITCH) {
boolean valid = true;
int codeOffset = instructionOffsetMap.getInstructionCodeOffset(i);
int targetOffset = codeOffset + ((OffsetInstruction)instruction).getCodeOffset();
try {
targetOffset = findSwitchPayload(targetOffset, Opcode.SPARSE_SWITCH_PAYLOAD);
} catch (InvalidSwitchPayload ex) {
valid = false;
// The offset to the payload instruction was invalid. Nothing to do, except that we won't
// add this instruction to the map.
}
if (valid) {
sparseSwitchMap.append(targetOffset, codeOffset);
}
instructionMap.append(currentCodeAddress, i);
currentCodeAddress += instruction.getSize(currentCodeAddress);
}
} 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,
baksmaliOptions options) 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, options);
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, classDef.options);
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++;
}
return totalRegisters - parameterRegisters;
public int findSwitchPayload(int targetOffset, Opcode type) {
int targetIndex;
try {
targetIndex = instructionOffsetMap.getInstructionIndexAtCodeOffset(targetOffset);
} catch (InvalidInstructionOffset ex) {
throw new InvalidSwitchPayload(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);
}
}
}
throw new InvalidSwitchPayload(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,
baksmaliOptions options) 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 && options.outputDebugInfo) {
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 +316,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 +350,28 @@ 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 = null;
try {
methodReference = (MethodReference)((ReferenceInstruction)instruction).getReference();
} catch (InvalidItemIndex ex) {
// just ignore it for now. We'll deal with it later, when processing the instructions
// themselves
}
if (methodReference != null &&
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 +379,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 +428,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 +478,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() {
@ -659,4 +539,17 @@ public class MethodDefinition {
return labels.values();
}
}
public static class InvalidSwitchPayload extends ExceptionWithContext {
private final int payloadOffset;
public InvalidSwitchPayload(int payloadOffset) {
super("No switch payload at offset: %d", payloadOffset);
this.payloadOffset = payloadOffset;
}
public int getPayloadOffset() {
return payloadOffset;
}
}
}

View File

@ -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;

View File

@ -28,30 +28,35 @@
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;
public PreInstructionRegisterInfoMethodItem(AnalyzedInstruction analyzedInstruction, MethodAnalyzer methodAnalyzer,
private final int registerInfo;
@Nonnull private final MethodAnalyzer methodAnalyzer;
@Nonnull private final RegisterFormatter registerFormatter;
@Nonnull private final AnalyzedInstruction analyzedInstruction;
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 +66,47 @@ 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 & baksmaliOptions.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 +114,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 +143,26 @@ 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) {
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 +174,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;
}

View File

@ -28,52 +28,38 @@
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);
case ReferenceType.METHOD:
ReferenceUtil.writeMethodDescriptor(writer, (MethodReference)reference);
return;
case TYPE_TYPE_ID_ITEM:
writeTypeReference(writer, (TypeIdItem)item);
case ReferenceType.FIELD:
ReferenceUtil.writeFieldDescriptor(writer, (FieldReference)reference);
return;
default:
throw new IllegalStateException("Unknown reference type");
}
}
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());
}
}

View File

@ -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)));

View File

@ -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;
}
}

View File

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

View File

@ -28,188 +28,214 @@
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 org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
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.Map.Entry;
import java.util.concurrent.*;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.ParserConfigurationException;
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.");
System.err.println("\n\nError occurred while loading boot class path files. Aborting.");
ex.printStackTrace(System.err);
System.exit(1);
return false;
}
}
File outputDirectoryFile = new File(outputDirectory);
if (options.resourceIdFileEntries != null) {
class PublicHandler extends DefaultHandler {
String prefix = null;
public PublicHandler(String prefix) {
super();
this.prefix = prefix;
}
public void startElement(String uri, String localName,
String qName, Attributes attr) throws SAXException {
if (qName.equals("public")) {
String type = attr.getValue("type");
String name = attr.getValue("name").replace('.', '_');
Integer public_key = Integer.decode(attr.getValue("id"));
String public_val = new StringBuffer()
.append(prefix)
.append(".")
.append(type)
.append(".")
.append(name)
.toString();
options.resourceIds.put(public_key, public_val);
}
}
};
for (Entry<String,String> entry: options.resourceIdFileEntries.entrySet()) {
try {
SAXParser saxp = SAXParserFactory.newInstance().newSAXParser();
String prefix = entry.getValue();
saxp.parse(entry.getKey(), new PublicHandler(prefix));
} catch (ParserConfigurationException e) {
continue;
} catch (SAXException e) {
continue;
} catch (IOException e) {
continue;
}
}
}
File outputDirectoryFile = new File(options.outputDirectory);
if (!outputDirectoryFile.exists()) {
if (!outputDirectoryFile.mkdirs()) {
System.err.println("Can't create the output directory " + outputDirectory);
System.exit(1);
System.err.println("Can't create the output directory " + options.outputDirectory);
return false;
}
}
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;
}
}
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) {
boolean errorOccurred = false;
try {
for (Future<Boolean> task: tasks) {
while(true) {
try {
writer.close();
} catch (Throwable ex) {
System.err.println("\n\nError occured while closing file " + smaliFile.toString());
ex.printStackTrace();
if (!task.get()) {
errorOccurred = true;
}
} catch (InterruptedException ex) {
continue;
} catch (ExecutionException ex) {
throw new RuntimeException(ex);
}
break;
}
}
} finally {
executor.shutdown();
}
return !errorOccurred;
}
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 (!ignoreErrors && classDefinition.hadValidationErrors()) {
System.exit(1);
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 occurred 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 occurred while closing file " + smaliFile.toString());
ex.printStackTrace();
}
}
}
}
private static final Pattern extJarPattern = Pattern.compile("(?:^|\\\\|/)ext.(?:jar|odex)$");
private static boolean isExtJar(String dexFilePath) {
Matcher m = extJarPattern.matcher(dexFilePath);
return m.find();
return true;
}
}

View File

@ -0,0 +1,100 @@
/*
* 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.HashMap;
import java.util.List;
import java.util.Map;
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 static final int DIFFPRE = 128;
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 Map<String,String> resourceIdFileEntries = new HashMap<String,String>();
public Map<Integer,String> resourceIds = new HashMap<Integer,String>();
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 allowOdex = 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(":")));
}
public void setResourceIdFiles(String resourceIdFiles) {
for (String resourceIdFile: resourceIdFiles.split(":")) {
String[] entry = resourceIdFile.split("=");
resourceIdFileEntries.put(entry[1], entry[0]);
}
}
}

View File

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

View File

@ -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,97 @@ 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 'i':
String rif = commandLine.getOptionValue("i");
options.setResourceIdFiles(rif);
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");
break;
case 'K':
checkPackagePrivateAccess = true;
options.inlineResolver = new CustomInlineMethodResolver(options.classPath, new File(commandLine.getOptionValue("T")));
break;
default:
assert false;
@ -267,68 +229,65 @@ 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);
if (options.apiLevel >= 17) {
options.checkPackagePrivateAccess = true;
}
//Read in and parse the dex file
DexFile dexFile = new DexFile(dexFileFile, !fixRegisters, false);
String inputDexFileName = remainingArgs[0];
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");
options.allowOdex = true;
}
} 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 +322,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 +401,26 @@ 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 resourceIdFilesOption = OptionBuilder.withLongOpt("resource-id-files")
.withDescription("the resource ID files to use, for analysis. A colon-separated list of prefix=file " +
"pairs. For example R=res/values/public.xml:" +
"android.R=$ANDROID_HOME/platforms/android-19/data/res/values/public.xml")
.hasArg()
.withArgName("FILES")
.create("i");
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,36 +434,10 @@ 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()
@ -509,15 +458,12 @@ public class main {
basicOptions.addOption(codeOffsetOption);
basicOptions.addOption(noAccessorCommentsOption);
basicOptions.addOption(apiLevelOption);
basicOptions.addOption(jobsOption);
basicOptions.addOption(resourceIdFilesOption);
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);
for (Object option: basicOptions.getOptions()) {
@ -527,4 +473,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");
}
}
}

View File

@ -0,0 +1 @@
application.version=${version}

View File

@ -1 +0,0 @@
application.version=1.4.1

View File

@ -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>
>>

View File

@ -0,0 +1,124 @@
/*
* 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);
String newline = System.getProperty("line.separator");
Assert.assertEquals(smaliContents.replace("\r", "").replace("\n", newline),
stringWriter.toString().replace("\r", "").replace("\n", newline));
}
}
@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);
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,105 @@
/*
* 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.
*/
apply plugin: 'idea'
version = '2.0.3'
def jarVersion = version
if (!('release' in gradle.startParameter.taskNames)) {
def versionSuffix
try {
def git = org.eclipse.jgit.api.Git.open(file('../.'))
def head = git.getRepository().getRef("HEAD")
versionSuffix = head.getObjectId().abbreviate(8).name()
if (!git.status().call().clean) {
versionSuffix += '-dirty'
}
} catch (Exception) {
// In case we can't get the commit for some reason,
// just use -dev
versionSuffix = 'dev'
}
def baseVersion = version
version = baseVersion + '-' + versionSuffix
// use something like module-1.2.3-dev.jar for the jar name, rather than the full
// module-1.2.3-001afe02-dirty.jar
jarVersion = baseVersion + '-dev'
}
subprojects {
apply plugin: 'java'
apply plugin: 'idea'
version = parent.version
ext {
depends = [guava: 'com.google.guava:guava:14.0',
findbugs: 'com.google.code.findbugs:jsr305:1.3.9',
junit: 'junit:junit:4.6',
antlr_runtime: 'org.antlr:antlr-runtime:3.5',
antlr: 'org.antlr:antlr:3.5',
commons_cli: 'commons-cli:commons-cli:1.2',
jflex: 'de.jflex:jflex:1.4.3',
proguard: 'net.sf.proguard:proguard-base:4.8'
]
}
jar {
version = jarVersion
}
repositories {
mavenCentral()
}
}
// Note: please don't use this. This is strictly for the official releases
// that are posted on the googlecode download page.
task release {
}
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.eclipse.jgit:org.eclipse.jgit:2.0.0.201206130900-r'
}
}
task wrapper(type: Wrapper) {
gradleVersion = '1.8'
}

View File

@ -1 +0,0 @@
/target

View File

@ -1,610 +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.dexlib;
import com.google.common.base.Preconditions;
import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.Util.ExceptionWithContext;
import org.jf.dexlib.Util.Input;
import org.jf.dexlib.Util.ReadOnlyArrayList;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
public class AnnotationDirectoryItem extends Item<AnnotationDirectoryItem> {
@Nullable
private AnnotationSetItem classAnnotations;
@Nullable
private FieldAnnotation[] fieldAnnotations;
@Nullable
private MethodAnnotation[] methodAnnotations;
@Nullable
private ParameterAnnotation[] parameterAnnotations;
/**
* Creates a new uninitialized <code>AnnotationDirectoryItem</code>
* @param dexFile The <code>DexFile</code> that this item belongs to
*/
protected AnnotationDirectoryItem(DexFile dexFile) {
super(dexFile);
}
/**
* Creates a new <code>AnnotationDirectoryItem</code> with the given values
* @param dexFile The <code>DexFile</code> that this item belongs to
* @param classAnnotations The annotations associated with the overall class
* @param fieldAnnotations A list of <code>FieldAnnotation</code> objects that contain the field annotations for
* this class
* @param methodAnnotations A list of <code>MethodAnnotation</code> objects that contain the method annotations for
* this class
* @param parameterAnnotations A list of <code>ParameterAnnotation</code> objects that contain the parameter
* annotations for the methods in this class
*/
private AnnotationDirectoryItem(DexFile dexFile, @Nullable AnnotationSetItem classAnnotations,
@Nullable List<FieldAnnotation> fieldAnnotations,
@Nullable List<MethodAnnotation> methodAnnotations,
@Nullable List<ParameterAnnotation> parameterAnnotations) {
super(dexFile);
this.classAnnotations = classAnnotations;
if (fieldAnnotations == null || fieldAnnotations.size() == 0) {
this.fieldAnnotations = null;
} else {
this.fieldAnnotations = new FieldAnnotation[fieldAnnotations.size()];
this.fieldAnnotations = fieldAnnotations.toArray(this.fieldAnnotations);
Arrays.sort(this.fieldAnnotations);
}
if (methodAnnotations == null || methodAnnotations.size() == 0) {
this.methodAnnotations = null;
} else {
this.methodAnnotations = new MethodAnnotation[methodAnnotations.size()];
this.methodAnnotations = methodAnnotations.toArray(this.methodAnnotations);
Arrays.sort(this.methodAnnotations);
}
if (parameterAnnotations == null || parameterAnnotations.size() == 0) {
this.parameterAnnotations = null;
} else {
this.parameterAnnotations = new ParameterAnnotation[parameterAnnotations.size()];
this.parameterAnnotations = parameterAnnotations.toArray(this.parameterAnnotations);
Arrays.sort(this.parameterAnnotations);
}
}
/**
* Returns an <code>AnnotationDirectoryItem</code> for the given values, and that has been interned into the given
* <code>DexFile</code>
* @param dexFile The <code>DexFile</code> that this item belongs to
* @param classAnnotations The annotations associated with the class
* @param fieldAnnotations A list of <code>FieldAnnotation</code> objects containing the field annotations
* @param methodAnnotations A list of <code>MethodAnnotation</code> objects containing the method annotations
* @param parameterAnnotations A list of <code>ParameterAnnotation</code> objects containin the parameter
* annotations
* @return an <code>AnnotationItem</code> for the given values, and that has been interned into the given
* <code>DexFile</code>
*/
public static AnnotationDirectoryItem internAnnotationDirectoryItem(DexFile dexFile,
AnnotationSetItem classAnnotations,
List<FieldAnnotation> fieldAnnotations,
List<MethodAnnotation> methodAnnotations,
List<ParameterAnnotation> parameterAnnotations) {
AnnotationDirectoryItem annotationDirectoryItem = new AnnotationDirectoryItem(dexFile, classAnnotations,
fieldAnnotations, methodAnnotations, parameterAnnotations);
return dexFile.AnnotationDirectoriesSection.intern(annotationDirectoryItem);
}
/** {@inheritDoc} */
protected void readItem(Input in, ReadContext readContext) {
classAnnotations = (AnnotationSetItem)readContext.getOptionalOffsettedItemByOffset(
ItemType.TYPE_ANNOTATION_SET_ITEM, in.readInt());
int fieldAnnotationCount = in.readInt();
if (fieldAnnotationCount > 0) {
fieldAnnotations = new FieldAnnotation[fieldAnnotationCount];
} else {
fieldAnnotations = null;
}
int methodAnnotationCount = in.readInt();
if (methodAnnotationCount > 0) {
methodAnnotations = new MethodAnnotation[methodAnnotationCount];
} else {
methodAnnotations = null;
}
int parameterAnnotationCount = in.readInt();
if (parameterAnnotationCount > 0) {
parameterAnnotations = new ParameterAnnotation[parameterAnnotationCount];
} else {
parameterAnnotations = null;
}
if (fieldAnnotations != null) {
for (int i=0; i<fieldAnnotations.length; i++) {
try {
FieldIdItem fieldIdItem = dexFile.FieldIdsSection.getItemByIndex(in.readInt());
AnnotationSetItem fieldAnnotationSet = (AnnotationSetItem)readContext.getOffsettedItemByOffset(
ItemType.TYPE_ANNOTATION_SET_ITEM, in.readInt());
fieldAnnotations[i] = new FieldAnnotation(fieldIdItem, fieldAnnotationSet);
} catch (Exception ex) {
throw ExceptionWithContext.withContext(ex,
"Error occured while reading FieldAnnotation at index " + i);
}
}
}
if (methodAnnotations != null) {
for (int i=0; i<methodAnnotations.length; i++) {
try {
MethodIdItem methodIdItem = dexFile.MethodIdsSection.getItemByIndex(in.readInt());
AnnotationSetItem methodAnnotationSet = (AnnotationSetItem)readContext.getOffsettedItemByOffset(
ItemType.TYPE_ANNOTATION_SET_ITEM, in.readInt());
methodAnnotations[i] = new MethodAnnotation(methodIdItem, methodAnnotationSet);
} catch (Exception ex) {
throw ExceptionWithContext.withContext(ex,
"Error occured while reading MethodAnnotation at index " + i);
}
}
}
if (parameterAnnotations != null) {
for (int i=0; i<parameterAnnotations.length; i++) {
try {
MethodIdItem methodIdItem = dexFile.MethodIdsSection.getItemByIndex(in.readInt());
AnnotationSetRefList paramaterAnnotationSet = (AnnotationSetRefList)readContext.getOffsettedItemByOffset(
ItemType.TYPE_ANNOTATION_SET_REF_LIST, in.readInt());
parameterAnnotations[i] = new ParameterAnnotation(methodIdItem, paramaterAnnotationSet);
} catch (Exception ex) {
throw ExceptionWithContext.withContext(ex,
"Error occured while reading ParameterAnnotation at index " + i);
}
}
}
}
/** {@inheritDoc} */
protected int placeItem(int offset) {
return offset + 16 + (
(fieldAnnotations==null?0:fieldAnnotations.length) +
(methodAnnotations==null?0:methodAnnotations.length) +
(parameterAnnotations==null?0:parameterAnnotations.length)) * 8;
}
/** {@inheritDoc} */
protected void writeItem(AnnotatedOutput out) {
if (out.annotates()) {
TypeIdItem parentType = getParentType();
if (parentType != null) {
out.annotate(0, parentType.getTypeDescriptor());
}
if (classAnnotations != null) {
out.annotate(4, "class_annotations_off: 0x" + Integer.toHexString(classAnnotations.getOffset()));
} else {
out.annotate(4, "class_annotations_off:");
}
int length = fieldAnnotations==null?0:fieldAnnotations.length;
out.annotate(4, "annotated_fields_size: 0x" + Integer.toHexString(length) + " (" +
length + ")");
length = methodAnnotations==null?0:methodAnnotations.length;
out.annotate(4, "annotated_methods_size: 0x" + Integer.toHexString(length) + " (" +
length + ")");
length = parameterAnnotations==null?0:parameterAnnotations.length;
out.annotate(4, "annotated_parameters_size: 0x" + Integer.toHexString(length) + " (" +
length + ")");
int index;
if (fieldAnnotations != null) {
index = 0;
for (FieldAnnotation fieldAnnotation: fieldAnnotations) {
out.annotate(0, "[" + index++ + "] field_annotation");
out.indent();
out.annotate(4, "field: " + fieldAnnotation.field.getFieldName().getStringValue() + ":" +
fieldAnnotation.field.getFieldType().getTypeDescriptor());
out.annotate(4, "annotations_off: 0x" +
Integer.toHexString(fieldAnnotation.annotationSet.getOffset()));
out.deindent();
}
}
if (methodAnnotations != null) {
index = 0;
for (MethodAnnotation methodAnnotation: methodAnnotations) {
out.annotate(0, "[" + index++ + "] method_annotation");
out.indent();
out.annotate(4, "method: " + methodAnnotation.method.getMethodString());
out.annotate(4, "annotations_off: 0x" +
Integer.toHexString(methodAnnotation.annotationSet.getOffset()));
out.deindent();
}
}
if (parameterAnnotations != null) {
index = 0;
for (ParameterAnnotation parameterAnnotation: parameterAnnotations) {
out.annotate(0, "[" + index++ + "] parameter_annotation");
out.indent();
out.annotate(4, "method: " + parameterAnnotation.method.getMethodString());
out.annotate(4, "annotations_off: 0x" +
Integer.toHexString(parameterAnnotation.annotationSet.getOffset()));
}
}
}
out.writeInt(classAnnotations==null?0:classAnnotations.getOffset());
out.writeInt(fieldAnnotations==null?0:fieldAnnotations.length);
out.writeInt(methodAnnotations==null?0:methodAnnotations.length);
out.writeInt(parameterAnnotations==null?0:parameterAnnotations.length);
if (fieldAnnotations != null) {
for (FieldAnnotation fieldAnnotation: fieldAnnotations) {
out.writeInt(fieldAnnotation.field.getIndex());
out.writeInt(fieldAnnotation.annotationSet.getOffset());
}
}
if (methodAnnotations != null) {
for (MethodAnnotation methodAnnotation: methodAnnotations) {
out.writeInt(methodAnnotation.method.getIndex());
out.writeInt(methodAnnotation.annotationSet.getOffset());
}
}
if (parameterAnnotations != null) {
for (ParameterAnnotation parameterAnnotation: parameterAnnotations) {
out.writeInt(parameterAnnotation.method.getIndex());
out.writeInt(parameterAnnotation.annotationSet.getOffset());
}
}
}
/** {@inheritDoc} */
public ItemType getItemType() {
return ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM;
}
/** {@inheritDoc} */
public String getConciseIdentity() {
TypeIdItem parentType = getParentType();
if (parentType == null) {
return "annotation_directory_item @0x" + Integer.toHexString(getOffset());
}
return "annotation_directory_item @0x" + Integer.toHexString(getOffset()) +
" (" + parentType.getTypeDescriptor() + ")";
}
/** {@inheritDoc} */
public int compareTo(AnnotationDirectoryItem o) {
Preconditions.checkNotNull(o);
TypeIdItem parentType = getParentType();
TypeIdItem otherParentType = o.getParentType();
if (parentType != null) {
if (otherParentType != null) {
return parentType.compareTo(otherParentType);
}
return 1;
}
if (otherParentType != null) {
return -1;
}
if (classAnnotations != null) {
if (o.classAnnotations != null) {
return classAnnotations.compareTo(o.classAnnotations);
}
return 1;
}
return -1;
}
/**
* Returns the parent type for an AnnotationDirectoryItem that is guaranteed to have a single parent, or null
* for one that may be referenced by multiple classes.
*
* Specifically, the AnnotationDirectoryItem may be referenced by multiple classes if it has only class annotations,
* but not field/method/parameter annotations.
*
* @return The parent type for this AnnotationDirectoryItem, or null if it may have multiple parents
*/
@Nullable
public TypeIdItem getParentType() {
if (fieldAnnotations != null && fieldAnnotations.length > 0) {
return fieldAnnotations[0].field.getContainingClass();
}
if (methodAnnotations != null && methodAnnotations.length > 0) {
return methodAnnotations[0].method.getContainingClass();
}
if (parameterAnnotations != null && parameterAnnotations.length > 0) {
return parameterAnnotations[0].method.getContainingClass();
}
return null;
}
/**
* @return An <code>AnnotationSetItem</code> containing the annotations associated with this class, or null
* if there are no class annotations
*/
@Nullable
public AnnotationSetItem getClassAnnotations() {
return classAnnotations;
}
/**
* Get a list of the field annotations in this <code>AnnotationDirectoryItem</code>
* @return A list of FieldAnnotation objects, or null if there are no field annotations
*/
@Nonnull
public List<FieldAnnotation> getFieldAnnotations() {
if (fieldAnnotations == null) {
return Collections.emptyList();
}
return ReadOnlyArrayList.of(fieldAnnotations);
}
/**
* Get a list of the method annotations in this <code>AnnotationDirectoryItem</code>
* @return A list of MethodAnnotation objects, or null if there are no method annotations
*/
@Nonnull
public List<MethodAnnotation> getMethodAnnotations() {
if (methodAnnotations == null) {
return Collections.emptyList();
}
return ReadOnlyArrayList.of(methodAnnotations);
}
/**
* Get a list of the parameter annotations in this <code>AnnotationDirectoryItem</code>
* @return A list of ParameterAnnotation objects, or null if there are no parameter annotations
*/
@Nonnull
public List<ParameterAnnotation> getParameterAnnotations() {
if (parameterAnnotations == null) {
return Collections.emptyList();
}
return ReadOnlyArrayList.of(parameterAnnotations);
}
/**
* Gets the field annotations for the given field, or null if no annotations are defined for that field
* @param fieldIdItem The field to get the annotations for
* @return An <code>AnnotationSetItem</code> containing the field annotations, or null if none are found
*/
@Nullable
public AnnotationSetItem getFieldAnnotations(FieldIdItem fieldIdItem) {
if (fieldAnnotations == null) {
return null;
}
int index = Arrays.binarySearch(fieldAnnotations, fieldIdItem);
if (index < 0) {
return null;
}
return fieldAnnotations[index].annotationSet;
}
/**
* Gets the method annotations for the given method, or null if no annotations are defined for that method
* @param methodIdItem The method to get the annotations for
* @return An <code>AnnotationSetItem</code> containing the method annotations, or null if none are found
*/
@Nullable
public AnnotationSetItem getMethodAnnotations(MethodIdItem methodIdItem) {
if (methodAnnotations == null) {
return null;
}
int index = Arrays.binarySearch(methodAnnotations, methodIdItem);
if (index < 0) {
return null;
}
return methodAnnotations[index].annotationSet;
}
/**
* Gets the parameter annotations for the given method, or null if no parameter annotations are defined for that
* method
* @param methodIdItem The method to get the parameter annotations for
* @return An <code>AnnotationSetRefList</code> containing the parameter annotations, or null if none are found
*/
@Nullable
public AnnotationSetRefList getParameterAnnotations(MethodIdItem methodIdItem) {
if (parameterAnnotations == null) {
return null;
}
int index = Arrays.binarySearch(parameterAnnotations, methodIdItem);
if (index < 0) {
return null;
}
return parameterAnnotations[index].annotationSet;
}
/**
*
*/
public int getClassAnnotationCount() {
if (classAnnotations == null) {
return 0;
}
AnnotationItem[] annotations = classAnnotations.getAnnotations();
return annotations.length;
}
/**
* @return The number of field annotations in this <code>AnnotationDirectoryItem</code>
*/
public int getFieldAnnotationCount() {
if (fieldAnnotations == null) {
return 0;
}
return fieldAnnotations.length;
}
/**
* @return The number of method annotations in this <code>AnnotationDirectoryItem</code>
*/
public int getMethodAnnotationCount() {
if (methodAnnotations == null) {
return 0;
}
return methodAnnotations.length;
}
/**
* @return The number of parameter annotations in this <code>AnnotationDirectoryItem</code>
*/
public int getParameterAnnotationCount() {
if (parameterAnnotations == null) {
return 0;
}
return parameterAnnotations.length;
}
@Override
public int hashCode() {
// If the item has a single parent, we can use the re-use the identity (hash) of that parent
TypeIdItem parentType = getParentType();
if (parentType != null) {
return parentType.hashCode();
}
if (classAnnotations != null) {
return classAnnotations.hashCode();
}
return 0;
}
@Override
public boolean equals(Object o) {
if (this==o) {
return true;
}
if (o==null || !this.getClass().equals(o.getClass())) {
return false;
}
AnnotationDirectoryItem other = (AnnotationDirectoryItem)o;
return (this.compareTo(other) == 0);
}
public static class FieldAnnotation implements Comparable<Convertible<FieldIdItem>>, Convertible<FieldIdItem> {
public final FieldIdItem field;
public final AnnotationSetItem annotationSet;
public FieldAnnotation(FieldIdItem field, AnnotationSetItem annotationSet) {
this.field = field;
this.annotationSet = annotationSet;
}
public int compareTo(Convertible<FieldIdItem> other) {
return field.compareTo(other.convert());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
return compareTo((FieldAnnotation)o) == 0;
}
@Override
public int hashCode() {
return field.hashCode() + 31 * annotationSet.hashCode();
}
public FieldIdItem convert() {
return field;
}
}
public static class MethodAnnotation implements Comparable<Convertible<MethodIdItem>>, Convertible<MethodIdItem> {
public final MethodIdItem method;
public final AnnotationSetItem annotationSet;
public MethodAnnotation(MethodIdItem method, AnnotationSetItem annotationSet) {
this.method = method;
this.annotationSet = annotationSet;
}
public int compareTo(Convertible<MethodIdItem> other) {
return method.compareTo(other.convert());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
return compareTo((MethodAnnotation)o) == 0;
}
@Override
public int hashCode() {
return method.hashCode() + 31 * annotationSet.hashCode();
}
public MethodIdItem convert() {
return method;
}
}
public static class ParameterAnnotation implements Comparable<Convertible<MethodIdItem>>,
Convertible<MethodIdItem> {
public final MethodIdItem method;
public final AnnotationSetRefList annotationSet;
public ParameterAnnotation(MethodIdItem method, AnnotationSetRefList annotationSet) {
this.method = method;
this.annotationSet = annotationSet;
}
public int compareTo(Convertible<MethodIdItem> other) {
return method.compareTo(other.convert());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
return compareTo((ParameterAnnotation)o) == 0;
}
@Override
public int hashCode() {
return method.hashCode() + 31 * annotationSet.hashCode();
}
public MethodIdItem convert() {
return method;
}
}
}

View File

@ -1,162 +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.dexlib;
import org.jf.dexlib.EncodedValue.AnnotationEncodedSubValue;
import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.Util.Input;
public class AnnotationItem extends Item<AnnotationItem> {
private int hashCode = 0;
private AnnotationVisibility visibility;
private AnnotationEncodedSubValue annotationValue;
/**
* Creates a new uninitialized <code>AnnotationItem</code>
* @param dexFile The <code>DexFile</code> that this item belongs to
*/
protected AnnotationItem(DexFile dexFile) {
super(dexFile);
}
/**
* Creates a new <code>AnnotationItem</code> with the given values
* @param dexFile The <code>DexFile</code> that this item belongs to
* @param visibility The visibility of this annotation
* @param annotationValue The value of this annotation
*/
private AnnotationItem(DexFile dexFile, AnnotationVisibility visibility,
AnnotationEncodedSubValue annotationValue) {
super(dexFile);
this.visibility = visibility;
this.annotationValue = annotationValue;
}
/**
* Returns an <code>AnnotationItem</code> for the given values, and that has been interned into the given
* <code>DexFile</code>
* @param dexFile The <code>DexFile</code> that this item belongs to
* @param visibility The visibility of this annotation
* @param annotationValue The value of this annotation
* @return an <code>AnnotationItem</code> for the given values, and that has been interned into the given
* <code>DexFile</code>
*/
public static AnnotationItem internAnnotationItem(DexFile dexFile, AnnotationVisibility visibility,
AnnotationEncodedSubValue annotationValue) {
AnnotationItem annotationItem = new AnnotationItem(dexFile, visibility, annotationValue);
return dexFile.AnnotationsSection.intern(annotationItem);
}
/** {@inheritDoc} */
protected void readItem(Input in, ReadContext readContext) {
visibility = AnnotationVisibility.fromByte(in.readByte());
annotationValue = new AnnotationEncodedSubValue(dexFile, in);
}
/** {@inheritDoc} */
protected int placeItem(int offset) {
return annotationValue.placeValue(offset + 1);
}
/** {@inheritDoc} */
protected void writeItem(AnnotatedOutput out) {
if (out.annotates()) {
out.annotate("visibility: " + visibility.name());
out.writeByte(visibility.value);
annotationValue.writeValue(out);
}else {
out.writeByte(visibility.value);
annotationValue.writeValue(out);
}
}
/** {@inheritDoc} */
public ItemType getItemType() {
return ItemType.TYPE_ANNOTATION_ITEM;
}
/** {@inheritDoc} */
public String getConciseIdentity() {
return "annotation_item @0x" + Integer.toHexString(getOffset());
}
/** {@inheritDoc} */
public int compareTo(AnnotationItem o) {
int comp = visibility.value - o.visibility.value;
if (comp == 0) {
comp = annotationValue.compareTo(o.annotationValue);
}
return comp;
}
/**
* @return The visibility of this annotation
*/
public AnnotationVisibility getVisibility() {
return visibility;
}
/**
* @return The encoded annotation value of this annotation
*/
public AnnotationEncodedSubValue getEncodedAnnotation() {
return annotationValue;
}
/**
* calculate and cache the hashcode
*/
private void calcHashCode() {
hashCode = visibility.value;
hashCode = hashCode * 31 + annotationValue.hashCode();
}
@Override
public int hashCode() {
//there's a small possibility that the actual hash code will be 0. If so, we'll
//just end up recalculating it each time
if (hashCode == 0)
calcHashCode();
return hashCode;
}
@Override
public boolean equals(Object o) {
if (this==o) {
return true;
}
if (o==null || !this.getClass().equals(o.getClass())) {
return false;
}
AnnotationItem other = (AnnotationItem)o;
return visibility == other.visibility && annotationValue.equals(other.annotationValue);
}
}

View File

@ -1,190 +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.dexlib;
import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.Util.Input;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class AnnotationSetItem extends Item<AnnotationSetItem> {
private int hashCode = 0;
private AnnotationItem[] annotations;
/**
* Creates a new uninitialized <code>AnnotationSetItem</code>
* @param dexFile The <code>DexFile</code> that this item belongs to
*/
protected AnnotationSetItem(DexFile dexFile) {
super(dexFile);
}
/**
* Creates a new <code>AnnotationSetItem</code> for the given annotations
* @param dexFile The <code>DexFile</code> that this item belongs to
* @param annotations The annotations for this <code>AnnotationSetItem</code>
*/
private AnnotationSetItem(DexFile dexFile, AnnotationItem[] annotations) {
super(dexFile);
this.annotations = annotations;
}
/**
* Returns an <code>AnnotationSetItem</code> for the given annotations, and that has been interned into the given
* <code>DexFile</code>
* @param dexFile The <code>DexFile</code> that this item belongs to
* @param annotations The annotations for this <code>AnnotationSetItem</code>
* @return an <code>AnnotationSetItem</code> for the given annotations
*/
public static AnnotationSetItem internAnnotationSetItem(DexFile dexFile, List<AnnotationItem> annotations) {
AnnotationSetItem annotationSetItem;
if (annotations == null) {
annotationSetItem = new AnnotationSetItem(dexFile, new AnnotationItem[0]);
} else {
AnnotationItem[] annotationsArray = new AnnotationItem[annotations.size()];
annotations.toArray(annotationsArray);
annotationSetItem = new AnnotationSetItem(dexFile, annotationsArray);
}
return dexFile.AnnotationSetsSection.intern(annotationSetItem);
}
/** {@inheritDoc} */
protected void readItem(Input in, ReadContext readContext) {
annotations = new AnnotationItem[in.readInt()];
for (int i=0; i<annotations.length; i++) {
annotations[i] = (AnnotationItem)readContext.getOffsettedItemByOffset(ItemType.TYPE_ANNOTATION_ITEM,
in.readInt());
}
}
/** {@inheritDoc} */
protected int placeItem(int offset) {
return offset + 4 + annotations.length * 4;
}
/** {@inheritDoc} */
protected void writeItem(AnnotatedOutput out) {
Arrays.sort(annotations, new Comparator<AnnotationItem>() {
public int compare(AnnotationItem annotationItem, AnnotationItem annotationItem2) {
int annotationItemIndex = annotationItem.getEncodedAnnotation().annotationType.getIndex();
int annotationItemIndex2 = annotationItem2.getEncodedAnnotation().annotationType.getIndex();
if (annotationItemIndex < annotationItemIndex2) {
return -1;
} else if (annotationItemIndex == annotationItemIndex2) {
return 0;
}
return 1;
}
});
if (out.annotates()) {
out.annotate(4, "size: 0x" + Integer.toHexString(annotations.length) + " (" + annotations.length + ")");
for (AnnotationItem annotationItem: annotations) {
out.annotate(4, "annotation_off: 0x" + Integer.toHexString(annotationItem.getOffset()) + " - " +
annotationItem.getEncodedAnnotation().annotationType.getTypeDescriptor());
}
}
out.writeInt(annotations.length);
for (AnnotationItem annotationItem: annotations) {
out.writeInt(annotationItem.getOffset());
}
}
/** {@inheritDoc} */
public ItemType getItemType() {
return ItemType.TYPE_ANNOTATION_SET_ITEM;
}
/** {@inheritDoc} */
public String getConciseIdentity() {
return "annotation_set_item @0x" + Integer.toHexString(getOffset());
}
/** {@inheritDoc} */
public int compareTo(AnnotationSetItem o) {
if (o == null) {
return 1;
}
int comp = annotations.length - o.annotations.length;
if (comp == 0) {
for (int i=0; i<annotations.length; i++) {
comp = annotations[i].compareTo(o.annotations[i]);
if (comp != 0) {
return comp;
}
}
}
return comp;
}
/**
* @return An array of the <code>AnnotationItem</code> objects in this <code>AnnotationSetItem</code>
*/
public AnnotationItem[] getAnnotations() {
return annotations;
}
/**
* calculate and cache the hashcode
*/
private void calcHashCode() {
hashCode = 0;
for (AnnotationItem annotationItem: annotations) {
hashCode = hashCode * 31 + annotationItem.hashCode();
}
}
@Override
public int hashCode() {
//there's a small possibility that the actual hash code will be 0. If so, we'll
//just end up recalculating it each time
if (hashCode == 0)
calcHashCode();
return hashCode;
}
@Override
public boolean equals(Object o) {
if (this==o) {
return true;
}
if (o==null || !this.getClass().equals(o.getClass())) {
return false;
}
AnnotationSetItem other = (AnnotationSetItem)o;
return (this.compareTo(other) == 0);
}
}

View File

@ -1,170 +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.dexlib;
import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.Util.Input;
import java.util.List;
public class AnnotationSetRefList extends Item<AnnotationSetRefList> {
private int hashCode = 0;
private AnnotationSetItem[] annotationSets;
/**
* Creates a new uninitialized <code>AnnotationSetRefList</code>
* @param dexFile The <code>DexFile</code> that this item belongs to
*/
protected AnnotationSetRefList(DexFile dexFile) {
super(dexFile);
}
/**
* Creates a new <code>AnnotationSetRefList</code> for the given annotation sets
* @param dexFile The <code>DexFile</code> that this item belongs to
* @param annotationSets The annotationSets for this <code>AnnotationSetRefList</code>
*/
private AnnotationSetRefList(DexFile dexFile, AnnotationSetItem[] annotationSets) {
super(dexFile);
this.annotationSets = annotationSets;
}
/**
* Returns an <code>AnnotationSetRefList</code> for the given annotation sets, and that has been interned into the
* given <code>DexFile</code>
* @param dexFile The <code>DexFile</code> that this item belongs to
* @param annotationSets The annotation sets for this <code>AnnotationSetRefList</code>
* @return an <code>AnnotationSetItem</code> for the given annotations
*/
public static AnnotationSetRefList internAnnotationSetRefList(DexFile dexFile,
List<AnnotationSetItem> annotationSets) {
AnnotationSetItem[] annotationSetsArray = new AnnotationSetItem[annotationSets.size()];
annotationSets.toArray(annotationSetsArray);
AnnotationSetRefList annotationSetRefList = new AnnotationSetRefList(dexFile, annotationSetsArray);
return dexFile.AnnotationSetRefListsSection.intern(annotationSetRefList);
}
/** {@inheritDoc} */
protected void readItem(Input in, ReadContext readContext) {
annotationSets = new AnnotationSetItem[in.readInt()];
for (int i=0; i<annotationSets.length; i++) {
annotationSets[i] = (AnnotationSetItem)readContext.getOptionalOffsettedItemByOffset(
ItemType.TYPE_ANNOTATION_SET_ITEM, in.readInt());
}
}
/** {@inheritDoc} */
protected int placeItem(int offset) {
return offset + 4 + annotationSets.length * 4;
}
/** {@inheritDoc} */
protected void writeItem(AnnotatedOutput out) {
if (out.annotates()) {
out.annotate(4, "size: 0x" + Integer.toHexString(annotationSets.length) + " (" + annotationSets.length +
")");
for (AnnotationSetItem annotationSetItem: annotationSets) {
out.annotate(4, "annotation_set_off: 0x" + Integer.toHexString(annotationSetItem.getOffset()));
}
}
out.writeInt(annotationSets.length);
for (AnnotationSetItem annotationSetItem: annotationSets) {
out.writeInt(annotationSetItem.getOffset());
}
}
/** {@inheritDoc} */
public ItemType getItemType() {
return ItemType.TYPE_ANNOTATION_SET_REF_LIST;
}
/** {@inheritDoc} */
public String getConciseIdentity() {
return "annotation_set_item @0x" + Integer.toHexString(getOffset());
}
/** {@inheritDoc} */
public int compareTo(AnnotationSetRefList o) {
int comp = annotationSets.length - o.annotationSets.length;
if (comp != 0) {
return comp;
}
for (int i=0; i<annotationSets.length; i++) {
comp = annotationSets[i].compareTo(o.annotationSets[i]);
if (comp != 0) {
return comp;
}
}
return comp;
}
/**
* @return An array of the <code>AnnotationSetItem</code> objects that make up this
* <code>AnnotationSetRefList</code>
*/
public AnnotationSetItem[] getAnnotationSets() {
return annotationSets;
}
/**
* calculate and cache the hashcode
*/
private void calcHashCode() {
hashCode = 0;
for (AnnotationSetItem annotationSetItem: annotationSets) {
hashCode = hashCode * 31 + annotationSetItem.hashCode();
}
}
@Override
public int hashCode() {
//there's a small possibility that the actual hash code will be 0. If so, we'll
//just end up recalculating it each time
if (hashCode == 0)
calcHashCode();
return hashCode;
}
@Override
public boolean equals(Object o) {
if (this==o) {
return true;
}
if (o==null || !this.getClass().equals(o.getClass())) {
return false;
}
AnnotationSetRefList other = (AnnotationSetRefList)o;
return (this.compareTo(other) == 0);
}
}

View File

@ -1,55 +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.dexlib;
public enum AnnotationVisibility {
BUILD((byte)0, "build"),
RUNTIME((byte)1, "runtime"),
SYSTEM((byte)2, "system");
public final byte value;
public final String visibility;
private AnnotationVisibility(byte value, String visibility) {
this.value = value;
this.visibility = visibility;
}
public static AnnotationVisibility fromByte(byte value) {
switch (value) {
case (byte)0:
return BUILD;
case (byte)1:
return RUNTIME;
case (byte)2:
return SYSTEM;
default:
throw new RuntimeException("Invalid annotation visibility value: " + value);
}
}
}

View File

@ -1,843 +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.dexlib;
import com.google.common.base.Preconditions;
import org.jf.dexlib.Util.*;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
public class ClassDataItem extends Item<ClassDataItem> {
@Nullable
private EncodedField[] staticFields = null;
@Nullable
private EncodedField[] instanceFields = null;
@Nullable
private EncodedMethod[] directMethods = null;
@Nullable
private EncodedMethod[] virtualMethods = null;
/**
* Creates a new uninitialized <code>ClassDataItem</code>
* @param dexFile The <code>DexFile</code> that this item belongs to
*/
public ClassDataItem(final DexFile dexFile) {
super(dexFile);
}
/**
* Creates a new <code>ClassDataItem</code> with the given values
* @param dexFile The <code>DexFile</code> that this item belongs to
* @param staticFields The static fields for this class
* @param instanceFields The instance fields for this class
* @param directMethods The direct methods for this class
* @param virtualMethods The virtual methods for this class
*/
private ClassDataItem(DexFile dexFile, @Nullable EncodedField[] staticFields,
@Nullable EncodedField[] instanceFields, @Nullable EncodedMethod[] directMethods,
@Nullable EncodedMethod[] virtualMethods) {
super(dexFile);
this.staticFields = staticFields;
this.instanceFields = instanceFields;
this.directMethods = directMethods;
this.virtualMethods = virtualMethods;
}
/**
* Creates a new <code>ClassDataItem</code> with the given values
* @param dexFile The <code>DexFile</code> that this item belongs to
* @param staticFields The static fields for this class
* @param instanceFields The instance fields for this class
* @param directMethods The direct methods for this class
* @param virtualMethods The virtual methods for this class
* @return a new <code>ClassDataItem</code> with the given values
*/
public static ClassDataItem internClassDataItem(DexFile dexFile, @Nullable List<EncodedField> staticFields,
@Nullable List<EncodedField> instanceFields,
@Nullable List<EncodedMethod> directMethods,
@Nullable List<EncodedMethod> virtualMethods) {
EncodedField[] staticFieldsArray = null;
EncodedField[] instanceFieldsArray = null;
EncodedMethod[] directMethodsArray = null;
EncodedMethod[] virtualMethodsArray = null;
if (staticFields != null && staticFields.size() > 0) {
SortedSet<EncodedField> staticFieldsSet = new TreeSet<EncodedField>();
for (EncodedField staticField: staticFields) {
if (staticFieldsSet.contains(staticField)) {
System.err.println(String.format("Ignoring duplicate static field definition: %s",
staticField.field.getFieldString()));
continue;
}
staticFieldsSet.add(staticField);
}
staticFieldsArray = new EncodedField[staticFieldsSet.size()];
staticFieldsArray = staticFieldsSet.toArray(staticFieldsArray);
}
if (instanceFields != null && instanceFields.size() > 0) {
SortedSet<EncodedField> instanceFieldsSet = new TreeSet<EncodedField>();
for (EncodedField instanceField: instanceFields) {
if (instanceFieldsSet.contains(instanceField)) {
System.err.println(String.format("Ignoring duplicate instance field definition: %s",
instanceField.field.getFieldString()));
continue;
}
instanceFieldsSet.add(instanceField);
}
instanceFieldsArray = new EncodedField[instanceFieldsSet.size()];
instanceFieldsArray = instanceFieldsSet.toArray(instanceFieldsArray);
}
TreeSet<EncodedMethod> directMethodSet = new TreeSet<EncodedMethod>();
if (directMethods != null && directMethods.size() > 0) {
for (EncodedMethod directMethod: directMethods) {
if (directMethodSet.contains(directMethod)) {
System.err.println(String.format("Ignoring duplicate direct method definition: %s",
directMethod.method.getMethodString()));
continue;
}
directMethodSet.add(directMethod);
}
directMethodsArray = new EncodedMethod[directMethodSet.size()];
directMethodsArray = directMethodSet.toArray(directMethodsArray);
}
if (virtualMethods != null && virtualMethods.size() > 0) {
TreeSet<EncodedMethod> virtualMethodSet = new TreeSet<EncodedMethod>();
for (EncodedMethod virtualMethod: virtualMethods) {
if (directMethodSet.contains(virtualMethod)) {
// If both a direct and virtual definition is present, dalvik's behavior seems to be undefined,
// so we can't gracefully handle this case, like we can if the duplicates are all direct or all
// virtual -- in which case, we ignore all but the first definition
throw new RuntimeException(String.format("Duplicate direct+virtual method definition: %s",
virtualMethod.method.getMethodString()));
}
if (virtualMethodSet.contains(virtualMethod)) {
System.err.println(String.format("Ignoring duplicate virtual method definition: %s",
virtualMethod.method.getMethodString()));
continue;
}
virtualMethodSet.add(virtualMethod);
}
virtualMethodsArray = new EncodedMethod[virtualMethodSet.size()];
virtualMethodsArray = virtualMethodSet.toArray(virtualMethodsArray);
}
ClassDataItem classDataItem = new ClassDataItem(dexFile, staticFieldsArray, instanceFieldsArray,
directMethodsArray, virtualMethodsArray);
return dexFile.ClassDataSection.intern(classDataItem);
}
/** {@inheritDoc} */
protected void readItem(Input in, ReadContext readContext) {
int staticFieldsCount = in.readUnsignedLeb128();
int instanceFieldsCount = in.readUnsignedLeb128();
int directMethodsCount = in.readUnsignedLeb128();
int virtualMethodsCount = in.readUnsignedLeb128();
if (staticFieldsCount > 0) {
staticFields = new EncodedField[staticFieldsCount];
EncodedField previousEncodedField = null;
for (int i=0; i<staticFieldsCount; i++) {
try {
staticFields[i] = previousEncodedField = new EncodedField(dexFile, in, previousEncodedField);
} catch (Exception ex) {
throw ExceptionWithContext.withContext(ex, "Error while reading static field at index " + i);
}
}
}
if (instanceFieldsCount > 0) {
instanceFields = new EncodedField[instanceFieldsCount];
EncodedField previousEncodedField = null;
for (int i=0; i<instanceFieldsCount; i++) {
try {
instanceFields[i] = previousEncodedField = new EncodedField(dexFile, in, previousEncodedField);
} catch (Exception ex) {
throw ExceptionWithContext.withContext(ex, "Error while reading instance field at index " + i);
}
}
}
if (directMethodsCount > 0) {
directMethods = new EncodedMethod[directMethodsCount];
EncodedMethod previousEncodedMethod = null;
for (int i=0; i<directMethodsCount; i++) {
try {
directMethods[i] = previousEncodedMethod = new EncodedMethod(dexFile, readContext, in,
previousEncodedMethod);
} catch (Exception ex) {
throw ExceptionWithContext.withContext(ex, "Error while reading direct method at index " + i);
}
}
}
if (virtualMethodsCount > 0) {
virtualMethods = new EncodedMethod[virtualMethodsCount];
EncodedMethod previousEncodedMethod = null;
for (int i=0; i<virtualMethodsCount; i++) {
try {
virtualMethods[i] = previousEncodedMethod = new EncodedMethod(dexFile, readContext, in,
previousEncodedMethod);
} catch (Exception ex) {
throw ExceptionWithContext.withContext(ex, "Error while reading virtual method at index " + i);
}
}
}
}
/** {@inheritDoc} */
protected int placeItem(int offset) {
offset += Leb128Utils.unsignedLeb128Size(getStaticFieldCount());
offset += Leb128Utils.unsignedLeb128Size(getInstanceFieldCount());
offset += Leb128Utils.unsignedLeb128Size(getDirectMethodCount());
offset += Leb128Utils.unsignedLeb128Size(getVirtualMethodCount());
if (staticFields != null) {
EncodedField previousEncodedField = null;
for (EncodedField encodedField: staticFields) {
offset = encodedField.place(offset, previousEncodedField);
previousEncodedField = encodedField;
}
}
if (instanceFields != null) {
EncodedField previousEncodedField = null;
for (EncodedField encodedField: instanceFields) {
offset = encodedField.place(offset, previousEncodedField);
previousEncodedField = encodedField;
}
}
if (directMethods != null) {
EncodedMethod previousEncodedMethod = null;
for (EncodedMethod encodedMethod: directMethods) {
offset = encodedMethod.place(offset, previousEncodedMethod);
previousEncodedMethod = encodedMethod;
}
}
if (virtualMethods != null) {
EncodedMethod previousEncodedMethod = null;
for (EncodedMethod encodedMethod: virtualMethods) {
offset = encodedMethod.place(offset, previousEncodedMethod);
previousEncodedMethod = encodedMethod;
}
}
return offset;
}
/** {@inheritDoc} */
protected void writeItem(AnnotatedOutput out) {
if (out.annotates()) {
int staticFieldCount = getStaticFieldCount();
out.annotate("static_fields_size: 0x" + Integer.toHexString(staticFieldCount) + " (" +
staticFieldCount + ")");
out.writeUnsignedLeb128(staticFieldCount);
int instanceFieldCount = getInstanceFieldCount();
out.annotate("instance_fields_size: 0x" + Integer.toHexString(instanceFieldCount) + " (" +
instanceFieldCount + ")");
out.writeUnsignedLeb128(instanceFieldCount);
int directMethodCount = getDirectMethodCount();
out.annotate("direct_methods_size: 0x" + Integer.toHexString(directMethodCount) + " (" +
directMethodCount + ")");
out.writeUnsignedLeb128(directMethodCount);
int virtualMethodCount = getVirtualMethodCount();
out.annotate("virtual_methods_size: 0x" + Integer.toHexString(virtualMethodCount) + " (" +
virtualMethodCount + ")");
out.writeUnsignedLeb128(virtualMethodCount);
if (staticFields != null) {
int index = 0;
EncodedField previousEncodedField = null;
for (EncodedField encodedField: staticFields) {
out.annotate("[" + index++ + "] static_field");
out.indent();
encodedField.writeTo(out, previousEncodedField);
out.deindent();
previousEncodedField = encodedField;
}
}
if (instanceFields != null) {
int index = 0;
EncodedField previousEncodedField = null;
for (EncodedField encodedField: instanceFields) {
out.annotate("[" + index++ + "] instance_field");
out.indent();
encodedField.writeTo(out, previousEncodedField);
out.deindent();
previousEncodedField = encodedField;
}
}
if (directMethods != null) {
int index = 0;
EncodedMethod previousEncodedMethod = null;
for (EncodedMethod encodedMethod: directMethods) {
out.annotate("[" + index++ + "] direct_method");
out.indent();
encodedMethod.writeTo(out, previousEncodedMethod);
out.deindent();
previousEncodedMethod = encodedMethod;
}
}
if (virtualMethods != null) {
int index = 0;
EncodedMethod previousEncodedMethod = null;
for (EncodedMethod encodedMethod: virtualMethods) {
out.annotate("[" + index++ + "] virtual_method");
out.indent();
encodedMethod.writeTo(out, previousEncodedMethod);
out.deindent();
previousEncodedMethod = encodedMethod;
}
}
} else {
out.writeUnsignedLeb128(getStaticFieldCount());
out.writeUnsignedLeb128(getInstanceFieldCount());
out.writeUnsignedLeb128(getDirectMethodCount());
out.writeUnsignedLeb128(getVirtualMethodCount());
if (staticFields != null) {
EncodedField previousEncodedField = null;
for (EncodedField encodedField: staticFields) {
encodedField.writeTo(out, previousEncodedField);
previousEncodedField = encodedField;
}
}
if (instanceFields != null) {
EncodedField previousEncodedField = null;
for (EncodedField encodedField: instanceFields) {
encodedField.writeTo(out, previousEncodedField);
previousEncodedField = encodedField;
}
}
if (directMethods != null) {
EncodedMethod previousEncodedMethod = null;
for (EncodedMethod encodedMethod: directMethods) {
encodedMethod.writeTo(out, previousEncodedMethod);
previousEncodedMethod = encodedMethod;
}
}
if (virtualMethods != null) {
EncodedMethod previousEncodedMethod = null;
for (EncodedMethod encodedMethod: virtualMethods) {
encodedMethod.writeTo(out, previousEncodedMethod);
previousEncodedMethod = encodedMethod;
}
}
}
}
/** {@inheritDoc} */
public ItemType getItemType() {
return ItemType.TYPE_CLASS_DATA_ITEM;
}
/** {@inheritDoc} */
public String getConciseIdentity() {
TypeIdItem parentType = getParentType();
if (parentType == null) {
return "class_data_item @0x" + Integer.toHexString(getOffset());
}
return "class_data_item @0x" + Integer.toHexString(getOffset()) + " (" + parentType.getTypeDescriptor() +")";
}
/** {@inheritDoc} */
public int compareTo(ClassDataItem other) {
Preconditions.checkNotNull(other);
// An empty CodeDataItem may be shared by multiple ClassDefItems, so we can't use parent in this case
if (isEmpty()) {
if (other.isEmpty()) {
return 0;
}
return -1;
}
if (other.isEmpty()) {
return 1;
}
TypeIdItem parentType = getParentType();
TypeIdItem otherParentType= other.getParentType();
if (parentType == null) {
if (otherParentType == null) {
return 0;
}
return -1;
}
if (otherParentType == null) {
return 1;
}
return parentType.compareTo(otherParentType);
}
@Override
public int hashCode() {
// If the item has a single parent, we can use the re-use the identity (hash) of that parent
TypeIdItem parentType = getParentType();
if (parentType != null) {
return parentType.hashCode();
}
return 0;
}
/**
* Returns the parent type for a non-empty ClassDataItem, or null for an empty one (which could be referenced by
* multiple ClassDefItem parents)
*
* Only an empty ClassDataItem may have multiple parents.
*
* @return The parent type for this ClassDefItem, or null if it may have multiple parents
*/
@Nullable
public TypeIdItem getParentType() {
if (staticFields != null && staticFields.length > 0) {
return staticFields[0].field.getContainingClass();
}
if (instanceFields != null && instanceFields.length > 0) {
return instanceFields[0].field.getContainingClass();
}
if (directMethods != null && directMethods.length > 0) {
return directMethods[0].method.getContainingClass();
}
if (virtualMethods != null && virtualMethods.length > 0) {
return virtualMethods[0].method.getContainingClass();
}
return null;
}
/**
* @return the static fields for this class
*/
@Nonnull
public List<EncodedField> getStaticFields() {
if (staticFields == null) {
return Collections.emptyList();
}
return ReadOnlyArrayList.of(staticFields);
}
/**
* @return the instance fields for this class
*/
@Nonnull
public List<EncodedField> getInstanceFields() {
if (instanceFields == null) {
return Collections.emptyList();
}
return ReadOnlyArrayList.of(instanceFields);
}
/**
* @return the direct methods for this class
*/
@Nonnull
public List<EncodedMethod> getDirectMethods() {
if (directMethods == null) {
return Collections.emptyList();
}
return ReadOnlyArrayList.of(directMethods);
}
/**
* @return the virtual methods for this class
*/
@Nonnull
public List<EncodedMethod> getVirtualMethods() {
if (virtualMethods == null) {
return Collections.emptyList();
}
return ReadOnlyArrayList.of(virtualMethods);
}
/**
* @return The number of static fields in this <code>ClassDataItem</code>
*/
public int getStaticFieldCount() {
if (staticFields == null) {
return 0;
}
return staticFields.length;
}
/**
* @return The number of instance fields in this <code>ClassDataItem</code>
*/
public int getInstanceFieldCount() {
if (instanceFields == null) {
return 0;
}
return instanceFields.length;
}
/**
* @return The number of direct methods in this <code>ClassDataItem</code>
*/
public int getDirectMethodCount() {
if (directMethods == null) {
return 0;
}
return directMethods.length;
}
/**
* @return The number of virtual methods in this <code>ClassDataItem</code>
*/
public int getVirtualMethodCount() {
if (virtualMethods == null) {
return 0;
}
return virtualMethods.length;
}
/**
* @return true if this is an empty ClassDataItem
*/
public boolean isEmpty() {
return (getStaticFieldCount() + getInstanceFieldCount() +
getDirectMethodCount() + getVirtualMethodCount()) == 0;
}
/**
* Performs a binary search for the definition of the specified direct method
* @param methodIdItem The MethodIdItem of the direct method to search for
* @return The EncodedMethod for the specified direct method, or null if not found
*/
public EncodedMethod findDirectMethodByMethodId(MethodIdItem methodIdItem) {
return findMethodByMethodIdInternal(methodIdItem.index, directMethods);
}
/**
* Performs a binary search for the definition of the specified virtual method
* @param methodIdItem The MethodIdItem of the virtual method to search for
* @return The EncodedMethod for the specified virtual method, or null if not found
*/
public EncodedMethod findVirtualMethodByMethodId(MethodIdItem methodIdItem) {
return findMethodByMethodIdInternal(methodIdItem.index, virtualMethods);
}
/**
* Performs a binary search for the definition of the specified method. It can be either direct or virtual
* @param methodIdItem The MethodIdItem of the virtual method to search for
* @return The EncodedMethod for the specified virtual method, or null if not found
*/
public EncodedMethod findMethodByMethodId(MethodIdItem methodIdItem) {
EncodedMethod encodedMethod = findMethodByMethodIdInternal(methodIdItem.index, directMethods);
if (encodedMethod != null) {
return encodedMethod;
}
return findMethodByMethodIdInternal(methodIdItem.index, virtualMethods);
}
private static EncodedMethod findMethodByMethodIdInternal(int methodIdItemIndex, EncodedMethod[] encodedMethods) {
int min = 0;
int max = encodedMethods.length;
while (min<max) {
int index = (min+max)>>1;
EncodedMethod encodedMethod = encodedMethods[index];
int encodedMethodIndex = encodedMethod.method.getIndex();
if (encodedMethodIndex == methodIdItemIndex) {
return encodedMethod;
} else if (encodedMethodIndex < methodIdItemIndex) {
if (min == index) {
break;
}
min = index;
} else {
if (max == index) {
break;
}
max = index;
}
}
return null;
}
public static class EncodedField implements Comparable<EncodedField> {
/**
* The <code>FieldIdItem</code> that this <code>EncodedField</code> is associated with
*/
public final FieldIdItem field;
/**
* The access flags for this field
*/
public final int accessFlags;
/**
* Constructs a new <code>EncodedField</code> with the given values
* @param field The <code>FieldIdItem</code> that this <code>EncodedField</code> is associated with
* @param accessFlags The access flags for this field
*/
public EncodedField(FieldIdItem field, int accessFlags) {
this.field = field;
this.accessFlags = accessFlags;
}
/**
* This is used internally to construct a new <code>EncodedField</code> while reading in a <code>DexFile</code>
* @param dexFile The <code>DexFile</code> that is being read in
* @param in the Input object to read the <code>EncodedField</code> from
* @param previousEncodedField The previous <code>EncodedField</code> in the list containing this
* <code>EncodedField</code>.
*/
private EncodedField(DexFile dexFile, Input in, @Nullable EncodedField previousEncodedField) {
int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex();
field = dexFile.FieldIdsSection.getItemByIndex(in.readUnsignedLeb128() + previousIndex);
accessFlags = in.readUnsignedLeb128();
}
/**
* Writes the <code>EncodedField</code> to the given <code>AnnotatedOutput</code> object
* @param out the <code>AnnotatedOutput</code> object to write to
* @param previousEncodedField The previous <code>EncodedField</code> in the list containing this
* <code>EncodedField</code>.
*/
private void writeTo(AnnotatedOutput out, EncodedField previousEncodedField) {
int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex();
if (out.annotates()) {
out.annotate("field: " + field.getFieldString());
out.writeUnsignedLeb128(field.getIndex() - previousIndex);
out.annotate("access_flags: " + AccessFlags.formatAccessFlagsForField(accessFlags));
out.writeUnsignedLeb128(accessFlags);
}else {
out.writeUnsignedLeb128(field.getIndex() - previousIndex);
out.writeUnsignedLeb128(accessFlags);
}
}
/**
* Calculates the size of this <code>EncodedField</code> and returns the offset
* immediately following it
* @param offset the offset of this <code>EncodedField</code> in the <code>DexFile</code>
* @param previousEncodedField The previous <code>EncodedField</code> in the list containing this
* <code>EncodedField</code>.
* @return the offset immediately following this <code>EncodedField</code>
*/
private int place(int offset, EncodedField previousEncodedField) {
int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex();
offset += Leb128Utils.unsignedLeb128Size(field.getIndex() - previousIndex);
offset += Leb128Utils.unsignedLeb128Size(accessFlags);
return offset;
}
/**
* Compares this <code>EncodedField</code> to another, based on the comparison of the associated
* <code>FieldIdItem</code>
* @param other The <code>EncodedField</code> to compare against
* @return a standard integer comparison value indicating the relationship
*/
public int compareTo(EncodedField other)
{
return field.compareTo(other.field);
}
/**
* Determines if this <code>EncodedField</code> is equal to other, based on the equality of the associated
* <code>FieldIdItem</code>
* @param other The <code>EncodedField</code> to test for equality
* @return true if other is equal to this instance, otherwise false
*/
public boolean equals(Object other) {
if (other instanceof EncodedField) {
return compareTo((EncodedField)other) == 0;
}
return false;
}
/**
* @return true if this is a static field
*/
public boolean isStatic() {
return (accessFlags & AccessFlags.STATIC.getValue()) != 0;
}
}
public static class EncodedMethod implements Comparable<EncodedMethod> {
/**
* The <code>MethodIdItem</code> that this <code>EncodedMethod</code> is associated with
*/
public final MethodIdItem method;
/**
* The access flags for this method
*/
public final int accessFlags;
/**
* The <code>CodeItem</code> containing the code for this method, or null if there is no code for this method
* (i.e. an abstract method)
*/
public final CodeItem codeItem;
/**
* Constructs a new <code>EncodedMethod</code> with the given values
* @param method The <code>MethodIdItem</code> that this <code>EncodedMethod</code> is associated with
* @param accessFlags The access flags for this method
* @param codeItem The <code>CodeItem</code> containing the code for this method, or null if there is no code
* for this method (i.e. an abstract method)
*/
public EncodedMethod(MethodIdItem method, int accessFlags, CodeItem codeItem) {
this.method = method;
this.accessFlags = accessFlags;
this.codeItem = codeItem;
if (codeItem != null) {
codeItem.setParent(this);
}
}
/**
* This is used internally to construct a new <code>EncodedMethod</code> while reading in a <code>DexFile</code>
* @param dexFile The <code>DexFile</code> that is being read in
* @param readContext a <code>ReadContext</code> object to hold information that is only needed while reading
* in a file
* @param in the Input object to read the <code>EncodedMethod</code> from
* @param previousEncodedMethod The previous <code>EncodedMethod</code> in the list containing this
* <code>EncodedMethod</code>.
*/
public EncodedMethod(DexFile dexFile, ReadContext readContext, Input in, EncodedMethod previousEncodedMethod) {
int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex();
method = dexFile.MethodIdsSection.getItemByIndex(in.readUnsignedLeb128() + previousIndex);
accessFlags = in.readUnsignedLeb128();
if (dexFile.skipInstructions()) {
in.readUnsignedLeb128();
codeItem = null;
} else {
codeItem = (CodeItem)readContext.getOptionalOffsettedItemByOffset(ItemType.TYPE_CODE_ITEM,
in.readUnsignedLeb128());
}
if (codeItem != null) {
codeItem.setParent(this);
}
}
/**
* Writes the <code>EncodedMethod</code> to the given <code>AnnotatedOutput</code> object
* @param out the <code>AnnotatedOutput</code> object to write to
* @param previousEncodedMethod The previous <code>EncodedMethod</code> in the list containing this
* <code>EncodedMethod</code>.
*/
private void writeTo(AnnotatedOutput out, EncodedMethod previousEncodedMethod) {
int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex();
if (out.annotates()) {
out.annotate("method: " + method.getMethodString());
out.writeUnsignedLeb128(method.getIndex() - previousIndex);
out.annotate("access_flags: " + AccessFlags.formatAccessFlagsForMethod(accessFlags));
out.writeUnsignedLeb128(accessFlags);
if (codeItem != null) {
out.annotate("code_off: 0x" + Integer.toHexString(codeItem.getOffset()));
out.writeUnsignedLeb128(codeItem.getOffset());
} else {
out.annotate("code_off: 0x0");
out.writeUnsignedLeb128(0);
}
}else {
out.writeUnsignedLeb128(method.getIndex() - previousIndex);
out.writeUnsignedLeb128(accessFlags);
out.writeUnsignedLeb128(codeItem==null?0:codeItem.getOffset());
}
}
/**
* Calculates the size of this <code>EncodedMethod</code> and returns the offset
* immediately following it
* @param offset the offset of this <code>EncodedMethod</code> in the <code>DexFile</code>
* @param previousEncodedMethod The previous <code>EncodedMethod</code> in the list containing this
* <code>EncodedMethod</code>.
* @return the offset immediately following this <code>EncodedField</code>
*/
private int place(int offset, EncodedMethod previousEncodedMethod) {
int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex();
offset += Leb128Utils.unsignedLeb128Size(method.getIndex() - previousIndex);
offset += Leb128Utils.unsignedLeb128Size(accessFlags);
offset += codeItem==null?1:Leb128Utils.unsignedLeb128Size(codeItem.getOffset());
return offset;
}
/**
* Compares this <code>EncodedMethod</code> to another, based on the comparison of the associated
* <code>MethodIdItem</code>
* @param other The <code>EncodedMethod</code> to compare against
* @return a standard integer comparison value indicating the relationship
*/
public int compareTo(EncodedMethod other) {
return method.compareTo(other.method);
}
/**
* Determines if this <code>EncodedMethod</code> is equal to other, based on the equality of the associated
* <code>MethodIdItem</code>
* @param other The <code>EncodedMethod</code> to test for equality
* @return true if other is equal to this instance, otherwise false
*/
public boolean equals(Object other) {
if (other instanceof EncodedMethod) {
return compareTo((EncodedMethod)other) == 0;
}
return false;
}
/**
* @return true if this is a direct method
*/
public boolean isDirect() {
return ((accessFlags & (AccessFlags.STATIC.getValue() | AccessFlags.PRIVATE.getValue() |
AccessFlags.CONSTRUCTOR.getValue())) != 0);
}
}
}

View File

@ -1,374 +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.dexlib;
import org.jf.dexlib.EncodedValue.ArrayEncodedSubValue;
import org.jf.dexlib.EncodedValue.EncodedValue;
import org.jf.dexlib.Util.AccessFlags;
import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.Util.Input;
import org.jf.dexlib.Util.TypeUtils;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
public class ClassDefItem extends Item<ClassDefItem> {
private TypeIdItem classType;
private int accessFlags;
private @Nullable TypeIdItem superType;
private @Nullable TypeListItem implementedInterfaces;
private @Nullable StringIdItem sourceFile;
private @Nullable AnnotationDirectoryItem annotations;
private @Nullable ClassDataItem classData;
private @Nullable EncodedArrayItem staticFieldInitializers;
/**
* Creates a new uninitialized <code>ClassDefItem</code>
* @param dexFile The <code>DexFile</code> that this item belongs to
*/
protected ClassDefItem(DexFile dexFile) {
super(dexFile);
}
/**
* Creates a new <code>ClassDefItem</code> with the given values
* @param dexFile The <code>DexFile</code> that this item belongs to
* @param classType The type of this class
* @param accessFlags The access flags of this class
* @param superType The superclass of this class, or null if none (only valid for java.lang.Object)
* @param implementedInterfaces A list of the interfaces that this class implements, or null if none
* @param sourceFile The main source file that this class is defined in, or null if not available
* @param annotations The annotations for this class and its fields, methods and method parameters, or null if none
* @param classData The <code>ClassDataItem</code> containing the method and field definitions for this class
* @param staticFieldInitializers The initial values for this class's static fields, or null if none. The initial
* values should be in the same order as the static fields in the <code>ClassDataItem</code>. It can contain
* fewer items than static fields, in which case the remaining static fields will be initialized with a default
* value of null/0. The initial value for any fields that don't specifically have a value can be either the
* type-appropriate null/0 encoded value, or null.
*/
private ClassDefItem(DexFile dexFile, TypeIdItem classType, int accessFlags, @Nullable TypeIdItem superType,
@Nullable TypeListItem implementedInterfaces, @Nullable StringIdItem sourceFile,
@Nullable AnnotationDirectoryItem annotations, @Nullable ClassDataItem classData,
@Nullable EncodedArrayItem staticFieldInitializers) {
super(dexFile);
assert classType != null;
this.classType = classType;
this.accessFlags = accessFlags;
this.superType = superType;
this.implementedInterfaces = implementedInterfaces;
this.sourceFile = sourceFile;
this.annotations = annotations;
this.classData = classData;
this.staticFieldInitializers = staticFieldInitializers;
}
/**
* Returns a <code>ClassDefItem</code> for the given values, and that has been interned into the given
* <code>DexFile</code>
* @param dexFile The <code>DexFile</code> that this item belongs to
* @param classType The type of this class
* @param accessFlags The access flags of this class
* @param superType The superclass of this class, or null if none (only valid for java.lang.Object)
* @param implementedInterfaces A list of the interfaces that this class implements, or null if none
* @param sourceFile The main source file that this class is defined in, or null if not available
* @param annotations The annotations for this class and its fields, methods and method parameters, or null if none
* @param classData The <code>ClassDataItem</code> containing the method and field definitions for this class
* @param staticFieldInitializers The initial values for this class's static fields, or null if none. If it is not
* null, it must contain the same number of items as the number of static fields in this class. The value in the
* <code>StaticFieldInitializer</code> for any field that doesn't have an explicit initial value can either be null
* or be the type-appropriate null/0 value.
* @return a <code>ClassDefItem</code> for the given values, and that has been interned into the given
* <code>DexFile</code>
*/
public static ClassDefItem internClassDefItem(DexFile dexFile, TypeIdItem classType, int accessFlags,
@Nullable TypeIdItem superType, @Nullable TypeListItem implementedInterfaces,
@Nullable StringIdItem sourceFile, @Nullable AnnotationDirectoryItem annotations,
@Nullable ClassDataItem classData,
@Nullable List<StaticFieldInitializer> staticFieldInitializers) {
EncodedArrayItem encodedArrayItem = null;
if(!dexFile.getInplace() && staticFieldInitializers != null && staticFieldInitializers.size() > 0) {
assert classData != null;
assert staticFieldInitializers.size() == classData.getStaticFieldCount();
encodedArrayItem = makeStaticFieldInitializersItem(dexFile, staticFieldInitializers);
}
ClassDefItem classDefItem = new ClassDefItem(dexFile, classType, accessFlags, superType, implementedInterfaces,
sourceFile, annotations, classData, encodedArrayItem);
return dexFile.ClassDefsSection.intern(classDefItem);
}
/** {@inheritDoc} */
protected void readItem(Input in, ReadContext readContext) {
classType = dexFile.TypeIdsSection.getItemByIndex(in.readInt());
accessFlags = in.readInt();
superType = dexFile.TypeIdsSection.getOptionalItemByIndex(in.readInt());
implementedInterfaces = (TypeListItem)readContext.getOptionalOffsettedItemByOffset(ItemType.TYPE_TYPE_LIST,
in.readInt());
sourceFile = dexFile.StringIdsSection.getOptionalItemByIndex(in.readInt());
annotations = (AnnotationDirectoryItem)readContext.getOptionalOffsettedItemByOffset(
ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM, in.readInt());
classData = (ClassDataItem)readContext.getOptionalOffsettedItemByOffset(ItemType.TYPE_CLASS_DATA_ITEM, in.readInt());
staticFieldInitializers = (EncodedArrayItem)readContext.getOptionalOffsettedItemByOffset(
ItemType.TYPE_ENCODED_ARRAY_ITEM, in.readInt());
}
/** {@inheritDoc} */
protected int placeItem(int offset) {
return offset + 32;
}
/** {@inheritDoc} */
protected void writeItem(AnnotatedOutput out) {
if (out.annotates()) {
out.annotate(4, "class_type: " + classType.getTypeDescriptor());
out.annotate(4, "access_flags: " + AccessFlags.formatAccessFlagsForClass(accessFlags));
out.annotate(4, "superclass_type: " + (superType==null?"":superType.getTypeDescriptor()));
out.annotate(4, "interfaces: " +
(implementedInterfaces==null?"":implementedInterfaces.getTypeListString(" ")));
out.annotate(4, "source_file: " + (sourceFile==null?"":sourceFile.getStringValue()));
out.annotate(4, "annotations_off: " +
(annotations==null?"":"0x"+Integer.toHexString(annotations.getOffset())));
out.annotate(4, "class_data_off:" +
(classData==null?"":"0x"+Integer.toHexString(classData.getOffset())));
out.annotate(4, "static_values_off: " +
(staticFieldInitializers==null?"":"0x"+Integer.toHexString(staticFieldInitializers.getOffset())));
}
out.writeInt(classType.getIndex());
out.writeInt(accessFlags);
out.writeInt(superType==null?-1:superType.getIndex());
out.writeInt(implementedInterfaces==null?0:implementedInterfaces.getOffset());
out.writeInt(sourceFile==null?-1:sourceFile.getIndex());
out.writeInt(annotations==null?0:annotations.getOffset());
out.writeInt(classData==null?0:classData.getOffset());
out.writeInt(staticFieldInitializers==null?0:staticFieldInitializers.getOffset());
}
/** {@inheritDoc} */
public ItemType getItemType() {
return ItemType.TYPE_CLASS_DEF_ITEM;
}
/** {@inheritDoc} */
public String getConciseIdentity() {
return "class_def_item: " + classType.getTypeDescriptor();
}
/** {@inheritDoc} */
public int compareTo(ClassDefItem o) {
//The actual sorting for this class is done during the placement phase, in ClassDefPlacer.
//This method is just used for sorting the associated ClassDataItem items after the ClassDefItems have been
//placed, so we can just do the comparison based on the offsets
return this.getOffset() - o.getOffset();
}
public TypeIdItem getClassType() {
return classType;
}
public int getAccessFlags() {
return accessFlags;
}
@Nullable
public TypeIdItem getSuperclass() {
return superType;
}
@Nullable
public TypeListItem getInterfaces() {
return implementedInterfaces;
}
@Nullable
public StringIdItem getSourceFile() {
return sourceFile;
}
@Nullable
public AnnotationDirectoryItem getAnnotations() {
return annotations;
}
@Nullable
public ClassDataItem getClassData() {
return classData;
}
@Nullable
public EncodedArrayItem getStaticFieldInitializers() {
return staticFieldInitializers;
}
public static int placeClassDefItems(IndexedSection<ClassDefItem> section, int offset) {
ClassDefPlacer cdp = new ClassDefPlacer(section);
return cdp.placeSection(offset);
}
/**
* This class places the items within a ClassDefItem section, such that superclasses and interfaces are
* placed before sub/implementing classes
*/
private static class ClassDefPlacer {
private final IndexedSection<ClassDefItem> section;
private final HashMap<TypeIdItem, ClassDefItem> unplacedClassDefsByType =
new HashMap<TypeIdItem, ClassDefItem>();
private int currentIndex = 0;
private int currentOffset;
public ClassDefPlacer(IndexedSection<ClassDefItem> section) {
this.section = section;
for (ClassDefItem classDefItem: section.items) {
TypeIdItem typeIdItem = classDefItem.classType;
unplacedClassDefsByType.put(typeIdItem, classDefItem);
}
}
public int placeSection(int offset) {
currentOffset = offset;
if (section.DexFile.getSortAllItems()) {
//presort the list, to guarantee a unique ordering
Collections.sort(section.items, new Comparator<ClassDefItem>() {
public int compare(ClassDefItem a, ClassDefItem b) {
return a.getClassType().compareTo(b.getClassType());
}
});
}
//we need to initialize the offset for all the classes to -1, so we can tell which ones
//have been placed
for (ClassDefItem classDefItem: section.items) {
classDefItem.offset = -1;
}
for (ClassDefItem classDefItem: section.items) {
placeClass(classDefItem);
}
for (ClassDefItem classDefItem: unplacedClassDefsByType.values()) {
section.items.set(classDefItem.getIndex(), classDefItem);
}
return currentOffset;
}
private void placeClass(ClassDefItem classDefItem) {
if (!classDefItem.isPlaced()) {
TypeIdItem superType = classDefItem.superType;
ClassDefItem superClassDefItem = unplacedClassDefsByType.get(superType);
if (superClassDefItem != null) {
placeClass(superClassDefItem);
}
TypeListItem interfaces = classDefItem.implementedInterfaces;
if (interfaces != null) {
for (TypeIdItem interfaceType: interfaces.getTypes()) {
ClassDefItem interfaceClass = unplacedClassDefsByType.get(interfaceType);
if (interfaceClass != null) {
placeClass(interfaceClass);
}
}
}
currentOffset = classDefItem.placeAt(currentOffset, currentIndex++);
unplacedClassDefsByType.remove(classDefItem.classType);
}
}
}
public static class StaticFieldInitializer implements Comparable<StaticFieldInitializer> {
public final EncodedValue value;
public final ClassDataItem.EncodedField field;
public StaticFieldInitializer(EncodedValue value, ClassDataItem.EncodedField field) {
this.value = value;
this.field = field;
}
public int compareTo(StaticFieldInitializer other) {
return field.compareTo(other.field);
}
}
/**
* A helper method to sort the static field initializers and populate the default values as needed
* @param dexFile the <code>DexFile</code>
* @param staticFieldInitializers the initial values
* @return an interned EncodedArrayItem containing the static field initializers
*/
private static EncodedArrayItem makeStaticFieldInitializersItem(DexFile dexFile,
@Nonnull List<StaticFieldInitializer> staticFieldInitializers) {
if (staticFieldInitializers.size() == 0) {
return null;
}
int len = staticFieldInitializers.size();
// make a copy before sorting. we don't want to modify the list passed to us
staticFieldInitializers = new ArrayList<StaticFieldInitializer>(staticFieldInitializers);
Collections.sort(staticFieldInitializers);
int lastIndex = -1;
for (int i=len-1; i>=0; i--) {
StaticFieldInitializer staticFieldInitializer = staticFieldInitializers.get(i);
if (staticFieldInitializer.value != null &&
(staticFieldInitializer.value.compareTo(TypeUtils.makeDefaultValueForType(
staticFieldInitializer.field.field.getFieldType())) != 0)) {
lastIndex = i;
break;
}
}
//we don't have any non-null/non-default values, so we don't need to create an EncodedArrayItem
if (lastIndex == -1) {
return null;
}
EncodedValue[] values = new EncodedValue[lastIndex+1];
for (int i=0; i<=lastIndex; i++) {
StaticFieldInitializer staticFieldInitializer = staticFieldInitializers.get(i);
EncodedValue encodedValue = staticFieldInitializer.value;
if (encodedValue == null) {
encodedValue = TypeUtils.makeDefaultValueForType(staticFieldInitializer.field.field.getFieldType());
}
values[i] = encodedValue;
}
ArrayEncodedSubValue encodedArrayValue = new ArrayEncodedSubValue(values);
return EncodedArrayItem.internEncodedArrayItem(dexFile, encodedArrayValue);
}
}

View File

@ -1,317 +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.dexlib.Code.Analysis;
import org.jf.dexlib.*;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DeodexUtil {
public static final int Virtual = 0;
public static final int Direct = 1;
public static final int Static = 2;
private final InlineMethodResolver inlineMethodResolver;
public final DexFile dexFile;
public DeodexUtil(DexFile dexFile) {
this.dexFile = dexFile;
OdexHeader odexHeader = dexFile.getOdexHeader();
if (odexHeader == null) {
//if there isn't an odex header, why are we creating an DeodexUtil object?
assert false;
throw new RuntimeException("Cannot create a DeodexUtil object for a dex file without an odex header");
}
inlineMethodResolver = InlineMethodResolver.createInlineMethodResolver(this, odexHeader.version);
}
public DeodexUtil(DexFile dexFile, InlineMethodResolver inlineMethodResolver) {
this.dexFile = dexFile;
this.inlineMethodResolver = inlineMethodResolver;
}
public InlineMethod lookupInlineMethod(AnalyzedInstruction instruction) {
return inlineMethodResolver.resolveExecuteInline(instruction);
}
public FieldIdItem lookupField(ClassPath.ClassDef accessingClass, ClassPath.ClassDef instanceClass,
int fieldOffset) {
ClassPath.FieldDef field = instanceClass.getInstanceField(fieldOffset);
if (field == null) {
return null;
}
return parseAndResolveField(accessingClass, instanceClass, field);
}
private static final Pattern shortMethodPattern = Pattern.compile("([^(]+)\\(([^)]*)\\)(.+)");
public MethodIdItem lookupVirtualMethod(ClassPath.ClassDef accessingClass, ClassPath.ClassDef instanceClass,
int methodIndex) {
String method = instanceClass.getVirtualMethod(methodIndex);
if (method == null) {
return null;
}
Matcher m = shortMethodPattern.matcher(method);
if (!m.matches()) {
assert false;
throw new RuntimeException("Invalid method descriptor: " + method);
}
String methodName = m.group(1);
String methodParams = m.group(2);
String methodRet = m.group(3);
if (instanceClass 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;
}
return parseAndResolveMethod(accessingClass, instanceClass, methodName, methodParams, methodRet);
}
private MethodIdItem parseAndResolveMethod(ClassPath.ClassDef accessingClass, ClassPath.ClassDef definingClass,
String methodName, String methodParams, String methodRet) {
StringIdItem methodNameItem = StringIdItem.lookupStringIdItem(dexFile, methodName);
if (methodNameItem == null) {
return null;
}
LinkedList<TypeIdItem> paramList = new LinkedList<TypeIdItem>();
for (int i=0; i<methodParams.length(); i++) {
TypeIdItem typeIdItem;
switch (methodParams.charAt(i)) {
case 'Z':
case 'B':
case 'S':
case 'C':
case 'I':
case 'J':
case 'F':
case 'D':
typeIdItem = TypeIdItem.lookupTypeIdItem(dexFile, methodParams.substring(i,i+1));
break;
case 'L':
{
int end = methodParams.indexOf(';', i);
if (end == -1) {
throw new RuntimeException("invalid parameter in the method");
}
typeIdItem = TypeIdItem.lookupTypeIdItem(dexFile, methodParams.substring(i, end+1));
i = end;
break;
}
case '[':
{
int end;
int typeStart = i+1;
while (typeStart < methodParams.length() && methodParams.charAt(typeStart) == '[') {
typeStart++;
}
switch (methodParams.charAt(typeStart)) {
case 'Z':
case 'B':
case 'S':
case 'C':
case 'I':
case 'J':
case 'F':
case 'D':
end = typeStart;
break;
case 'L':
end = methodParams.indexOf(';', typeStart);
if (end == -1) {
throw new RuntimeException("invalid parameter in the method");
}
break;
default:
throw new RuntimeException("invalid parameter in the method");
}
typeIdItem = TypeIdItem.lookupTypeIdItem(dexFile, methodParams.substring(i, end+1));
i = end;
break;
}
default:
throw new RuntimeException("invalid parameter in the method");
}
if (typeIdItem == null) {
return null;
}
paramList.add(typeIdItem);
}
TypeListItem paramListItem = null;
if (paramList.size() > 0) {
paramListItem = TypeListItem.lookupTypeListItem(dexFile, paramList);
if (paramListItem == null) {
return null;
}
}
TypeIdItem retType = TypeIdItem.lookupTypeIdItem(dexFile, methodRet);
if (retType == null) {
return null;
}
ProtoIdItem protoItem = ProtoIdItem.lookupProtoIdItem(dexFile, retType, paramListItem);
if (protoItem == null) {
return null;
}
ClassPath.ClassDef methodClassDef = definingClass;
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)) {
return methodIdItem;
}
}
methodClassDef = methodClassDef.getSuperclass();
} while (methodClassDef != null);
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) {
String definingClass = field.definingClass;
String fieldName = field.name;
String fieldType = field.type;
StringIdItem fieldNameItem = StringIdItem.lookupStringIdItem(dexFile, fieldName);
if (fieldNameItem == null) {
return null;
}
TypeIdItem fieldTypeItem = TypeIdItem.lookupTypeIdItem(dexFile, fieldType);
if (fieldTypeItem == null) {
return null;
}
ClassPath.ClassDef fieldClass = instanceClass;
ArrayList<ClassPath.ClassDef> parents = new ArrayList<ClassPath.ClassDef>();
parents.add(fieldClass);
while (fieldClass != null && !fieldClass.getClassType().equals(definingClass)) {
fieldClass = fieldClass.getSuperclass();
parents.add(fieldClass);
}
for (int i=parents.size()-1; i>=0; i--) {
fieldClass = parents.get(i);
TypeIdItem classTypeItem = TypeIdItem.lookupTypeIdItem(dexFile, fieldClass.getClassType());
if (classTypeItem == null) {
continue;
}
FieldIdItem fieldIdItem = FieldIdItem.lookupFieldIdItem(dexFile, classTypeItem, fieldTypeItem, fieldNameItem);
if (fieldIdItem != null && checkClassAccess(accessingClass, fieldClass)) {
return fieldIdItem;
}
}
return null;
}
public static class InlineMethod {
public final int methodType;
public final String classType;
public final String methodName;
public final String parameters;
public final String returnType;
private MethodIdItem methodIdItem = null;
InlineMethod(int methodType, String classType, String methodName, String parameters,
String returnType) {
this.methodType = methodType;
this.classType = classType;
this.methodName = methodName;
this.parameters = parameters;
this.returnType = returnType;
}
public MethodIdItem getMethodIdItem(DeodexUtil deodexUtil) {
if (methodIdItem == null) {
loadMethod(deodexUtil);
}
return methodIdItem;
}
private void loadMethod(DeodexUtil deodexUtil) {
ClassPath.ClassDef classDef = ClassPath.getClassDef(classType);
this.methodIdItem = deodexUtil.parseAndResolveMethod(classDef, classDef, methodName, parameters,
returnType);
}
public String getMethodString() {
return String.format("%s->%s(%s)%s", classType, methodName, parameters, returnType);
}
}
}

View File

@ -1,56 +0,0 @@
/*
* [The "BSD licence"]
* Copyright (c) 2011 Ben Gruver
* 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.dexlib.Code.Analysis;
import org.jf.dexlib.ClassDefItem;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.TypeIdItem;
import java.util.HashMap;
/**
* Keeps a simple map of classes defined in a dex file, allowing you to look them up by TypeIdItem or name
*/
public class DexFileClassMap {
private final HashMap<String, ClassDefItem> definedClasses = new HashMap<String, ClassDefItem>();
public DexFileClassMap(DexFile dexFile) {
for (ClassDefItem classDefItem: dexFile.ClassDefsSection.getItems()) {
definedClasses.put(classDefItem.getClassType().getTypeDescriptor(), classDefItem);
}
}
public ClassDefItem getClassDefByName(String typeName) {
return definedClasses.get(typeName);
}
public ClassDefItem getClassDefByType(TypeIdItem typeIdItem) {
return definedClasses.get(typeIdItem.getTypeDescriptor());
}
}

View File

@ -1,187 +0,0 @@
/*
* [The "BSD licence"]
* Copyright (c) 2010 Ben Gruver
* 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.dexlib.Code.Analysis;
import org.jf.dexlib.Code.OdexedInvokeInline;
import org.jf.dexlib.Code.OdexedInvokeVirtual;
import static org.jf.dexlib.Code.Analysis.DeodexUtil.Static;
import static org.jf.dexlib.Code.Analysis.DeodexUtil.Virtual;
import static org.jf.dexlib.Code.Analysis.DeodexUtil.Direct;
public abstract class InlineMethodResolver {
public static InlineMethodResolver createInlineMethodResolver(DeodexUtil deodexUtil, int odexVersion) {
if (odexVersion == 35) {
return new InlineMethodResolver_version35(deodexUtil);
} else if (odexVersion == 36) {
return new InlineMethodResolver_version36(deodexUtil);
} else {
throw new RuntimeException(String.format("odex version %d is not supported yet", odexVersion));
}
}
protected InlineMethodResolver() {
}
public abstract DeodexUtil.InlineMethod resolveExecuteInline(AnalyzedInstruction instruction);
private static class InlineMethodResolver_version35 extends InlineMethodResolver
{
private final DeodexUtil.InlineMethod[] inlineMethods;
public InlineMethodResolver_version35(DeodexUtil deodexUtil) {
inlineMethods = new DeodexUtil.InlineMethod[] {
new DeodexUtil.InlineMethod(Static, "Lorg/apache/harmony/dalvik/NativeTestTarget;", "emptyInlineMethod", "", "V"),
new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "charAt", "I", "C"),
new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "compareTo", "Ljava/lang/String;", "I"),
new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "equals", "Ljava/lang/Object;", "Z"),
new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "length", "", "I"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "abs", "I", "I"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "abs", "J", "J"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "abs", "F", "F"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "abs", "D", "D"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "min", "II", "I"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "max", "II", "I"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "sqrt", "D", "D"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "cos", "D", "D"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "sin", "D", "D")
};
}
@Override
public DeodexUtil.InlineMethod resolveExecuteInline(AnalyzedInstruction analyzedInstruction) {
assert analyzedInstruction.instruction instanceof OdexedInvokeInline;
OdexedInvokeInline instruction = (OdexedInvokeInline)analyzedInstruction.instruction;
int inlineIndex = instruction.getInlineIndex();
if (inlineIndex < 0 || inlineIndex >= inlineMethods.length) {
throw new RuntimeException("Invalid inline index: " + inlineIndex);
}
return inlineMethods[inlineIndex];
}
}
private static class InlineMethodResolver_version36 extends InlineMethodResolver
{
private final DeodexUtil.InlineMethod[] inlineMethods;
private final DeodexUtil.InlineMethod indexOfIMethod;
private final DeodexUtil.InlineMethod indexOfIIMethod;
private final DeodexUtil.InlineMethod fastIndexOfMethod;
private final DeodexUtil.InlineMethod isEmptyMethod;
public InlineMethodResolver_version36(DeodexUtil deodexUtil) {
//The 5th and 6th entries differ between froyo and gingerbread. We have to look at the parameters being
//passed to distinguish between them.
//froyo
indexOfIMethod = new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "indexOf", "I", "I");
indexOfIIMethod = new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "indexOf", "II", "I");
//gingerbread
fastIndexOfMethod = new DeodexUtil.InlineMethod(Direct, "Ljava/lang/String;", "fastIndexOf", "II", "I");
isEmptyMethod = new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "isEmpty", "", "Z");
inlineMethods = new DeodexUtil.InlineMethod[] {
new DeodexUtil.InlineMethod(Static, "Lorg/apache/harmony/dalvik/NativeTestTarget;", "emptyInlineMethod", "", "V"),
new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "charAt", "I", "C"),
new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "compareTo", "Ljava/lang/String;", "I"),
new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "equals", "Ljava/lang/Object;", "Z"),
//froyo: deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "indexOf", "I", "I"),
//gingerbread: deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "fastIndexOf", "II", "I"),
null,
//froyo: deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "indexOf", "II", "I"),
//gingerbread: deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "isEmpty", "", "Z"),
null,
new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "length", "", "I"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "abs", "I", "I"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "abs", "J", "J"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "abs", "F", "F"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "abs", "D", "D"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "min", "II", "I"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "max", "II", "I"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "sqrt", "D", "D"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "cos", "D", "D"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "sin", "D", "D"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/Float;", "floatToIntBits", "F", "I"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/Float;", "floatToRawIntBits", "F", "I"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/Float;", "intBitsToFloat", "I", "F"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/Double;", "doubleToLongBits", "D", "J"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/Double;", "doubleToRawLongBits", "D", "J"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/Double;", "longBitsToDouble", "J", "D"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/StrictMath;", "abs", "I", "I"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/StrictMath;", "abs", "J", "J"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/StrictMath;", "abs", "F", "F"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/StrictMath;", "abs", "D", "D"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/StrictMath;", "min", "II", "I"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/StrictMath;", "max", "II", "I"),
new DeodexUtil.InlineMethod(Static, "Ljava/lang/StrictMath;", "sqrt", "D", "D"),
};
}
@Override
public DeodexUtil.InlineMethod resolveExecuteInline(AnalyzedInstruction analyzedInstruction) {
assert analyzedInstruction.instruction instanceof OdexedInvokeInline;
OdexedInvokeInline instruction = (OdexedInvokeInline)analyzedInstruction.instruction;
int inlineIndex = instruction.getInlineIndex();
if (inlineIndex < 0 || inlineIndex >= inlineMethods.length) {
throw new RuntimeException("Invalid method index: " + inlineIndex);
}
if (inlineIndex == 4) {
int parameterCount = getParameterCount(instruction);
if (parameterCount == 2) {
return indexOfIMethod;
} else if (parameterCount == 3) {
return fastIndexOfMethod;
} else {
throw new RuntimeException("Could not determine the correct inline method to use");
}
} else if (inlineIndex == 5) {
int parameterCount = getParameterCount(instruction);
if (parameterCount == 3) {
return indexOfIIMethod;
} else if (parameterCount == 1) {
return isEmptyMethod;
} else {
throw new RuntimeException("Could not determine the correct inline method to use");
}
}
return inlineMethods[inlineIndex];
}
private int getParameterCount(OdexedInvokeInline instruction) {
return instruction.getRegCount();
}
}
}

View File

@ -1,322 +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.dexlib.Code.Analysis;
import org.jf.dexlib.TypeIdItem;
import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import static org.jf.dexlib.Code.Analysis.ClassPath.ClassDef;
public class RegisterType {
private final static HashMap<RegisterType, RegisterType> internedRegisterTypes =
new HashMap<RegisterType, RegisterType>();
public final Category category;
public final ClassDef type;
private RegisterType(Category category, ClassDef type) {
assert ((category == Category.Reference || category == Category.UninitRef || category == Category.UninitThis) &&
type != null) ||
((category != Category.Reference && category != Category.UninitRef && category != Category.UninitThis) &&
type == null);
this.category = category;
this.type = type;
}
@Override
public String toString() {
return "(" + category.name() + (type==null?"":("," + type.getClassType())) + ")";
}
public void writeTo(Writer writer) throws IOException {
writer.write('(');
writer.write(category.name());
if (type != null) {
writer.write(',');
writer.write(type.getClassType());
}
writer.write(')');
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RegisterType that = (RegisterType) o;
if (category != that.category) return false;
if (type != null ? !type.equals(that.type) : that.type != null) return false;
return true;
}
@Override
public int hashCode() {
int result = category.hashCode();
result = 31 * result + (type != null ? type.hashCode() : 0);
return result;
}
public static enum Category {
//the Unknown category denotes a register type that hasn't been determined yet
Unknown,
Uninit,
Null,
One,
Boolean,
Byte,
PosByte,
Short,
PosShort,
Char,
Integer,
Float,
LongLo,
LongHi,
DoubleLo,
DoubleHi,
//the UninitRef category is used after a new-instance operation, and before the corresponding <init> is called
UninitRef,
//the UninitThis category is used the "this" register inside an <init> method, before the superclass' <init>
//method is called
UninitThis,
Reference,
//This is used when there are multiple incoming execution paths that have incompatible register types. For
//example if the register's type is an Integer on one incomming code path, but is a Reference type on another
//incomming code path. There is no register type that can hold either an Integer or a Reference.
Conflicted;
//this table is used when merging register types. For example, if a particular register can be either a Byte
//or a Char, then the "merged" type of that register would be Integer, because it is the "smallest" type can
//could hold either type of value.
protected static Category[][] mergeTable =
{
/* Unknown Uninit Null One, Boolean Byte PosByte Short PosShort Char Integer, Float, LongLo LongHi DoubleLo DoubleHi UninitRef UninitThis Reference Conflicted*/
/*Unknown*/ {Unknown, Uninit, Null, One, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, LongLo, LongHi, DoubleLo, DoubleHi, UninitRef, UninitThis, Reference, Conflicted},
/*Uninit*/ {Uninit, Uninit, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*Null*/ {Null, Conflicted, Null, Boolean, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Reference, Conflicted},
/*One*/ {One, Conflicted, Boolean, One, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*Boolean*/ {Boolean, Conflicted, Boolean, Boolean, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*Byte*/ {Byte, Conflicted, Byte, Byte, Byte, Byte, Byte, Short, Short, Integer, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*PosByte*/ {PosByte, Conflicted, PosByte, PosByte, PosByte, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*Short*/ {Short, Conflicted, Short, Short, Short, Short, Short, Short, Short, Integer, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*PosShort*/ {PosShort, Conflicted, PosShort, PosShort, PosShort, Short, PosShort, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*Char*/ {Char, Conflicted, Char, Char, Char, Integer, Char, Integer, Char, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*Integer*/ {Integer, Conflicted, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*Float*/ {Float, Conflicted, Float, Float, Float, Float, Float, Float, Float, Float, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*LongLo*/ {LongLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongLo, Conflicted, LongLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*LongHi*/ {LongHi, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongHi, Conflicted, LongHi, Conflicted, Conflicted, Conflicted, Conflicted},
/*DoubleLo*/ {DoubleLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongLo, Conflicted, DoubleLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*DoubleHi*/ {DoubleHi, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongHi, Conflicted, DoubleHi, Conflicted, Conflicted, Conflicted, Conflicted},
/*UninitRef*/ {UninitRef, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
/*UninitThis*/ {UninitThis, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, UninitThis, Conflicted, Conflicted},
/*Reference*/ {Reference, Conflicted, Reference, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Reference, Conflicted},
/*Conflicted*/ {Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}
};
//this table is used to denote whether a given value type can be assigned to a "slot" of a certain type. For
//example, to determine if you can assign a Boolean value to a particular array "slot", where the array is an
//array of Integers, you would look up assignmentTable[Boolean.ordinal()][Integer.ordinal()]
//Note that not all slot types in the table are expected to be used. For example, it doesn't make sense to
//check if a value can be assigned to an uninitialized reference slot - because there is no such thing.
protected static boolean[][] assigmentTable =
{
/* Unknown Uninit Null One, Boolean Byte PosByte Short PosShort Char Integer, Float, LongLo LongHi DoubleLo DoubleHi UninitRef UninitThis Reference Conflicted |slot type*/
/*Unknown*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
/*Uninit*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
/*Null*/ {false, false, true, false, true, true, true, true, true, true, true, true, false, false, false, false, false, false, true, false},
/*One*/ {false, false, false, true, true, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false},
/*Boolean*/ {false, false, false, false, true, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false},
/*Byte*/ {false, false, false, false, false, true, false, true, true, false, true, true, false, false, false, false, false, false, false, false},
/*PosByte*/ {false, false, false, false, false, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false},
/*Short*/ {false, false, false, false, false, false, false, true, false, false, true, true, false, false, false, false, false, false, false, false},
/*PosShort*/ {false, false, false, false, false, false, false, true, true, true, true, true, false, false, false, false, false, false, false, false},
/*Char*/ {false, false, false, false, false, false, false, false, false, true, true, true, false, false, false, false, false, false, false, false},
/*Integer*/ {false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false},
/*Float*/ {false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false},
/*LongLo*/ {false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false, false},
/*LongHi*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false},
/*DoubleLo*/ {false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false, false},
/*DoubleHi*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false},
/*UninitRef*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
/*UninitThis*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
/*Reference*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false},
/*Conflicted*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}
/*----------*/
/*value type*/
};
}
public static RegisterType getRegisterTypeForType(String type) {
switch (type.charAt(0)) {
case 'V':
throw new ValidationException("The V type can only be used as a method return type");
case 'Z':
return getRegisterType(Category.Boolean, null);
case 'B':
return getRegisterType(Category.Byte, null);
case 'S':
return getRegisterType(Category.Short, null);
case 'C':
return getRegisterType(Category.Char, null);
case 'I':
return getRegisterType(Category.Integer, null);
case 'F':
return getRegisterType(Category.Float, null);
case 'J':
return getRegisterType(Category.LongLo, null);
case 'D':
return getRegisterType(Category.DoubleLo, null);
case 'L':
case '[':
return getRegisterType(Category.Reference, ClassPath.getClassDef(type));
default:
throw new RuntimeException("Invalid type: " + type);
}
}
public static RegisterType getRegisterTypeForTypeIdItem(TypeIdItem typeIdItem) {
return getRegisterTypeForType(typeIdItem.getTypeDescriptor());
}
public static RegisterType getWideRegisterTypeForTypeIdItem(TypeIdItem typeIdItem, boolean firstRegister) {
if (typeIdItem.getRegisterCount() == 1) {
throw new RuntimeException("Cannot use this method for non-wide register type: " +
typeIdItem.getTypeDescriptor());
}
switch (typeIdItem.getTypeDescriptor().charAt(0)) {
case 'J':
if (firstRegister) {
return getRegisterType(Category.LongLo, null);
} else {
return getRegisterType(Category.LongHi, null);
}
case 'D':
if (firstRegister) {
return getRegisterType(Category.DoubleLo, null);
} else {
return getRegisterType(Category.DoubleHi, null);
}
default:
throw new RuntimeException("Invalid type: " + typeIdItem.getTypeDescriptor());
}
}
public static RegisterType getRegisterTypeForLiteral(long literalValue) {
if (literalValue < -32768) {
return getRegisterType(Category.Integer, null);
}
if (literalValue < -128) {
return getRegisterType(Category.Short, null);
}
if (literalValue < 0) {
return getRegisterType(Category.Byte, null);
}
if (literalValue == 0) {
return getRegisterType(Category.Null, null);
}
if (literalValue == 1) {
return getRegisterType(Category.One, null);
}
if (literalValue < 128) {
return getRegisterType(Category.PosByte, null);
}
if (literalValue < 32768) {
return getRegisterType(Category.PosShort, null);
}
if (literalValue < 65536) {
return getRegisterType(Category.Char, null);
}
return getRegisterType(Category.Integer, null);
}
public RegisterType merge(RegisterType type) {
if (type == null || type == this) {
return this;
}
Category mergedCategory = Category.mergeTable[this.category.ordinal()][type.category.ordinal()];
ClassDef mergedType = null;
if (mergedCategory == Category.Reference) {
if (this.type instanceof ClassPath.UnresolvedClassDef ||
type.type instanceof ClassPath.UnresolvedClassDef) {
mergedType = ClassPath.getUnresolvedObjectClassDef();
} else {
mergedType = ClassPath.getCommonSuperclass(this.type, type.type);
}
} else if (mergedCategory == Category.UninitRef || mergedCategory == Category.UninitThis) {
if (this.category == Category.Unknown) {
return type;
}
assert type.category == Category.Unknown;
return this;
}
return RegisterType.getRegisterType(mergedCategory, mergedType);
}
public boolean canBeAssignedTo(RegisterType slotType) {
if (Category.assigmentTable[this.category.ordinal()][slotType.category.ordinal()]) {
if (this.category == Category.Reference && slotType.category == Category.Reference) {
if (!slotType.type.isInterface()) {
return this.type.extendsClass(slotType.type);
}
//for verification, we assume all objects implement all interfaces, so we don't verify the type if
//slotType is an interface
}
return true;
}
return false;
}
public static RegisterType getUnitializedReference(ClassDef classType) {
//We always create a new RegisterType instance for an uninit ref. Each unique uninit RegisterType instance
//is used to track a specific uninitialized reference, so that if multiple registers contain the same
//uninitialized reference, then they can all be upgraded to an initialized reference when the appropriate
//<init> is invoked
return new RegisterType(Category.UninitRef, classType);
}
public static RegisterType getRegisterType(Category category, ClassDef classType) {
RegisterType newRegisterType = new RegisterType(category, classType);
RegisterType internedRegisterType = internedRegisterTypes.get(newRegisterType);
if (internedRegisterType == null) {
internedRegisterTypes.put(newRegisterType, newRegisterType);
return newRegisterType;
}
return internedRegisterType;
}
}

View File

@ -1,140 +0,0 @@
/*
* [The "BSD licence"]
* Copyright (c) 2011 Ben Gruver
* 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.dexlib.Code.Analysis;
import org.jf.dexlib.*;
import org.jf.dexlib.Code.Format.Instruction22c;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.InstructionWithReference;
import org.jf.dexlib.Util.AccessFlags;
import java.util.HashMap;
public class SyntheticAccessorResolver {
public static final int METHOD = 0;
public static final int GETTER = 1;
public static final int SETTER = 2;
private final DexFileClassMap classMap;
private final HashMap<MethodIdItem, AccessedMember> resolvedAccessors = new HashMap<MethodIdItem, AccessedMember>();
public SyntheticAccessorResolver(DexFile dexFile) {
classMap = new DexFileClassMap(dexFile);
}
public static boolean looksLikeSyntheticAccessor(MethodIdItem methodIdItem) {
return methodIdItem.getMethodName().getStringValue().startsWith("access$");
}
public AccessedMember getAccessedMember(MethodIdItem methodIdItem) {
AccessedMember accessedMember = resolvedAccessors.get(methodIdItem);
if (accessedMember != null) {
return accessedMember;
}
ClassDefItem classDefItem = classMap.getClassDefByType(methodIdItem.getContainingClass());
if (classDefItem == null) {
return null;
}
ClassDataItem classDataItem = classDefItem.getClassData();
if (classDataItem == null) {
return null;
}
ClassDataItem.EncodedMethod encodedMethod = classDataItem.findDirectMethodByMethodId(methodIdItem);
if (encodedMethod == null) {
return null;
}
//A synthetic accessor will be marked synthetic
if ((encodedMethod.accessFlags & AccessFlags.SYNTHETIC.getValue()) == 0) {
return null;
}
Instruction[] instructions = encodedMethod.codeItem.getInstructions();
//TODO: add support for odexed formats
switch (instructions[0].opcode.format) {
case Format35c:
case Format3rc: {
//a synthetic method access should be either 2 or 3 instructions, depending on if the method returns
//anything or not
if (instructions.length < 2 || instructions.length > 3) {
return null;
}
InstructionWithReference instruction = (InstructionWithReference)instructions[0];
Item referencedItem = instruction.getReferencedItem();
if (!(referencedItem instanceof MethodIdItem)) {
return null;
}
MethodIdItem referencedMethodIdItem = (MethodIdItem)referencedItem;
accessedMember = new AccessedMember(METHOD, referencedMethodIdItem);
resolvedAccessors.put(methodIdItem, accessedMember);
return accessedMember;
}
case Format22c: {
//a synthetic field access should be exactly 2 instructions. The set/put, and then the return
if (instructions.length != 2) {
return null;
}
Instruction22c instruction = (Instruction22c)instructions[0];
Item referencedItem = instruction.getReferencedItem();
if (!(referencedItem instanceof FieldIdItem)) {
return null;
}
FieldIdItem referencedFieldIdItem = (FieldIdItem)referencedItem;
if (instruction.opcode.setsRegister() || instruction.opcode.setsWideRegister()) {
//If the instruction sets a register, that means it is a getter - it gets the field value and
//stores it in the register
accessedMember = new AccessedMember(GETTER, referencedFieldIdItem);
} else {
accessedMember = new AccessedMember(SETTER, referencedFieldIdItem);
}
resolvedAccessors.put(methodIdItem, accessedMember);
return accessedMember;
}
default:
return null;
}
}
public static class AccessedMember {
public final int accessedMemberType;
public final Item accessedMember;
public AccessedMember(int accessedMemberType, Item accessedMember) {
this.accessedMemberType = accessedMemberType;
this.accessedMember = accessedMember;
}
}
}

View File

@ -1,52 +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.dexlib.Code.Analysis;
import org.jf.dexlib.Util.ExceptionWithContext;
public class ValidationException extends ExceptionWithContext {
private int codeAddress;
public ValidationException(int codeAddress, String errorMessage) {
super(errorMessage);
this.codeAddress = codeAddress;
}
public ValidationException(String errorMessage) {
super(errorMessage);
}
public void setCodeAddress(int codeAddress) {
this.codeAddress = codeAddress;
}
public int getCodeAddress() {
return codeAddress;
}
}

View File

@ -1,37 +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.dexlib.Code;
public interface FiveRegisterInstruction extends InvokeInstruction {
byte getRegisterA();
byte getRegisterD();
byte getRegisterE();
byte getRegisterF();
byte getRegisterG();
}

View File

@ -1,152 +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.dexlib.Code.Format;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.Util.NumberUtils;
import java.util.Iterator;
public class ArrayDataPseudoInstruction extends Instruction {
public static final Instruction.InstructionFactory Factory = new Factory();
private int elementWidth;
private byte[] encodedValues;
@Override
public int getSize(int codeAddress) {
return ((encodedValues.length + 1)/2) + 4 + (codeAddress % 2);
}
public ArrayDataPseudoInstruction(int elementWidth, byte[] encodedValues) {
super(Opcode.NOP);
if (encodedValues.length % elementWidth != 0) {
throw new RuntimeException("There are not a whole number of " + elementWidth + " byte elements");
}
this.elementWidth = elementWidth;
this.encodedValues = encodedValues;
}
public ArrayDataPseudoInstruction(byte[] buffer, int bufferIndex) {
super(Opcode.NOP);
byte opcodeByte = buffer[bufferIndex];
if (opcodeByte != 0x00) {
throw new RuntimeException("Invalid opcode byte for an ArrayData pseudo-instruction");
}
byte subopcodeByte = buffer[bufferIndex+1];
if (subopcodeByte != 0x03) {
throw new RuntimeException("Invalid sub-opcode byte for an ArrayData pseudo-instruction");
}
this.elementWidth = NumberUtils.decodeUnsignedShort(buffer, bufferIndex+2);
int elementCount = NumberUtils.decodeInt(buffer, bufferIndex+4);
this.encodedValues = new byte[elementCount * elementWidth];
System.arraycopy(buffer, bufferIndex+8, encodedValues, 0, elementCount * elementWidth);
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
out.alignTo(4);
int elementCount = encodedValues.length / elementWidth;
out.writeByte(0x00);
out.writeByte(0x03);
out.writeShort(elementWidth);
out.writeInt(elementCount);
out.write(encodedValues);
//make sure we're written out an even number of bytes
out.alignTo(2);
}
protected void annotateInstruction(AnnotatedOutput out, int currentCodeAddress) {
out.annotate(getSize(currentCodeAddress)*2, "[0x" + Integer.toHexString(currentCodeAddress) + "] " +
"fill-array-data instruction");
}
public Format getFormat() {
return Format.ArrayData;
}
public int getElementWidth() {
return elementWidth;
}
public int getElementCount() {
return encodedValues.length / elementWidth;
}
public static class ArrayElement {
public final byte[] buffer;
public int bufferIndex;
public final int elementWidth;
public ArrayElement(byte[] buffer, int elementWidth) {
this.buffer = buffer;
this.elementWidth = elementWidth;
}
}
public Iterator<ArrayElement> getElements() {
return new Iterator<ArrayElement>() {
final int elementCount = getElementCount();
int i=0;
int position=0;
final ArrayElement arrayElement = new ArrayElement(encodedValues, getElementWidth());
public boolean hasNext() {
return i<elementCount;
}
public ArrayElement next() {
arrayElement.bufferIndex = position;
position += arrayElement.elementWidth;
i++;
return arrayElement;
}
public void remove() {
}
};
}
private static class Factory implements Instruction.InstructionFactory {
public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
if (opcode != Opcode.NOP) {
throw new RuntimeException("The opcode for an ArrayDataPseudoInstruction must be NOP");
}
return new ArrayDataPseudoInstruction(buffer, bufferIndex);
}
}
}

View File

@ -1,86 +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.dexlib.Code.Format;
import org.jf.dexlib.Code.Instruction;
public enum Format {
Format10t(Instruction10t.Factory, 2),
Format10x(Instruction10x.Factory, 2),
Format11n(Instruction11n.Factory, 2),
Format11x(Instruction11x.Factory, 2),
Format12x(Instruction12x.Factory, 2),
Format20bc(Instruction20bc.Factory, 4),
Format20t(Instruction20t.Factory, 4),
Format21c(Instruction21c.Factory, 4),
Format21h(Instruction21h.Factory, 4),
Format21s(Instruction21s.Factory, 4),
Format21t(Instruction21t.Factory, 4),
Format22b(Instruction22b.Factory, 4),
Format22c(Instruction22c.Factory, 4),
Format22cs(Instruction22cs.Factory, 4),
Format22s(Instruction22s.Factory, 4),
Format22t(Instruction22t.Factory, 4),
Format22x(Instruction22x.Factory, 4),
Format23x(Instruction23x.Factory, 4),
Format30t(Instruction30t.Factory, 6),
Format31c(Instruction31c.Factory, 6),
Format31i(Instruction31i.Factory, 6),
Format31t(Instruction31t.Factory, 6),
Format32x(Instruction32x.Factory, 6),
Format35c(Instruction35c.Factory, 6),
Format35mi(Instruction35mi.Factory, 6),
Format35ms(Instruction35ms.Factory, 6),
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),
UnresolvedOdexInstruction(null, -1, false),
;
public final Instruction.InstructionFactory Factory;
public final int size;
public final boolean variableSizeFormat;
private Format(Instruction.InstructionFactory factory, int size) {
this(factory, size, false);
}
private Format(Instruction.InstructionFactory factory, int size, boolean variableSizeFormat) {
this.Factory = factory;
this.size = size;
this.variableSizeFormat = variableSizeFormat;
}
}

View File

@ -1,92 +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.dexlib.Code.Format;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.OffsetInstruction;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction10t extends OffsetInstruction {
public static final InstructionFactory Factory = new Factory();
private int targetAddressOffset;
public Instruction10t(Opcode opcode, int offA) {
super(opcode);
this.targetAddressOffset = offA;
if (targetAddressOffset == 0) {
throw new RuntimeException("The address offset cannot be 0. Use goto/32 instead.");
}
//allow out of range address offsets here, so we have the option of replacing this instruction
//with goto/16 or goto/32 later
}
private Instruction10t(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode);
assert buffer[bufferIndex] == opcode.value;
this.targetAddressOffset = buffer[bufferIndex + 1];
assert targetAddressOffset != 0;
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
if (targetAddressOffset == 0) {
throw new RuntimeException("The address offset cannot be 0. Use goto/32 instead");
}
if (targetAddressOffset < -128 || targetAddressOffset > 127) {
throw new RuntimeException("The address offset is out of range. It must be in [-128,-1] or [1, 127]");
}
out.writeByte(opcode.value);
out.writeByte(targetAddressOffset);
}
public void updateTargetAddressOffset(int targetAddressOffset) {
this.targetAddressOffset = targetAddressOffset;
}
public Format getFormat() {
return Format.Format10t;
}
public int getTargetAddressOffset() {
return targetAddressOffset;
}
private static class Factory implements InstructionFactory {
public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
return new Instruction10t(opcode, buffer, bufferIndex);
}
}
}

View File

@ -1,64 +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.dexlib.Code.Format;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction10x extends Instruction {
public static final InstructionFactory Factory = new Factory();
public Instruction10x(Opcode opcode) {
super(opcode);
}
public Instruction10x(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode);
assert (buffer[bufferIndex] & 0xFF) == opcode.value;
assert buffer[bufferIndex + 1] == 0x00;
}
public void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
out.writeByte(opcode.value);
out.writeByte(0);
}
public Format getFormat() {
return Format.Format10x;
}
private static class Factory implements InstructionFactory {
public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
return new Instruction10x(opcode, buffer, bufferIndex);
}
}
}

View File

@ -1,89 +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.dexlib.Code.Format;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.LiteralInstruction;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.SingleRegisterInstruction;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.Util.NumberUtils;
public class Instruction11n extends Instruction implements SingleRegisterInstruction, LiteralInstruction {
public static final InstructionFactory Factory = new Factory();
private byte regA;
private byte litB;
public Instruction11n(Opcode opcode, byte regA, byte litB) {
super(opcode);
if (regA >= 1 << 4) {
throw new RuntimeException("The register number must be less than v16");
}
if (litB < -(1 << 3) ||
litB >= 1 << 3) {
throw new RuntimeException("The literal value must be between -8 and 7 inclusive");
}
this.regA = regA;
this.litB = litB;
}
private Instruction11n(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode);
this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
this.litB = NumberUtils.decodeHighSignedNibble(buffer[bufferIndex + 1]);
}
public void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
out.writeByte(opcode.value);
out.writeByte((litB << 4) | regA);
}
public Format getFormat() {
return Format.Format11n;
}
public int getRegisterA() {
return regA;
}
public long getLiteral() {
return litB;
}
private static class Factory implements Instruction.InstructionFactory {
public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
return new Instruction11n(opcode, buffer, bufferIndex);
}
}
}

View File

@ -1,76 +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.dexlib.Code.Format;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.SingleRegisterInstruction;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.Util.NumberUtils;
public class Instruction11x extends Instruction implements SingleRegisterInstruction {
public static final Instruction.InstructionFactory Factory = new Factory();
private byte regA;
public Instruction11x(Opcode opcode, short regA) {
super(opcode);
if (regA >= 1 << 8) {
throw new RuntimeException("The register number must be less than v256");
}
this.regA = (byte)regA;
}
private Instruction11x(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode);
this.regA = (byte)NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]);
}
public void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
out.writeByte(opcode.value);
out.writeByte(regA);
}
public Format getFormat() {
return Format.Format11x;
}
public int getRegisterA() {
return regA & 0xFF;
}
private static class Factory implements Instruction.InstructionFactory {
public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
return new Instruction11x(opcode, buffer, bufferIndex);
}
}
}

View File

@ -1,83 +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.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.Util.AnnotatedOutput;
import org.jf.dexlib.Util.NumberUtils;
public class Instruction12x extends Instruction implements TwoRegisterInstruction {
public static final Instruction.InstructionFactory Factory = new Factory();
private int regA;
private int regB;
public Instruction12x(Opcode opcode, byte regA, byte regB) {
super(opcode);
if (regA >= 1 << 4 ||
regB >= 1 << 4) {
throw new RuntimeException("The register number must be less than v16");
}
this.regA = regA;
this.regB = regB;
}
private Instruction12x(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode);
this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
this.regB = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]);
}
public void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
out.writeByte(opcode.value);
out.writeByte((regB << 4) | regA);
}
public Format getFormat() {
return Format.Format12x;
}
public int getRegisterA() {
return regA;
}
public int getRegisterB() {
return regB;
}
private static class Factory implements Instruction.InstructionFactory {
public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
return new Instruction12x(opcode, buffer, bufferIndex);
}
}
}

View File

@ -1,100 +0,0 @@
/*
* [The "BSD licence"]
* Copyright (c) 2011 Ben Gruver
* 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.dexlib.Code.Format;
import org.jf.dexlib.*;
import org.jf.dexlib.Code.*;
import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.Util.NumberUtils;
public class Instruction20bc extends InstructionWithReference {
public static final Instruction.InstructionFactory Factory = new Factory();
private VerificationErrorType validationErrorType;
public Instruction20bc(Opcode opcode, VerificationErrorType validationErrorType, Item referencedItem) {
super(opcode, referencedItem, getReferenceType(referencedItem));
this.validationErrorType = validationErrorType;
}
private static ReferenceType getReferenceType(Item item) {
if (item instanceof TypeIdItem) {
return ReferenceType.type;
}
if (item instanceof FieldIdItem) {
return ReferenceType.field;
}
if (item instanceof MethodIdItem) {
return ReferenceType.method;
}
return null;
}
private Instruction20bc(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
super(dexFile, opcode, buffer, bufferIndex);
short val = NumberUtils.decodeUnsignedByte(buffer[bufferIndex+1]);
validationErrorType = VerificationErrorType.getValidationErrorType(val & 0x3f);
}
protected ReferenceType readReferenceType(Opcode opcode, byte[] buffer, int bufferIndex) {
short val = NumberUtils.decodeUnsignedByte(buffer[bufferIndex+1]);
short referenceType = (short)(val >> 6);
return ReferenceType.fromValidationErrorReferenceType(referenceType);
}
@Override
public Format getFormat() {
return Format.Format20bc;
}
@Override
protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
if(opcode == Opcode.CONST_STRING && getReferencedItem().getIndex() > 0xFFFF) {
throw new RuntimeException("String offset is too large for const-string. Use string-const/jumbo instead.");
}
out.writeByte(opcode.value);
out.writeByte((this.getReferenceType().getValidationErrorReferenceType() << 6) &
validationErrorType.getValue());
out.writeShort(getReferencedItem().getIndex());
}
public VerificationErrorType getValidationErrorType() {
return validationErrorType;
}
private static class Factory implements Instruction.InstructionFactory {
public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
return new Instruction20bc(dexFile, opcode, buffer, bufferIndex);
}
}
}

Some files were not shown because too many files have changed in this diff Show More